文章目录
一,基础函数用法
1,argparse的用法
1.1
使用argparse模块创建一个ArgumentParser解析对象,可以理解成一个容器,将包含将命令行解析为Python数据类型所需的所有信息。
ap = argparse.ArgumentParser()#可以理解成一个容器,将包含将命令行解析为Python数据类型所需的所有信息。
ap.add_argument("-i", "--image", default='./images/credit_card_01.png',
help="path to input image")
ap.add_argument("-t", "--template", default='./ocr_a_reference.png',
help="path to template OCR image")
args = vars(ap.parse_args())
1.2
ap.add_argument("-i", "--image", default='./images/credit_card_01.png',
help="path to input image")
我们通过对象的add_argument函数来增加参数。这里我们增加了的参数是image和template,其中’-i’,’–image’表示同一个参数,default参数表示我们在运行命令时若没有提供参数,程序会将此值当做参数值。help,参数的帮助信息,当指定为 argparse.SUPPRESS时表示不显示该参数的帮助信息。打印当前解析器中所有选项的完整的帮助信息然后退出。默认情况下,help动作会自动添加到解析器中。
1.3
最后采用对象的parse_args获取解析的参数这里要注意个问题,当’-‘和’–'同时出现的时候,系统默认后者为参数名,前者不是,但是在命令行输入的时候没有这个区分接下来就是打印参数信息了。
2,sorted用法
函数定义:
sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list
参数说明:
iterabele : 是可迭代的类型
cmp :用于比较的函数,比较什么由key决定,有默认值,迭代集合中的一项
key : 用列表元素的某个属性和函数作为关键字,有默认值,迭代集合中的一项
reverse :排序规则,reverse=True或reverse=False,有默认值
返回值: 是一个经过排序的可迭代类型,和Iterable一样
sorted与sort函数不同的是,sort是在原位置重新排列列表,而sorted()是产生一个新的列表
1.1基本排序
>>> print sorted([1,2,3,6,5,4])
[1, 2, 3, 4, 5, 6]
1.2cmp参数排序
>>> L = [('b',2),('a',5),('c',1),('d',4)]
>>> print sorted(L, cmp=lambda x,y:cmp(x[1],y[1]))
[('c', 1), ('b', 2), ('d', 4), ('a', 5)]
>>> print sorted(L, cmp=lambda x,y:cmp(x[0],y[0]))
[('a', 5), ('b', 2), ('c', 1), ('d', 4)]
1.3key参数排序
>>> print sorted(L, key=lambda x:x[1])
[('c', 1), ('b', 2), ('d', 4), ('a', 5)]
1.4reverse参数排序
>>> print sorted([1,2,3,6,5,4], reverse=True)
[6, 5, 4, 3, 2, 1]
>>> print sorted([1,2,3,6,5,4], reverse=False)
[1, 2, 3, 4, 5, 6]
3,zip与zip
zip() 函数用于将可迭代的对象(直观理解就是能用for循环进行迭代的对象就是可迭代对象。比如:字符串,列表,元祖,字典,集合等等,都是可迭代对象。)作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
a = [(1, 2), (2, 3), (3, 4)]
# 类似这些都是可以迭代的元祖,字符串等等。a = ((1, 2), (2, 3), (3, 4)), a="abc"
b = [(5, 6), (7, 8), (9, 9)]
print(zip(a, b)) # <zip object at 0x000001B5EB0CA0C8>
ret = list(zip(a, b))
# 输出 :[((1, 2), (5, 6)), ((2, 3), (7, 8)), ((3, 4), (9, 9))]
ret1 = list(zip(*ret))
# 或者写成这样:ret1 = list(zip(*(zip(z,b)))
# 输出: [((1, 2), (2, 3), (3, 4)), ((5, 6), (7, 8), (9, 9))]
4,items()用法
items()方法把字典中每对key和value组成一个元组,并把这些元组放在列表中返回。
D = {'Google': 'www.google.com', 'Runoob': 'www.runoob.com', 'taobao': 'www.taobao.com'}
print(D.items())
print(list(D.items()))
# 遍历字典列表
for key, value in D.items():
print(key, value)
# dict_items([('Google', 'www.google.com'), ('Runoob', 'www.runoob.com'), ('taobao', 'www.taobao.com')])
#结果: [('Google', 'www.google.com'), ('Runoob', 'www.runoob.com'), ('taobao', 'www.taobao.com')]
5,format()用法
print("{0} {1}".format("Hello","World"))
print("{1} {0}".format("Hello","World"))
print("{0} {1} {0}".format("Hello","World"))
print("{1} {1} {0}".format("Hello","World"))
Hello World
World Hello
Hello World Hello
World World Hello
二,opencv中的重要函数用法
1,二值操作
type:二值化操作的类型,以下五种:cv2.THRESH_BINARY;cv2.THRESH_BINARY_INV;cv2.THRESH_TRUNC;cv2.THREAH_TOZREO; cv2.THRESH_TOZERO_INV;
cv2.THRESH_BINARY:超过阈值部分取最大值,否则为0;例:127,255.比较每一个像素点,与127比较,小于的为0,大于的为255.越亮阈值越大
cv2.THRESH_BINARY_INV: THRESH_BINARY的反转
cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变。例:127 为阈值,所有大于127的为127,小于等于127不变。
cv2.THREAH_TOZREO:大于阈值部分不改变,否则为0
cv2.THRESH_TOZERO_INV:cv2.THRESH_TOZERO的反转
**
3,闭处理
#闭:先膨胀,再腐蚀
img = cv2.imread('dige.png')
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
cv_show('closing',closing)
3,礼帽
#礼帽=原始输入-开运算结果
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
cv_show('tophat',tophat)
4,cv2.getStructuringElement()框的形状处理
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))
这个函数的第一个参数表示内核的形状,有三种形状可以选择。
矩形:MORPH_RECT;
交叉形:MORPH_CROSS;
椭圆形:MORPH_ELLIPSE;
第二和第三个参数分别是内核的尺寸以及锚点的位置。一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得
getStructuringElement函数的返回值:
对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心点。element形状唯一依赖锚点位置,其他情况下,锚点只是影响了形态学运算结果的偏移。
5,轮廓检测cv2.findContours(img,mode,method)
cv2.findContours(img,mode,method)
mode:轮廓检索模式
RETR_EXTERNAL :只检索最外面的轮廓;
RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;
method:轮廓逼近方法
CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
6,模板匹配
#method:
(1)cv2.TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
(2)cv2.TM_CCORR:计算相关性,计算出来的值越大,越相关
(3)cv2.TM_CCOFFF:计算相关系数,计算出来的值越大,越相关
(4)cv2.TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
(5)cv2.TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
(6)cv2.TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
7,min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
min_val:算法算出的最小值
max_val:算法算出的最大值
min_loc:最小值所在的坐标(因为得到了h,w,所以可得到矩形)
max_loc :最大值所在的坐标
三,代码实现
# 导入工具包
from imutils import contours
import numpy as np
import argparse
import cv2
import myutils
# 【一、基础设置】
# 设置参数
ap = argparse.ArgumentParser()#可以理解成一个容器,将包含将命令行解析为Python数据类型所需的所有信息。
ap.add_argument("-i", "--image", default='./images/credit_card_01.png',
help="path to input image")
ap.add_argument("-t", "--template", default='./ocr_a_reference.png',
help="path to template OCR image")
args = vars(ap.parse_args())
# 指定信用卡类型
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
# 绘图展示
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 读取一个模板图像
img = cv2.imread(args["template"])
# cv_show('template',img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv_show('template_gray',ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
#该函数形成了黑底白字的图像
cv_show('template_bi', ref)
# 【二、模板处理流程】: 轮廓检测, 外接矩形, 抠出模板, 让模板对应每个数值
# 1.计算轮廓
'''
cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),
cv2.RETR_EXTERNAL只检测外轮廓,
cv2.CHAIN_APPROX_SIMPLE只 http://127.0.0.1:8888/?token=91f9f40d29f63f7d3e3303fea42ed280d4eeb7008242e771保留终点坐标
返回的list中每个元素都是图像中的一个轮廓
'''
refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
img = cv2.drawContours(img, refCnts, -1, (0, 0, 255), 2) # 轮廓在二值图上得到, 画是画在原图上,-1:画所有的轮廓。2:线条的宽度
cv_show('template_Contours', img)
print(np.array(refCnts).shape) # 10个轮廓,所以是10
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]#对轮廓进行排序
digits = {}
# 2.遍历每一个轮廓,外接矩形
for (i, c) in enumerate(refCnts): # c是每个轮廓的终点坐标
# 计算外接矩形并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c)
# 3.抠出模板
roi = ref[y:y + h, x:x + w] # 每个roi对应一个数字
# print(roi.shape)
roi = cv2.resize(roi, (57, 88)) # 太小,调大点。这就将每个数字抠出
# 4.每一个数字对应每一个模板,及i 刚好对应roi轮廓中的数值
digits[i] = roi
# cv2.imshow('roi_'+str(i),roi)
# cv2.waitKey(0)
# print(digits)
# 【三、输入图像处理】
#避免背景以及其他文字的干扰
# 形态学操作,礼帽+闭操作可以突出明亮区域,但并不是非得礼帽+闭操作
# 1.初始化卷积核,根据实际任务指定大小,不一定非要3x3
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
#第一个参数表示内核的形状。第二和第三个参数分别是内核的尺寸以及锚点的位置。
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
onekernel = np.ones((9, 9), np.uint8)
# 2.读取输入图像,预处理
image = cv2.imread(args["image"])
# cv_show('Input_img',image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# cv_show('Input_gray',gray)
# 3.礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
# cv_show('Input_tophat',tophat)
# 4.x方向的Sobel算子,实验表明,加y的效果的并不好
gradX = cv2.Sobel(tophat, cv2.CV_32F, 1, 0, ksize=3) # ksize=-1相当于用3*3的
gradX = np.absolute(gradX) # absolute: 计算绝对值
min_Val, max_val = np.min(gradX), np.max(gradX)
gradX = (255 * (gradX - min_Val) / (max_val - min_Val))
gradX = gradX.astype("uint8")
print(np.array(gradX).shape)
# cv_show('Input_Sobel_gradX',gradX)
# 5.通过闭操作(先膨胀,再腐蚀)将数字连在一起. 将来是4个数字的4个框膨胀成1个框,就腐蚀不掉了
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
# cv_show('Input_CLOSE_gradX',gradX)
# 6.THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show('Input_thresh',thresh)
# 7.再来一个闭操作,填补空洞
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
# cv_show('Input_thresh_CLOSE',thresh)
# 8.计算轮廓
threshCnts = cv2.findContours(thresh.copy(),
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[0]
cur_img = image.copy()
cv2.drawContours(cur_img, threshCnts, -1, (0, 0, 255), 2)
#将轮廓画入原始图像中,此时画出的是所有轮廓,所以要进行筛选
# cv_show('Input_Contours',cur_img)
# 【四、遍历轮廓和数字】
# 1.遍历轮廓
locs = [] # 存符合条件的轮廓
for i, c in enumerate(threshCnts):
# 计算矩形
x, y, w, h = cv2.boundingRect(c)
ar = w / float(h)
print(ar,w,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])#sorted 从小到大排列
# 2.遍历每一个轮廓中的数字
output = [] # 存正确的数字
for (i, (gx, gy, gw, gh)) in enumerate(locs): # 遍历每一组大轮廓(包含4个数字)
# initialize the list of group digits
groupOutput = []
# 根据坐标提取每一个组(4个值)
group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5] # 往外扩一点
cv_show('group_' + str(i), group)
# 2.1 预处理
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 二值化的group
# cv_show('group_'+str(i),group)
# 计算每一组的轮廓 这样就分成4个小轮廓了
digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
# 排序
digitCnts = myutils.sort_contours(digitCnts, method="left-to-right")[0]
# 2.2 计算并匹配每一组中的每一个数值
for c in digitCnts: # c表示每个小轮廓的终点坐标
z = 0
# 找到当前数值的轮廓,resize成合适的的大小
(x, y, w, h) = cv2.boundingRect(c) # 外接矩形
roi = group[y:y + h, x:x + w] # 在原图中取出小轮廓覆盖区域,即数字
roi = cv2.resize(roi, (57, 88))
# cv_show("roi_"+str(z),roi)
# 计算匹配得分: 0得分多少,1得分多少...
scores = [] # 单次循环中,scores存的是一个数值 匹配 10个模板数值的最大得分
# 在模板中计算每一个得分
# digits的digit正好是数值0,1,...,9;digitROI是每个数值的特征表示
for (digit, digitROI) in digits.items():
# 进行模板匹配, res是结果矩阵
res = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF) # 此时roi是X digitROI是0 依次是1,2.. 匹配10次,看模板最高得分多少
Max_score = cv2.minMaxLoc(res)[1] # 返回4个,取第二个最大值Maxscore
scores.append(Max_score) # 10个最大值
# print("scores:",scores)
# 得到最合适的数字
groupOutput.append(str(np.argmax(scores))) # 返回的是输入列表中最大值的位置
z = z + 1
# 2.3 画出来
cv2.rectangle(image, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 1) # 左上角,右下角
# 2.4 putText参数:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
cv2.putText(image, "".join(groupOutput), (gx, gy - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 2.5 得到结果
output.extend(groupOutput)
print("groupOutput:", groupOutput)
# cv2.imshow("Output_image_"+str(i), image)
# cv2.waitKey(0)
# 3.打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Output_image", image)
cv2.waitKey(0)