opencv实战--信用卡

一,基础函数用法

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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值