🧡💛💚💙💜OpenCV实战系列总目录
有任何问题欢迎在下面留言
本篇文章的代码运行界面均在Pycharm中进行
本篇文章配套的代码资源已经上传
下篇内容:
openCV实战-系列教程12:信用卡数字识别下(二值/灰度/礼帽/图像轮廓/模版匹配/sobel梯度/阈值/闭操作)项目实战、源码解读
1、总体流程解读
1.1 问题描述
信用卡数字识别项目实战,我们有一些信用卡的图片
我们需要将当前的输入的信用卡图片的卡号识别出来,并且将它框出来,会将前面的知识全都穿插到一起来完成这个操作。
1.2 问题分解过程
1、怎样把这些数字判断成一个4和1呢?之前我们讲过模板匹配,看一下这张图片:
当我找到信用卡卡号数字4的位置的时候,将它框起来,怎样让计算机知道这个小块的像素是4呢?
我们将有0-9数字的这张图片把他裁剪成10个小块的图像,而且在信用卡中框起来的小块图像大小保存一致。然后我们将信用卡中框起来的数字和这10个数字的小块图像进行像素的对比,差别最小的就判断成那个数字,这个过程是一个模板匹配,这样就能把数字4识别出来了。
但是做这个事有一个前提,就是需要保证模板中的数字字体和我们需要识别的字体保持一致。
2、我们之前也讲解过轮廓检测,但是比如0这种数字的形状可以很轻易的检测出轮廓,但是像1和4这种的轮廓就是这个数字形状的轮廓不是一个矩形,我们可以用外接矩形(这个问题也讲解过)的操作来解决这个问题。
3、在模板和信用卡图像都做这样的外接矩形,写个for循环将信用卡的外接矩形和模板的外接矩形进行对比计算,就可以计算出数字的识别。但是在此之前肯定需要做一些resize保证外接矩形大小一样。
4、但是看上面的信用卡图片,我们在识别轮廓检测中,会检测出多个轮廓,如my,我们需要保存多个轮廓索引,通过判断轮廓的长宽和长宽比例可以判断出这个轮廓是不是我们需要识别数字的轮廓。比如车牌识别,它的长宽比例都是固定,没有哪个车牌数字字母灯长宽比例不同。
5、假如得到20个轮廓,我们首先需要做一个过滤操作,过滤掉所有长宽比例不符合我们实现设定的规范的。最后剩下来的就是我们需要的那些轮廓了。
6、第一次我们可能得到是一些大致的轮廓,比如如图中这个4000的轮廓,再将这个轮廓分细节处理。
所以最后这个项目会用到前面很多的openCV的各种操作,比如形态学
2、参数设置
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to input image")
ap.add_argument("-t", "--template", required=True,
help="path to template OCR-A image")
args = vars(ap.parse_args())
i代表image,就是输入图像
t带边template,就是输入的模板
这部分的代码就是为了指定好两个输入数据的路径
新版本的pycharm的配置参数发生了一些改变,可以看这个教程
所以运行的时候需要添加一些配置参数:
--image images/credit_card_03.png --template ocr_a_reference.png
通过修改配置参数,来选择识别哪个信用卡的卡号。
3、模版数据处理
3.1 读取、灰度、二值
img = cv2.imread(args["template"])
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)
一般我们得到轮廓都是用二值来处理,将模板读进来并打印,灰度处理并打印,二值处理并打印:
3.2 计算轮廓、画出轮廓、打印图像、打印维度
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)
print(np.array(refCnts).shape)
cv2.findContours轮廓检测
- 别忘了要执行ref.copy()
- cv2.RETR_EXTERNAL表示检测外轮廓,因为需要计算外接矩形
- cv2.CHAIN_APPROX_SIMPLE,保留终点坐标
- 轮廓信息保存在refCnts参数中
cv2.drawContours画出轮廓
然后画出10个轮廓
3.3 轮廓排序
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] # 排序,从左到右,从上到下
每个序号对应轮廓对应的数字
这里单独做了一个函数myutils.sort_contours:
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
关键部分在于:
1、cv2.boundingRect计算外接矩形,实际上只需要对每一个点的横坐标,根据横坐标的的大小就能进行排序了
2、cnts是每一个轮廓的索引,boundingBoxes是轮廓
3.4 遍历轮廓
digits = {}
# 遍历每一个轮廓
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, 88))
# 每一个数字对应每一个模板
digits[i] = roi
- 构造一个字典
- i, c分别对应每一个轮廓和每一个轮廓的索引
- 获取外接矩形
- 通过外接矩形框出轮廓,找出这个框
- 框太小,resize成一个稍微大一点的部分
- i是从0开始的,roi是从里面抠出来的轮廓,将他们对应到字典里面
执行完这个for循环后,这个字典保存了0-9数字作为key,对应的是这个数字对应的小方块图像
下篇内容:
openCV实战-系列教程12:信用卡数字识别下(二值/灰度/礼帽/图像轮廓/模版匹配/sobel梯度/阈值/闭操作)项目实战、源码解读