使用Python,OpenCV进行卡类型及16位卡号数字的OCR

  • 1. 效果图
  • 2. 原理
    • 2.1 OCR-A字体
    • 2.2 检测过程步骤
    • 2.3 优化
  • 3. 源代码
  • 参考

这篇博客将介绍如何通过OpenCV和Python使用模板匹配执行光学字符识别(OCR)。具体来说,将使用Python+OpenCV实现模板匹配算法,以自动识别卡的类型和以及16位卡号数字。

在比较数字时,模板匹配是一种非常快速的方法。

为此将图像处理管道分为4个步骤:

  1. 通过各种图像处理技术检测信用卡上四组四个数字,包括形态学操作、阈值和轮廓提取。
  2. 从四个分组中提取每个单独的数字,得到16个需要分类的数字。
  3. 将模板匹配应用于每个数字,将其与OCR-A字体进行比较,以获得数字分类。
  4. 检查信用卡号的第一位数字以确定发卡公司。

在对信用卡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字体,是一种专门用于辅助光学字符识别算法的字体。

主要分为:

  1. 检测图像中信用卡的位置;
  2. 本地化信用卡上的四组四位数字;
  3. 应用OCR识别信用卡上的16位数字;
  4. 识别信用卡的类型。

Tesseract库在某些情况无法正确识别数字(这可能是因为Tesseract未接受信用卡示例字体培训)。

2.2 检测过程步骤

  1. 在字典中存储卡类型映射关系(卡号的第一位数字代表卡类型)。
  2. 获取参考图像并提取数字。
  3. 将数字模板存储在字典中。
  4. 本地化四个信用卡号组,每个组有四位数字(总共16位)。
  5. 提取要“匹配”的数字。
  6. 对每个数字执行模板匹配,将每个单独的ROI与每个数字模板0-9进行比较,同时存储每个尝试匹配的分数。
  7. 查找每个候选数字的最高分数,并构建一个名为“输出”的列表。其中包含信用卡号。
  8. 将信用卡号和信用卡类型输出到终端,并将输出图像显示到屏幕上。

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.pngimport argparseimport 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))# 更新数字字典,数字匹配ROIdigits[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):# 添加数字组轮廓的编辑框轮廓到位置listlocs.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):# 初始化存放每组数字的listgroupOutput = []# 提取每组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))# 初始化模板匹配分数listscores = []# 遍历参考数字名和数字ROIfor (digit, digitROI) in digits.items():# 应用基于相关性的模板匹配,计算分数,更新分数list# apply correlation-based template matching, take the# score, and update the scores listresult = 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 scoregroupOutput.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/

使用Python,OpenCV进行卡类型及16位卡号数字的OCR相关推荐

  1. Python OpenCV开发MR智能人脸识别打卡系统(一、需求分析与系统设计)

    需要源码请点赞关注收藏后评论区留言私信~~~ 整体系统讲解如下链接 Python OpenCV开发MR智能人脸识别打卡系统(二.文件系统.数据实体模块设计) Python OpenCV开发MR智能人脸 ...

  2. Python OpenCV开发MR智能人脸识别打卡系统(四、服务模块设计)

    需要源码请点赞关注收藏后评论区留言私信~~~ 整体系统讲解如下 Python OpenCV开发MR智能人脸识别打卡系统(一.需求分析与系统设计) Python OpenCV开发MR智能人脸识别打卡系统 ...

  3. Python OpenCV开发MR智能人脸识别打卡系统(三、工具模块设计)

    需要源码请点赞关注收藏后评论区留言私信~~~ 整体系统讲解如下 Python OpenCV开发MR智能人脸识别打卡系统(一.需求分析与系统设计) Python OpenCV开发MR智能人脸识别打卡系统 ...

  4. Python OpenCV开发MR智能人脸识别打卡系统(五、程序入口设计与测试)

    需要源码请点赞关注收藏后评论区留言私信~~~ 整体系统讲解如下 Python OpenCV开发MR智能人脸识别打卡系统(一.需求分析与系统设计) Python OpenCV开发MR智能人脸识别打卡系统 ...

  5. short类型占16位,可以赋给short类型的最大值32767,最小值为-32768

    short类型占16位,可以赋给short类型的最大值32767,最小值为-32768 最大值0111111111111111 = 32767 最小值1000000000000000 = -32768

  6. 生成16位卡号和激活码

    生成16位不重复数字,每四位"-"隔开 16位卡号 工具类(规则可自己修改生成) 用例 卡号(16位数字,每4位之间用-隔开) 激活码(12位数字+随机字母) 16位卡号 工具类( ...

  7. python十进制单精度浮点(float)转16位16进制(FP16 hex)

    python十进制单精度浮点(float)转16位16进制(FP16 hex) 目的 将神经网络权重存放到FPGA内部需要将可训练参数从float转换到FP16 hex. Code # tt.py i ...

  8. [转载] Python中三种类型的引号(单引号、双引号、三引号)

    参考链接: Python中的三引号 当向Python输入一个字符串时,要将字符串放到引号中,Python含有三种类型的引号: 单引号形式:'  ' 双引号形式:"  " 三引号形式 ...

  9. MD5加密方式和MD5加密后的类型(16位和32位)

    文章目录 1. 加密方式 2. 加密后的格式 代码收录 1. 加密方式 MD5一般的加密方式是下面这种方式 public String Md5(String str) {if (str.equals( ...

最新文章

  1. SPOJ GSS3-Can you answer these queries III-分治+线段树区间合并
  2. 【青少年编程】【一级】舞者凯希
  3. PSS:更简单有效的End-to-End检测
  4. golang 二维切片
  5. SQLServer自增变量修复
  6. .Net多线程文章收集
  7. SQLServer、Mysql、Oracle 创建、删除用户和授予用户权限
  8. 根据录入的计算公式计算_小规模纳税人增值税计算公式是什么,什么人能被称为小规模纳税人?- 理财技巧...
  9. HTML5新增的video标签,HTML5中video标签的使用方法
  10. 什么是倾斜45度的火山图?
  11. mysql exporter_mysql exporter
  12. pythonselenium教程模拟鼠标和键盘_【02篇】python+selenium实现Web自动化:鼠标操作和键盘操作!...
  13. python做前端开发_Python开发【前端】:汇总
  14. java 数字水印_如何使用JAVA实现数字水印
  15. 一个月空余时间微信诗词小程序前后端开发上线实践指南
  16. 带电检测必要性_GIS的概念和定期局部放电检测的重要性
  17. 数据总线,地址总线,控制总线
  18. 1、如何给视频和照片进行调色
  19. 移动拼图游戏(八数码问题) BFS版
  20. 华师大计算机在线测试,华师大英语B在线测试.doc

热门文章

  1. 汽车HUD(Head-up Display)的技术难点
  2. 云服务器dns配置文件,linux服务器dns配置文件
  3. web app升级—带进度条的App自动更新
  4. Linux磁盘空间满的处理方法
  5. [BZOJ3337] ORZJRY I --块状链表大毒瘤
  6. 2022-2028年中国化学纤维行业市场研究及前瞻分析报告
  7. Bundle捆绑压缩技术
  8. SQL Server数据类型
  9. 《OpenCV3编程入门》学习笔记7 图像变换(五 )直方图均衡化
  10. OpenCV+python:图像梯度