使用Python+OpenCV进行卡类型及16位卡号数字的OCR功
目录
- 1. 效果图
- 2. 原理
- 2.1 OCR-A字体
- 2.2 检测过程步骤
- 2.3 优化
- 3. 源代码
这篇博客将介绍如何通过OpenCV和Python使用模板匹配执行光学字符识别(OCR)。具体来说,将使用Python+OpenCV实现模板匹配算法,以自动识别卡的类型和以及16位卡号数字。
在比较数字时,模板匹配是一种非常快速的方法。
为此将图像处理管道分为4个步骤:
- 通过各种图像处理技术检测信用卡上四组四个数字,包括形态学操作、阈值和轮廓提取。
- 从四个分组中提取每个单独的数字,得到16个需要分类的数字。
- 将模板匹配应用于每个数字,将其与OCR-A字体进行比较,以获得数字分类。
- 检查信用卡号的第一位数字以确定发卡公司。
在对信用卡OCR系统进行评估后,发现如果发卡信用卡公司使用OCR-A字体作为数字,该系统的准确率为100%。 优化可以考虑在野外采集信用卡的真实图像,并训练机器学习模型(通过标准特征提取或训练或卷积神经网络),以进一步提高此系统的准确性。
1. 效果图
首先了解一下卡的组成:
OCR-A 参考字体识别如下:原始图 VS 灰度图 VS 阈值化图 VS 轮廓每个数字提取图:
灰度图:忽略颜色对轮廓提取的影响
阈值化图:使得轮廓在前景白色,背景黑色便于轮廓提取。
轮廓提取图:提取每个数字ROI并记录,方便后续对比卡片中的区域以识别出对应的数字。
以下卡号均是演示卡,
正确的识别卡的类型和卡号,效果图1:
识别过程1——原图 VS 灰度图 VS 白帽图 VS 梯度图如下:
灰度图:忽略色彩影响
白帽图:从较暗的背景中提取较亮的区域
梯度图:计算Schaar梯度图,便于了解图像的色彩分配及提取;
识别过程2——形态学闭合图 VS 二值化图1 VS 阈值化图2 如下:
形态学闭合图:矩形框形态学闭合操作,以帮助闭合信用卡数字之间的小的缝隙
二值化图:以便于提取
阈值化图:方形框形态学闭合操作,以二次帮助闭合信用卡数字区域之间的缝隙
识别过程3——轮廓过滤图 VS 提取最终效果图 如下:
轮廓过滤图:根据面积及纵横比,只保留卡片中的卡号区
最终效果图:提取4组4数字每一个组,然后对每一个组中的4个数字进行截取ROI并识别,并与之前存储的数字ROI进行模板匹配,选取匹配值最高的作为最终结果。
2. 原理
2.1 OCR-A字体
OCR-A字体,是一种专门用于辅助光学字符识别算法的字体。
主要分为:
检测图像中信用卡的位置;本地化信用卡上的四组四位数字;应用OCR识别信用卡上的16位数字;识别信用卡的类型。
Tesseract库在某些情况无法正确识别数字(这可能是因为Tesseract未接受信用卡示例字体培训)。
2.2 检测过程步骤
在字典中存储卡类型映射关系(卡号的第一位数字代表卡类型)。获取参考图像并提取数字。将数字模板存储在字典中。本地化四个信用卡号组,每个组有四位数字(总共16位)。提取要“匹配”的数字。对每个数字执行模板匹配,将每个单独的ROI与每个数字模板0-9进行比较,同时存储每个尝试匹配的分数。查找每个候选数字的最高分数,并构建一个名为“输出”的列表。其中包含信用卡号。将信用卡号和信用卡类型输出到终端,并将输出图像显示到屏幕上。
2.3 优化
使用OpenCV和Python匹配OCR脚本的模板在100%的时间内正确识别了16位数字中的每一位。然而在将OCR图像应用于真实的信用卡图像时,考虑到照明条件、视角和其他一般噪音的变化,可能需要采取更面向机器学习的方法。
3. 源代码
# 信用卡类型及卡号OCR系统 # USAGE # python ocr_template_match.py --reference images/ocr_a_reference.png --image images/credit_card_05.png import argparse import cv2 import imutils import numpy as np # 导入必要的包 from imutils import contours # 构建命令行参数及解析 # --image 必须 要进行OCR的输入图像 # --reference 必须 参考OCR-A图像 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="path to input image") ap.add_argument("-r", "--reference", required=True, help="path to reference OCR-A image") args = vars(ap.parse_args()) # 定义一个字典(映射信用卡第一位数字和信用卡类型的编号) FIRST_NUMBER = { "3": "American Express", "4": "Visa", "5": "MasterCard", "6": "Discover Card" } # 从磁盘加载参考OCR-A图像,转换为灰度图,阈值化图像以显示为白色前景和黑色背景 # 并反转图像 # and invert it, such that the digits appear as *white* on a *black* ref_origin = cv2.imread(args["reference"]) cv2.imshow("ref_origin", ref_origin) ref = ref_origin.copy() ref = cv2.cvtColor(ref, cv2.COLOR_BGR2GRAY) cv2.imshow("ref_gray", ref) ref = cv2.threshold(ref, 180, 255, cv2.THRESH_BINARY)[1] cv2.imshow("ref_threshhold", ref) cv2.waitKey(0) # 寻找OCR-A图像中的轮廓(数字的外轮廓线) # 并从左到右排序轮廓,初始化一个字典来存储数字ROI refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) print('findContours: ', len(refCnts)) refCnts = imutils.grab_contours(refCnts) refCnts = contours.sort_contours(refCnts, method="left-to-right")[0] digits = {} # 遍历OCR-A轮廓 for (i, c) in enumerate(refCnts): # 计算数字的边界框,提取它,缩放到固定的大小 (x, y, w, h) = cv2.boundingRect(c) cv2.rectangle(ref_origin, (x, y), (x + w, y + h), (0, 255, 0), 2) roi = ref[y:y + h, x:x + w] roi = cv2.resize(roi, (57, 88)) # 更新数字字典,数字匹配ROI digits[i] = roi cv2.imshow("ref and digits", ref_origin) cv2.waitKey(0) # 初始化矩形和方形结构内核 # 在图像上滑动它来进行(卷积)操作,如模糊、锐化、边缘检测或其他图像处理操作。 # 使用矩形函数作为Top-hat形态学运算符,使用方形函数作为闭合运算。 rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 准备进行OCR的输入图像 # 加载输入图像,保持纵横比缩放图像宽度为300,转换为灰度图 origin = cv2.imread(args["image"]) origin = imutils.resize(origin, width=300) image = origin.copy() cv2.imshow("origin", origin) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imshow("gray", gray) # 执行形态学操作 # 应用tophat(白帽)形态学操作以在暗的背景中提取出亮的区域(信用卡上的数字卡号) # Top hat操作在深色背景(即信用卡号)下显示浅色区域 tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) cv2.imshow("tophat", tophat) # 计算Scharr梯度,计算梯度值 # 在白色礼帽上,计算x方向的Scharr梯度,然后缩放到范围[0, 255] 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)) # 最小/最大归一化, 由float转换gradX到uint8范围[0-255] gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) gradX = gradX.astype("uint8") cv2.imshow("gradient", gradX) # 使用矩形框应用闭合操作以帮助闭合信用卡数字之间的小的缝隙 # 应用Otsu's阈值方法二值化图像 gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) cv2.imshow("morphologyEx", gradX) thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv2.imshow("thresh1", thresh) # 在二值化图像上,应用二次闭合操作 # 再一次方形框形态学操作,帮助闭合信用卡数字区域之间的缝隙 thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) cv2.imshow("thresh2", thresh) # 阈值图像中查找轮廓,然后初始化数字位置列表 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) locs = [] # 遍历轮廓 for (i, c) in enumerate(cnts): # 计算轮廓的边界框,并计算纵横比 (x, y, w, h) = cv2.boundingRect(c) ar = w / float(h) # 由于信用卡有固定的4组4数字,可以根据纵横比来寻找潜在的轮廓 if ar > 2.5 and ar < 4.0: # 轮廓可以在最小/最大宽度上进一步修剪 if (w > 40 and w < 55) and (h > 10 and h < 20): # 添加数字组轮廓的编辑框轮廓到位置list locs.append((x, y, w, h)) cv2.rectangle(origin, (x, y), (x + w, y + h), (255, 0, 0), -1) cv2.imshow("contours filter", origin) # 突出显示信用卡上四组四位数字(总共十六位)。 # 从左到右排序轮廓,并初始化list来存储信用卡数字列表 locs = sorted(locs, key=lambda x: x[0]) output = [] # 遍历四组四位数字 for (i, (gX, gY, gW, gH)) in enumerate(locs): # 初始化存放每组数字的list groupOutput = [] # 提取每组4位数字的灰度图ROI # 应用阈值方法从背景信用卡中分割数字 group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5] group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 检测组中每个单独数字的轮廓 # 从左到右排序轮廓 digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) digitCnts = imutils.grab_contours(digitCnts) digitCnts = contours.sort_contours(digitCnts, method="left-to-right")[0] # 遍历数字轮廓 for c in digitCnts: # 计算每个单独数字的边界框 # 提取数字,缩放以拥有和参考OCR-A字体模板图像相同的大小 (x, y, w, h) = cv2.boundingRect(c) roi = group[y:y + h, x:x + w] roi = cv2.resize(roi, (57, 88)) # 初始化模板匹配分数list scores = [] # 遍历参考数字名和数字ROI for (digit, digitROI) in digits.items(): # 应用基于相关性的模板匹配,计算分数,更新分数list # apply correlation-based template matching, take the # score, and update the scores list result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF) (_, score, _, _) = cv2.minMaxLoc(result) scores.append(score) # 数字ROI的分类将取 模板匹配分数中分数最大的参考数字 # the classification for the digit ROI will be the reference # digit name with the *largest* template matching score groupOutput.append(str(np.argmax(scores))) # 围绕每组画一个矩形,并以红色文本标识图像上的信用卡号 # 绘制每组的数字识别分类结果 cv2.rectangle(image, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 2) cv2.putText(image, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2) # 更新输出数字分组列表 # Pythonic的方法是使用extend函数,它将iterable对象的每个元素(本例中为列表)追加到列表的末尾 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)
参考 https://www.pyimagesearch.com/2017/07/17/credit-card-ocr-with-opencv-and-python/
到此这篇关于使用Pyhton+OpenCV进行卡类型及16位卡号数字的OCR功能的文章就介绍到这了,更多相关Pyhton+OpenCV卡号数字识别内容请搜索hwidc以前的文章或继续浏览下面的相关文章希望大家以后多多支持hwidc!
【自由互联:韩国服务器 转载请保留连接】