湖北省建设厅网站杨凯,在线p图网页版免费,百度网盘搜索引擎网站,深圳市外贸网站1 概述
1.1 opencv介绍
OpenCV是Open Source Computer Vision Library#xff08;开源计算机视觉库#xff09;的简称#xff0c;由Intel公司在1999年提出建立#xff0c;现在由Willow Garage提供运行支持#xff0c;它是一个高度开源发行的计算机视觉库#xff0c;可以…1 概述
1.1 opencv介绍
OpenCV是Open Source Computer Vision Library开源计算机视觉库的简称由Intel公司在1999年提出建立现在由Willow Garage提供运行支持它是一个高度开源发行的计算机视觉库可以实现Windows、Linux、Mac等多平台的跨平台操作。opencv是一个用于图像处理、分析、机器视觉方面的开源函数库已经成为学习计算机视觉强大的工具。在入侵检测、特定目标跟踪、目标检测、人脸检测、人脸识别、人脸跟踪等领域opencv可谓大显身手。在这篇文章中主要使用opencv进行银行卡号识别。
1.2 银行卡号识别步骤
银行卡号的识别过程主要包含读入图片的基本图像操作用模板去匹配处理后的银行卡最终识别出银行卡的卡号。所涉及的图像操作包括灰度转换、二值转换、阈值分割、轮廓检测、礼帽操作、梯度运算、闭操作、模板匹配。
1.2.1 预处理模板图像
首先需要将模板里的数字单独切出来然后把银行卡上的数字也单独切出来最后对银行卡的数字一个一个对比模板(0-9,10个数字。
原始图像如下 存储路径为:../data/card_template.jpg
假设把模板的每个数字切成矩形可以先对每个数字求外轮廓然后根据轮廓可得外接矩形便可切出其中对于外轮廓处理需传入二值图。于是步骤如下
读入图像模板
template cv2.imread(../data/card_template.jpg)
ShowImage(template, template) 转化为灰度图
# 将图像转化为灰度图
image_Gray cv2.cvtColor(template, cv2.COLOR_RGB2GRAY)
ShowImage(gray, image_Gray) 转化为二值图
# 转换为二值化图像,[1]表示返回二值化图像[0]表示返回阈值177
image_Binary cv2.threshold(image_Gray, 177, 255, cv2.THRESH_BINARY_INV)[1]
ShowImage(binary, image_Binary) 画出0-9这10个数字的外轮廓
# 提取轮廓
refcnts, his cv2.findContours(image_Binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(template, refcnts, -1, (0,0,255), 2)
ShowImage(contour, template) 计算外接矩形并且resize成合适大小
# 遍历每一个轮廓
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 1.2.2 预处理银行卡图像
对于银行卡图像需要过滤掉背景保留主要信息(下文1-6步)。上文模板是按矩形切出来的那么卡号也按矩形切割便于匹配。银行卡卡号位置是四位一组可以先处理一组再对每一组的每一个数字切割进行模板匹配。其中可以通过长宽比过滤掉银行卡上不是卡号的其他信息。 银行卡图片存储路径“../data/credit03.jpg”
读入需识别的银行卡并化为灰度图
# 读取图像进行预处理
image cv2.imread(../data/credit03.jpg)
ShowImage(card, image)
显示结果如下 image resize(image, width300)
# 将图像转化为灰度图
gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ShowImage(card_gray, gray)
显示结果如下 礼帽操作礼帽操作可以突出更明亮的区域原始输入-开运算先腐蚀再膨胀
# 通过顶帽操作突出更明亮的区域
tophat cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
ShowImage(tophat_card, tophat) 显示结果如下 梯度运算Sobel算子边缘检测可计算出轮廓
gradx cv2.Sobel(tophat, ddepthcv2.CV_32F, dx1, dy0, ksize-1)
grady cv2.Sobel(tophat, ddepthcv2.CV_32F, dx0, dy1, ksize-1)
gradx np.absolute(gradx)
minVal np.min(gradx)
maxVal np.max(gradx)
# (minVal, maxVal) (np.min(gradx), np.max(gradx))
# 保证值的范围在0-255之间
gradx (255 * ((gradx - minVal) / (maxVal - minVal)))
gradx gradx.astype(uint8)print(np.array(gradx).shape)
ShowImage(gradx_card, gradx)
显示结果如下 闭操作通过闭操作先膨胀再腐蚀将数字连在一起,便于后面求矩形框
# 通过闭操作先膨胀后腐蚀将数字连接在一块
gradx cv2.morphologyEx(gradx, cv2.MORPH_CLOSE,rectKernel)
ShowImage(gradx_card, gradx)
显示结果如下 阈值分割利用阈值对图片进行二值化处理,聚焦处理部分
# THRESH_OTSU会自动寻找合适的阈值适合双峰需要把阈值设置为0
thresh cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
ShowImage(thresh_card, thresh)
显示结果如下 再进行闭操作把图中连接的数字填饱满一点
# 再来一个闭合操作填充白框内的黑色区域
thresh cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
ShowImage(thresh2_card, thresh)
显示结果如下 计算外轮廓经过上文一系列操作对银行卡中是数字的地方有了清晰的候选同处理模板对象一样把可能是数字的地方通过外轮廓把全部矩形框画出来。后续再做筛选即可。
# 计算轮廓
threshCnts, his cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts threshCnts
cur_img image.copy()
cv2.drawContours(cur_img, cnts, -1, (0,0,255), 2)
ShowImage(contour_card, cur_img)
显示结果如下 计算外接矩形并且筛选符合的矩形框
locs []
# 遍历轮廓
for (i, c) in enumerate(cnts): # 函数用于遍历序列中的元素以及它们的下标# 计算矩形(x, y, w, h) cv2.boundingRect(c)ar w/float(h)# 选择合适的区域根据实际任务来这里是四个数字为一组if ar 2.5 and ar 5.0:if (w 40 and w 85) and (h 10 and h 20):# 把符合的留下locs.append((x,y,w,h))# 将符合的轮廓根据x的值从左到右排序
locs sorted(locs, keylambda x: x[0])
对每一个矩形框进行单独处理
output []
# 遍历轮廓中的每一个数字
for (i,(gx, gy, gw, gh)) in enumerate(locs):# 初始化链表groupOutput []# 根据坐标提取每一个组往外多取一点要不然看不清楚group gray[gy-5:gygh5,gx-5:gxgw5]ShowImage(group, group)# 预处理group cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 二值化ShowImage(group, group)# 找到每一组的轮廓digitCnts, his cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# digitCnts sortContours(digitCnts, methodLefttoRight)[0]# 对找到的轮廓进行排序digitCnts sort_contours(digitCnts, methodleft-to-right)[0]
1.2.3 模板匹配计算得分
# 计算每一组中的每一个数值for c in digitCnts:# 找到当前数值的轮廓resize成合适的大小(x,y,w,h) cv2.boundingRect(c)roi group[y:yh, x:xw]roi cv2.resize(roi, (57,88))ShowImage(roi, roi)scores []for(digit, digitROI) in digits.items():# 模板匹配#result cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) cv2.minMaxLoc(result)scores.append(score)# 得到最合适的数字groupOutput.append(str(np.argmax(scores)))
1.2.4 绘制结果
# 画矩形和字体cv2.rectangle(image, (gx - 5, gy - 5), (gxgw5, gygh5), (0,0,255),1)cv2.putText(image, .join(groupOutput), (gx, gy-15), cv2.FONT_HERSHEY_SIMPLEX,0.65, (0,0,255),2)# 得到结果output.extend(groupOutput)
2 银行卡号识别完整代码
import cv2
import numpy as npdef ShowImage(name, image):cv2.imshow(name, image)cv2.waitKey(0) # 等待时间0表示任意键退出cv2.destroyAllWindows()def sort_contours(cnts, methodleft-to-right):# reverse False 表示升序若不指定reverse则默认升序reverse Falsei 0if method right-to-left or method bottom-to-top:reverse True # reverse True 表示降序if method top-to-bottom or method bottom-to-top:i 1# 用一个最小的矩形把找到的形状包起来用x,y,h,w表示boundingBoxes [cv2.boundingRect(c) for c in cnts]# zip函数用于打包可迭代数据得到最终输出的cnts和boundingBoxes(cnts, boundingBoxes) zip(*sorted(zip(cnts, boundingBoxes),keylambda b: b[1][i], reversereverse))return cnts, boundingBoxesdef resize(image, widthNone, heightNone, intercv2.INTER_AREA):dim None(h, w) image.shape[:2] # 获取图像的高度和宽度if width is None and height is None:return imageif 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, interpolationinter) # 使用cv库的resize函数return resizedtemplate cv2.imread(../data/card_template.jpg)
ShowImage(template, template)# 将图像转化为灰度图
image_Gray cv2.cvtColor(template, cv2.COLOR_RGB2GRAY)
ShowImage(gray, image_Gray)# 转换为二值化图像,[1]表示返回二值化图像[0]表示返回阈值177
image_Binary cv2.threshold(image_Gray, 177, 255, cv2.THRESH_BINARY_INV)[1]
ShowImage(binary, image_Binary)# 提取轮廓
refcnts, his cv2.findContours(image_Binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(template, refcnts, -1, (0, 0, 255), 2)
ShowImage(contour, template)refcnts sort_contours(refcnts, methodleft-to-right)[0]
digits {}# 遍历每个轮廓
for (i, c) in enumerate(refcnts): # enumerate函数用于遍历序列中的元素以及它们的下标(x, y, w, h) cv2.boundingRect(c)roi image_Binary[y:yh, x:xw]roi cv2.resize(roi, (57, 88))digits[i] roi# 初始化卷积核
rectKernel cv2.getStructuringElement(cv2.MORPH_RECT, (9,3))
sqKernel cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))# 读取图像进行预处理
image cv2.imread(../data/credit03.jpg)
ShowImage(card, image)image resize(image, width300)
# 将图像转化为灰度图
gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ShowImage(card_gray, gray)# 通过顶帽操作突出更明亮的区域
tophat cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
ShowImage(tophat_card, tophat)gradx cv2.Sobel(tophat, ddepthcv2.CV_32F, dx1, dy0, ksize-1)
grady cv2.Sobel(tophat, ddepthcv2.CV_32F, dx0, dy1, ksize-1)
gradx np.absolute(gradx)
minVal np.min(gradx)
maxVal np.max(gradx)
# (minVal, maxVal) (np.min(gradx), np.max(gradx))
# 保证值的范围在0-255之间
gradx (255 * ((gradx - minVal) / (maxVal - minVal)))
gradx gradx.astype(uint8)print(np.array(gradx).shape)
ShowImage(gradx_card, gradx)# 通过闭操作先膨胀后腐蚀将数字连接在一块
gradx cv2.morphologyEx(gradx, cv2.MORPH_CLOSE,rectKernel)
ShowImage(gradx_card, gradx)# THRESH_OTSU会自动寻找合适的阈值适合双峰需要把阈值设置为0
thresh cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
ShowImage(thresh_card, thresh)# 再来一个闭合操作填充白框内的黑色区域
thresh cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
ShowImage(thresh2_card, thresh)# 计算轮廓
threshCnts, his cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts threshCnts
cur_img image.copy()
cv2.drawContours(cur_img, cnts, -1, (0,0,255), 2)
ShowImage(contour_card, cur_img)locs []
# 遍历轮廓
for (i, c) in enumerate(cnts): # 函数用于遍历序列中的元素以及它们的下标# 计算矩形(x, y, w, h) cv2.boundingRect(c)ar w/float(h)# 选择合适的区域根据实际任务来这里是四个数字为一组if ar 2.5 and ar 5.0:if (w 40 and w 85) and (h 10 and h 20):# 把符合的留下locs.append((x,y,w,h))# 将符合的轮廓根据x的值从左到右排序
locs sorted(locs, keylambda x: x[0])output []
# 遍历轮廓中的每一个数字
for (i,(gx, gy, gw, gh)) in enumerate(locs):# 初始化链表groupOutput []# 根据坐标提取每一个组往外多取一点要不然看不清楚group gray[gy-5:gygh5,gx-5:gxgw5]ShowImage(group, group)# 预处理group cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 二值化ShowImage(group, group)# 找到每一组的轮廓digitCnts, his cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# digitCnts sortContours(digitCnts, methodLefttoRight)[0]# 对找到的轮廓进行排序digitCnts sort_contours(digitCnts, methodleft-to-right)[0]# 计算每一组中的每一个数值for c in digitCnts:# 找到当前数值的轮廓resize成合适的大小(x,y,w,h) cv2.boundingRect(c)roi group[y:yh, x:xw]roi cv2.resize(roi, (57,88))ShowImage(roi, roi)scores []for(digit, digitROI) in digits.items():# 模板匹配#result cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) cv2.minMaxLoc(result)scores.append(score)# 得到最合适的数字groupOutput.append(str(np.argmax(scores)))# 画矩形和字体cv2.rectangle(image, (gx - 5, gy - 5), (gxgw5, gygh5), (0,0,255),1)cv2.putText(image, .join(groupOutput), (gx, gy-15), cv2.FONT_HERSHEY_SIMPLEX,0.65, (0,0,255),2)# 得到结果output.extend(groupOutput)ShowImage(card_result, image)
运行结果