我认为一个合格的项目组织者或者执行者需要从项目的整体架构掌握本身,首先对该项目的逻辑做一个整体的介绍:
一、项目需求:
1、信用卡数字的模板,用于匹配其他信用卡(并且已知)
2、各种信用卡,用于测试算法的准确性
二、项目流程
①
1)读入图像、灰度化、二值化进行数据预处理
2)画出处理的轮廓
3)对各个轮廓进行按照left-right排序
4)排序好之后进行保存
②
5)待处理的图像数据,灰度化
6)数据预处理:a)首先对轮廓进行顶帽操作,过滤掉不必要的噪声
b)完成形态学的闭操作,使得轮廓粘合在一起,方便找出需要的轮廓的范围,实现归一化、二值化、如果效果不好在进行一次形态学闭操作
7)以上操作可以分出四个区域,对四个区域的gray_image进行轮廓遍历,再进行轮廓细分(这个地方需要大家好好理解)。
8)打分,进行模板匹配打分。是在大的for循环中,执行小循环,形成4*4的scores矩阵,最后得出结果!
三、项目过程
1、对模板进行数据预处理(我没有使用命令行参数,这个需要的各位可以使用,下面对argpaser模块进行解释)
很多人查资料关于argparse模块的用法,网上众说纷纭,对于如果参数很多,比较复杂,并且类型不统一,那么argparse可以很好的解决这些问题,下面一个实例解释:
# 1、
import argparse
# description参数可以用于描述脚本的参数作用,默认为空 # 可以不写description
# 调用命令行参数,求长方形的面积
parser=argparse.ArgumentParser(description="The square of rectangle") # 声明一个对象
parser.add_argument('-w','--width',type=int,required=True,help='Use width')
parser.add_argument('-l','--length',type=int,required=True,help='use length')
# --width 是这个属性变量 -w 是选择性参数,顾名思义,可以在赋值的时候使用-w代替,
# required 是对非空的时候进行提示而不是直接运行出现逻辑错误,help就是咱们普通的理解
args=parser.parse_args() # 解释创建的对象
square = (args.width) * (args.length) # 注意调用的方式
print(square)
```python
我使用的是pycharm,大家在使用命令行参数的时候需要配置,菜单栏->run->edit configure...进行配置即可,有问题大家共同探讨
2、数据预处理 在进行数据预处理的过程,需要将模板的各个数据轮廓,
即cv2.findContours()命令,下面对这个函数进行解析
# 读入模板图像
img = cv2.imread('images/ocr_a_reference.png')
# cv_show('img',img)
# 灰度化
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# cv_show('img_gray',img_gray)
# 二值化
img_threshold = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY_INV)[1] # [1]这个函数返回两个值 第二值比较有用 cv2.THRESH_BINARY_INV是取反操作,因为检测轮廓的时候目标是白色区域,故将原先二值化按照取反
#
# cv_show('img_threshold',img_threshold)
对函数cv2.findContours()函数,需要注意OpenCV4系列与之前的版本有所区别,之前这个函数是返回3个值,从4版本开始返回两个值:contour,binary ,其中c…为轮廓,b…为维度
3、
contour_s,binary = cv2.findContours(img_threshold.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 在外边缘进行画框
# 1)注意需要将灰度图像拷贝一下,否则将在原图进行修改
# 2)需要将图像处理为灰度图才能进行二值化(基础知识)
cv2.drawContours(img,contour_s,-1,(0,0,255),3)
cv_show('img_draw',img) # -1 代表画出所有轮廓
4、介绍对轮廓排序算法
from imutils import contours # 你可以应用contours.sort_contours对轮廓进行排序,按照left-to-right的顺序,也可以使用方法myutils.sorted_contours自己定义的方法
# 注明:一下方法可以直接使用cv2.方法,这个方法作为参考,都可以使用!
import cv2
# 排序算法
def sorted_contours(cnt,method = 'left-to-right'):
reverse = False
i = 0
if method == 'right-to-left' or method == 'bottom-to-top' :
reverse = True # 指定排序顺序
if method == 'bottom-to-top' or method == 'top-to-bottom':
i = 1 # 指定按照元组中那个元素进行操作
boundingBoxs = [cv2.boundingRect(c) for c in cnt]
(cnt,boundingBoxs) = zip(*sorted(zip(cnt,boundingBoxs),key=lambda b:b[1][i],reverse = reverse))
return cnt,boundingBoxs
def resize(image,width=None,height=None,inter=cv2.INTER_AREA):
dim = None
(h,w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width,int(h * r))
resized = cv2.resize(image,dim,interpolation=inter)
return resized
5、对所有模板进行保存
digitals = {}
contour_s = myutils.sorted_contours(contour_s,method='left-to-right')[0]
for i,c in enumerate(contour_s):
x,y,w,h = cv2.boundingRect(c)
roi = img_gray[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
cv_show('roi_resize',roi)
digitals[i] = roi
6、对上述进行总结
1)读入图像、灰度化、二值化进行数据预处理
2)画出处理的轮廓
3)对各个轮廓进行按照left-right排序
4)排序好之后进行保存
第二阶段:对需要辨别的信用卡进行操作
7、图像数据的输入
image = cv2.imread('images/credit_card_03.png')
image = myutils.resize(image,width=300)
cv_show('image',image)
# 灰度化
image_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('image_gray',image_gray)
8、数据噪声处理
为了使得将要识别的数字,轮廓更加明显,进行以下操作
1)顶帽操作:突出区域
kernel = np.ones((3,3),np.uint8)
tophat = cv2.morphologyEx(image_gray,cv2.MORPH_TOPHAT,kernel) # 过滤掉一些不必要的区域
cv_show('tophat',tophat)
# 以上进行顶帽操作:形态学顶帽操作是计算原图像与开运算结果之差,形态学黑帽操作是计算闭运算结果图与原图像之差。各自有什么作用呢? 那么首先要知道开运算和闭运算分别给图像减少和增加了什么东西,因此它们与原图像的差就是这些增加和减少的东西吧。
# 说明一下开运算和闭运算的作用如下:
# 形态学开运算能排除小区域物体、消除孤立点、去噪、平滑物体的轮廓。
# 形态学闭运算能填充目标区域内的离散小空洞和分散部分。
2)sobel梯度处理
这个需要根据项目处理的结果来,我这里需要对x,y都进行sobel算子处理,才能使得更明显,轮廓开始聚集
gradx = cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=-1)
gardx = np.absolute(gradx)
# 执行归一化操作
(minval,maxval) = (np.min(gradx),np.max(gradx))
gradx = (255*(gradx - minval)/(maxval - minval))
cv_show('gradx',gradx)
由于不如x,y同时操作效果好,故对y做同样操作
grady = cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=0,dy=1,ksize=-1)
grady = np.absolute(grady)
(g_min,g_max) =(np.min(grady),np.max(grady))
grady = (255*(gradx - g_min)/(g_max - g_min))
# 将二者合起来
gradxy = cv2.addWeight(gradx,0.5,grady,0.5,0) # 最后是偏置项,具体每一个函数的查看可以自行百度
# 最后效果还是很好的,可以将轮廓划分的比较好
3)形态学操作-目的:完成轮廓粘合(这个用词可能不太准确)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) # 这个矩阵的选择非常非常重要,将直接影响效果问题(9,3)是大部分取左边的数值,防止被上边的过分稀释。
image_closing = cv2.morphologyEx(gradxy,cv2.MORPH_CLOSE, rectKernel)
cv_show('1',image_closing)
im_thresh = cv2.threshold(image_closing,0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh',im_thresh)
i_closing = cv2.morphologyEx(im_thresh,cv2.MORPH_CLOSE, rectKernel) # 再进行一次闭操作效果还是比较不错的,你也可以不做,根据你的项目需求来。
cv_show('i_closing',i_closing)
4)画出轮廓来
contour_ss,binar_y = cv2.findContours(i_closing.copy(),cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image.copy(),contour_ss,-1,(0,0,255),3)
cv_show('i',image)
9、对选择的轮廓进行筛选
locs = []
for i,c in enumerate(contour_ss):
x,y,w,h = cv2.boundingRect(c)
ar = w/float(h)
if ar > 2.5 and ar 40 and w 10 and h < 20): # 条件不严格可能导致过滤不掉其他噪声
#上述数据是进行测试之后的,当然你也可以进行其他测试
locs.append((x,y,w,h))
locs = sorted(locs,key = lambda x :x[0]) # 按照x的顺序,对locs进行排序,然后保存
print(locs) # 用于测试
# 对排好顺序的大轮廓,在gray图像中表示出来
for (i,(x,y,w,h)) in enumerate(locs):
group = image_gray[y-5:y+h+5,x-5:x+w+5]
cv_show('group',group)
group_thresh = cv2.threshold(group,0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('group_thresh',group_thresh)
group_contours,binar__y = cv2.findContours(group_thresh.copy(),cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
result_group = contours.sort_contours(group_contours, method='left-to-right')[0] # 这个可以直接利用cobtours函数
# result_group 是大轮廓分成的小轮廓的列表
for (i,(gx,gy,gw,gh)) in enumerate(result_group):
(x, y, w, h) = cv2.boundingRect(c)
roi = group[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
cv_show('roi', roi)
# 下面是关键一步,是计算模板匹配的得分,并对其进行求最大值
scores = []
for digital,digitalroi in digitals.items():
result = cv2.matchTemplate(roi,digitalroi,cv2.TM_CCOEFF)
# print(result.shape)
(_, score, _, _) = cv2.minMaxLoc(result)# 其中注意模板匹配的方法,这个是取得分的最大值,你也可以用归一化等方法,完成操作
scores.append(score)
# 大家在做这一步的过程中可能遇到以下问题:# AttributeError: 'float' object has no attribute 'append' 很多问题导致,主要是索引的问题需要注意
print(argmin(scores))
group_output = []
group_output.append(str(np.argmin(scores))) # AttributeError: 'NoneType' object has no attribute 'append'因为你不能一切方法皆函数用,.append()方法不能有返回值 即不能group_output = group_output.append(...)
结果展示:
### 知识点累积:
cv2.rectangle() 在指定区域画出矩形
cv2.putText()一个添加文本函数,具体用法,可自行百度
extend()方法是在一个列表后边继续添加另一个列表
注意join()函数的用法,是在列表元素之间连接
10、对结果进行展示:
cv2.rectangle(image, (gy - 5, gy - 5),
(gx + gw + 5, gy + gh + 5), (0, 0, 255), 1)
cv2.putText(image, "".join(group_output), (gx, gy - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 得到结果
output.extend(group_output)
# 打印结果
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)
最后将所有的程序源码附在这里,大家针对具体问题具体对待,对于一些参数需要继续修改,欢迎大家留言指正!
# 引入需要的模块
import cv2
import matplotlib.pyplot as plt
from imutils import contours
import myutils
import numpy as np
# 定义画图函数,方便对结果进行检验
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
# 读入模板图像
img = cv2.imread('images/ocr_a_reference.png')
cv_show('img',img)
# 灰度化
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('img_gray',img_gray)
# 二值化
img_threshold = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY_INV)[1] # 这个函数返回两个值 第二值比较有用
cv_show('img_threshold',img_threshold)
# 对轮廓进行检测
contour_s,binary = cv2.findContours(img_threshold.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 在外边缘进行画框
cv2.drawContours(img,contour_s,-1,(0,0,255),3)
cv_show('img_draw',img)
# 遍历每一个轮廓,对x,y坐标排序
digitals = {}
contour_s = myutils.sorted_contours(contour_s,method='left-to-right')[0]
for i,c in enumerate(contour_s):
x,y,w,h = cv2.boundingRect(c)
roi = img_gray[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
cv_show('roi_resize',roi)
digitals[i] = roi
# 开始处理需要识别卡的信息
image = cv2.imread('images/credit_card_03.png')
image = myutils.resize(image,width=300)
cv_show('image',image)
# 灰度化
image_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('image_gray',image_gray)
# 突出边界,突出更明亮的部分
kernel = np.ones((3,3),np.uint8)
tophat = cv2.morphologyEx(image_gray,cv2.MORPH_TOPHAT,kernel) # 过滤掉一些不必要的区域(还需要再看看)
cv_show('tophat',tophat)
# sobel算子突出更明亮的区域
gradx = cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=-1)
gradx = np.absolute(gradx)
(g_min,g_max) =(np.min(gradx),np.max(gradx))
gradx = (255*(gradx - g_min)/(g_max - g_min))
print (np.array(gradx).shape)
gradx = gradx.astype("uint8")
grady = cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=0,dy=1,ksize=-1)
grady = np.absolute(grady)
(g_min,g_max) =(np.min(grady),np.max(grady))
grady = (255*(gradx - g_min)/(g_max - g_min))
print (np.array(grady).shape)
grady = gradx.astype("uint8")
gradxy = cv2.addWeighted(gradx,0.5,grady,0.5,0)
cv_show('gradxy',gradxy)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) # 这个矩阵的选择非常非常重要,将直接影响效果问题
image_closing = cv2.morphologyEx(gradxy,cv2.MORPH_CLOSE, rectKernel)
cv_show('1',image_closing)
im_thresh = cv2.threshold(image_closing,0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh',im_thresh)
i_closing = cv2.morphologyEx(im_thresh,cv2.MORPH_CLOSE, rectKernel)
cv_show('i_closing',i_closing)
contour_ss,binar_y = cv2.findContours(i_closing.copy(),cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image.copy(),contour_ss,-1,(0,0,255),3)
cv_show('i',image)
locs = []
for (i,c) in enumerate(contour_ss):
(x,y,w,h) = cv2.boundingRect(c)
ar = w/float(h)
if ar > 2.5 and ar 40 and w 10 and h < 20): # 条件不严格可能导致过滤不掉其他噪声
locs.append((x,y,w,h))
locs = sorted(locs, key=lambda x: x[0])
print(locs)
output = []
for (i,(gx,gy,gw,gh)) in enumerate(locs):
group_output = []
group = image_gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
cv_show('group',group)
group_thresh = cv2.threshold(group,0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('group_thresh',group_thresh)
group_contours,binar__y = cv2.findContours(group_thresh.copy(),cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
result_group = myutils.sorted_contours(group_contours, method='left-to-right')[0]
#顺序处理完毕
#再进行resize操作
for c in result_group:# 注意这是个列表
(x, y, w, h) = cv2.boundingRect(c)
roi = group[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
cv_show('roi', roi)
scores = []
for (digital,digitalroi) in digitals.items():
result = cv2.matchTemplate(roi,digitalroi,cv2.TM_CCOEFF)
# print(result.shape)
(_, score, _, _) = cv2.minMaxLoc(result)
scores.append(score) # AttributeError: 'float' object has no attribute 'append' 很多问题导致,主要是索引的问题需要注意
print(scores)
print(np.argmin(scores))
group_output.append(str(np.argmin(scores))) # AttributeError: 'NoneType' object has no attribute 'append'因为g = g了
# 画出来
cv2.rectangle(image, (gy - 5, gy - 5),
(gx + gw + 5, gy + gh + 5), (0, 0, 255), 1)
cv2.putText(image, "".join(group_output), (gx, gy - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 得到结果
output.extend(group_output)
# 打印结果
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)
``
(鉴于笔者水平不是很高,需要大家继续批评指正,有问题留言讨论)
参考文献:
https://www.cnblogs.com/wmy-ncut/p/9889294.html
https://blog.csdn.net/weixin_42081389/article/details/87935735
https://blog.csdn.net/zjuxsl/article/details/79437563
https://blog.csdn.net/S_o_l_o_n/article/details/89046665
bit__zhang
原创文章 2获赞 1访问量 75
关注
私信
展开阅读全文