Fu Xianjun. All Rights Reserved
对模板图片进行处理,得到每个数字的模板import cv2 import numpy as np def cv_show(name, img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows()
def sort_contours(cnts, method="left-to-right"): reverse = False i = 0 if method == "right-to-left" or method == "bottom-to-top": reverse = True if method == "top-to-bottom" or method == "bottom-to-top": i = 1 boundingBoxes = [cv2.boundingRect(c) for c in cnts] #用一个最小的矩形,把找到的形状包起来 x,y,h,w (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse)) return cnts, boundingBoxes
# 读取模板图片 template = cv2.imread('ocr_a_reference.png') cv_show('template', template) # 模板图片灰度化。这里的模板图片本身就是二值化的因此没有明显区别。 template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) cv_show('templage_gray', template_gray) # 二值化,转化为数字为白色,背景为黑色的图片。 template_binary = cv2.threshold(template_gray, 127, 255, cv2.THRESH_BINARY_INV)[1] cv_show('template_binary', template_binary)
这里 threshold 后面的 [1] 表示取 threshold 函数返回值的第二个。 threshold 函数返回值为阈值和二值化后的图片,即这里仅保存返回后的图片。# 根据二值化的模板图,进行轮廓检测 cnts, hierarchy = cv2.findContours(template_binary , cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE) # 画出每个数字的轮廓 template_rect = cv2.drawContours(template.copy(), cnts, -1, (0,0,255), 2) cv_show('template_rect', template_rect) # 对十个数字根据左上角的位置进行排序,这样数字按照从小到大的顺序排列出来。 cnts = sort_contours(cnts, method="left-to-right")[0] number = {} # 根据排列的结果,将每个数字截取出来。将每个数字图片所对应的数字对应起来。 # 这里要注意,对像素值进行取值时,数组的行对应的是图片的y轴,列对应的图片的x轴。 for (i, cnt) in enumerate(cnts): (x,y,w,h) = cv2.boundingRect(cnt) roi = template_binary[y:y+h, x:x+w] roi = cv2.resize(roi, (57,88)) number[i] = roi
对信用卡信息进行处理,去除多余的背景信息。# 读取信用卡图片 cardImg = cv2.imread('credit_card_02.png') cardImg = cv2.resize(cardImg , (300, int(float(300 / cardImg.shape[1]) * cardImg.shape[0])) , interpolation=cv2.INTER_AREA) cv_show('cardImg', cardImg) cardImg_gray = cv2.cvtColor(cardImg, cv2.COLOR_BGR2GRAY) cv_show('cardImg_gray', cardImg_gray) # 指定卷积核大小 rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # 进行礼帽操作 cardImg_tophat = cv2.morphologyEx(cardImg_gray, cv2.MORPH_TOPHAT, rectKernel) cv_show('cardImg_open', cardImg_tophat)
对信用卡上的数字进行选取,对于非卡号数字进行剔除。# 使用sobel算子进行边缘检测,这里仅适用x方向的梯度。因为经过实验,使用x,y混合的梯度,效果并不理想。 sobelx = cv2.Sobel(cardImg_tophat, cv2.CV_64F, 1, 0, ksize=3) sobelx = cv2.convertScaleAbs(sobelx) (minX, maxX) = (np.min(sobelx), np.max(sobelx)) sobelx = (255 * ((sobelx - minX) / (maxX - minX))) sobelx = sobelx.astype('uint8') cv_show('sobelx', sobelx) #进行闭运算,使相邻的数字连接起来,这样便于筛选。 cardImg_close = cv2.morphologyEx(sobelx, cv2.MORPH_CLOSE, rectKernel) cv_show('cardImg_close', cardImg_close) cardImg_binary = cv2.threshold(cardImg_close, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)[1] cv_show('cardImg_binary', cardImg_binary) cardImg_close = cv2.morphologyEx(cardImg_binary, cv2.MORPH_CLOSE, sqKernel) cv_show('cardImg_close', cardImg_close) # 轮廓检测,检测出每一个数字区块 cnts, hierarchy = cv2.findContours(cardImg_close , cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE) cardImg_cnts = cv2.drawContours(cardImg.copy(), cnts, -1, (0,0,255), 2) cv_show('cardImg_cnts', cardImg_cnts) # 对轮廓进行筛选, 根据边框的尺寸仅保留卡号区域 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 = [] # 对每个4数字块进行处理 for (i, (x, y, w, h)) in enumerate(locs): group_output = [] group = cardImg_gray[y-5:y + h + 5, x-5:x + w + 5] group = cv2.threshold(group, 0, 255, cv2.THRESH_OTSU|cv2.THRESH_BINARY)[1] cv_show('group', group) group_cnts, group_hierarchy = cv2.findContours(group, cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE) group_cnts = sort_contours(group_cnts, method="left-to-right")[0] # 分割每个数字 for cnt in group_cnts: (nx,ny,nw,nh) = cv2.boundingRect(cnt) roi = group[ny:ny+nh, nx:nx+nw] roi = cv2.resize(roi, (57, 88)) cv_show('roi', roi) score = [] # 对每个数字进行模板匹配 for (number_i, number_roi) in number.items(): result = cv2.matchTemplate(roi, number_roi, cv2.TM_CCOEFF) score_ = cv2.minMaxLoc(result)[1] score.append(score_) group_output.append(str(np.argmax(score))) # 绘制每个数字 cv2.rectangle(cardImg, (x - 5, y - 5), (x + w + 5, y + h + 5), (0, 0, 255), 1) cv2.putText(cardImg, "".join(group_output), (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2) output.append(group_output) cv_show('cardImg', cardImg)