You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
5.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import tkinter as tk
from tkinter import filedialog
import cv2
import utils
import numpy as np
import argparse
import imutils
from imutils import contours
# 创建tkinter根窗口并立即隐藏
root = tk.Tk()
root.withdraw()
# 弹出文件选择对话框让用户选择模板文件
template_file_path = filedialog.askopenfilename(title="选择模板文件", filetypes=[("PNG files", "*.png"), ("JPEG files", "*.jpg"), ("All files", "*.*")])
# 弹出文件选择对话框让用户选择信用卡图片
image_file_path = filedialog.askopenfilename(title="选择信用卡图片", filetypes=[("PNG files", "*.png"), ("JPEG files", "*.jpg"), ("All files", "*.*")])
# 使用用户选择的路径读取模板文件和信用卡图片
img = cv2.imread(template_file_path)
image = cv2.imread(image_file_path)
#指定信用卡类型
FIRST_NUMBER={
"3":"American Express",
"4":"Visa",
"5":"MasterCard",
"6":"Discover Card"
}
#绘图展示
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#读取一个模板文件
img=cv2.imread(template_file_path)
cv_show("img",img)
#灰度图
ref=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
#二值图像
ref=cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)
#计算轮廓
#cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图)
#cv2.RETR_EXTERNAL只检测外轮廓cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
refCnts,hierarchy=cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)
cv_show('img',img)
refCnts=utils.sort_contours(refCnts,method="left-to-right")[0]#排序从左到右,从上到下
digits={}
'''
第一个参数img是原图
第二个参数xy是矩阵的左上点坐标
第三个参数x+wy+h是矩阵的右下点坐标
第四个参数0,255,0是画线对应的rgb颜色
'''
#遍历每一个轮廓
for(i,c) in enumerate(refCnts):
#计算外接矩形并且resize成合适大小
(x,y,w,h)=cv2.boundingRect(c)
roi=ref[y:y+h,x:x+w]
roi=cv2.resize(roi,(57,58))
#每一个数字对应一个模板
digits[i]=roi
#初始化卷积核
rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
#读取输入图像,预处理
image=cv2.imread(image_file_path)
cv_show('image',image)
image=utils.resize(image,width=300)
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)
#礼帽操作,突出更明亮的区域
tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)
#计算
gradX=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=1)
gradX=np.absolute(gradX)
(minVal,maxVal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minVal)/(maxVal-minVal)))
gradX=gradX.astype("uint8")
print(np.array(gradX).shape)
cv_show('gradX',gradX)
#通过闭操作,(先膨胀,在腐蚀)将数字连在一起
gradX=cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',gradX)
#THRESH_OTSU会自动寻找合适的阈值适合双峰需把阈值参数设置为0
thresh=cv2.threshold(gradX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
cv_show('thresh',thresh)
#再来一个闭操作
thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show('thresh',thresh)
#计算轮廓
threshCnts,hierarchy=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts=threshCnts
cur_img=image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv_show('img',cur_img)
locs=[]
#遍历轮廓
for (i,c) in enumerate(cnts):
#计算矩形
(x,y,w,h)=cv2.boundingRect(c)
ar=w/float(h)
#适合合适的区域,根据实际任务来,这里的基本是四个数字一组
if ar>2.5 and ar <4.0:
if(w>40 and w<55) and (h>10 and h<20):
#符合的留下来
locs.append((x,y,w,h))
#将符合的轮廓从左到右排序
locs=sorted(locs,key=lambda x:x[0])
output=[]
#遍历每一个轮廓中的数字
for (i,(gX,gY,gW,gH)) in enumerate(locs):
#initialize the list of group digits
groupOutput=[]
#根据坐标提取每一个组
group=gray[gY-5:gY+gH+5,gX-5:gX+gW+5]
cv_show('group',group)
#预处理
group=cv2.threshold(group,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
cv_show('group',group)
#计算每一个轮廓
digitCnts,hierarchy=cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
digitCnts=contours.sort_contours(digitCnts,method="left-to-right")[0]
#计算每一组总的每一个数值
for c in digitCnts:
#找到当前数值的轮廓resize成合适的大小
(x,y,w,h)=cv2.boundingRect(c)
roi=group[y:y+h,x:x+w]
roi=cv2.resize(roi,(57,58))
cv_show('roi',roi)
#计算匹配得分
scores=[]
#在模板章中计算每一个得分
for(digit,digiROI) in digits.items():
#模板匹配
result=cv2.matchTemplate(roi,digiROI,cv2.TM_CCOEFF)
(_,score,_,_)=cv2.minMaxLoc(result)
scores.append(score)
#得到合适的数字
groupOutput.append(str(np.argmax(scores)))
#画出来
cv2.rectangle(image,(gX-5,gY-5),(gX+gW+5,gY+gH+5),(0,0,255),1)
cv2.putText(image,"".join(groupOutput),(gX,gY-15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
#得到结果
output.extend(groupOutput)
# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows() # 确保关闭所有窗口
cv2.imwrite("final_output.jpg", image) # 保存图像到文件