文章目录
- 一、流程概述
- 二、环境配置与参数设置
- 三、命令行参数配置
- 四、银行卡类型定义
- 五、辅助函数定义
- 六、模板图像预处理
- 6.1 模板加载与初步处理
- 6.2 轮廓提取与数字模板建立
- 七、银行卡图像处理流程
- 7.1 图像加载与初步调整
- 7.2 形态学处理突出数字区域
- 7.3 数字区域定位与提取
- 7.4 数字组轮廓检测与筛选
- 八、数字识别核心算法
- 8.1 单个数字组处理
- 8.2 模板匹配识别数字
- 8.3 结果可视化
- 九、最终结果输出
一、流程概述
采用经典的图像处理流程,主要分为两个核心阶段:
- 模板预处理阶段:预先处理标准OCR-A字体模板,提取并存储每个数字的特征模板
- 卡号识别阶段:对输入的银行卡图像进行多步处理,定位卡号区域,分割单个数字,通过模板匹配实现识别
整个系统基于OpenCV库实现。
二、环境配置与参数设置
importnumpyasnpimportargparseimportcv2importmyutilsfromimutilsimportcontours模块作用分析:
numpy:提供高效的数值计算和数组操作argparse:Python内置库,用于解析命令行参数cv2:OpenCV库,核心图像处理功能myutils:自定义工具模块,包含轮廓排序等辅助函数imutils.contours:提供轮廓处理的便捷函数
三、命令行参数配置
# 设置参数ap=argparse.ArgumentParser()# 创建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())# 将参数转换为字典格式,便于访问# 使用示例:# python demo47.py -i card1.png -t kahao.png参数说明:
-i/--image:必需参数,指定待识别的银行卡图像路径-t/--template:必需参数,指定OCR-A模板图像路径
四、银行卡类型定义
# 根据卡号首位数字判断银行卡类型FIRST_NUMBER={"3":"American Express",# 美国运通卡"4":"Visa",# Visa卡"5":"MasterCard",# 万事达卡"6":"Discover Card"# 发现卡}这个字典基于银行卡号的行业标准:首位数字3代表美国运通,4代表Visa,5代表万事达,6代表发现卡。
五、辅助函数定义
defcv_show(name,img):# 图像显示函数"""显示图像并等待按键响应"""cv2.imshow(name,img)cv2.waitKey(0)这是一个简单的图像显示封装函数,便于在开发过程中可视化各个处理阶段的结果。
六、模板图像预处理
6.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)关键步骤解析:
- 灰度转换:将彩色图像转换为单通道灰度图,减少计算复杂度
- 二值化:使用阈值处理,将图像转换为纯黑白两色,便于轮廓提取
THRESH_BINARY_INV:反向二值化,生成黑底白字效果
6.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('refCnts',img)# 对轮廓从左到右排序refCnts=myutils.sort_contours(refCnts,method="left-to-right")[0]digits={}# 存储数字模板的字典# 提取每个数字作为模板for(i,c)inenumerate(refCnts):# 遍历每个数字轮廓# 计算轮廓的外接矩形(x,y,w,h)=cv2.boundingRect(c)# 提取数字区域并缩放到统一尺寸roi=ref[y:y+h,x:x+w]roi=cv2.resize(roi,(57,88))# 标准化尺寸cv_show('roi',roi)digits[i]=roi# 存储模板:键为数字,值为模板图像技术细节:
cv2.findContours():在二值图像中查找轮廓RETR_EXTERNAL:仅检测最外层轮廓CHAIN_APPROX_SIMPLE:压缩轮廓点,减少内存使用- 模板标准化:将所有数字缩放到57×88像素,确保后续匹配的准确性
七、银行卡图像处理流程
7.1 图像加载与初步调整
# 读取待识别图像image=cv2.imread(args["image"])cv_show('image',image)# 调整图像尺寸,保持宽高比image=myutils.resize(image,width=300)# 转换为灰度图像gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)cv_show('gray',gray)宽度统一调整为300像素,既保证处理速度,又维持足够的分辨率。
7.2 形态学处理突出数字区域
# 定义形态学操作的卷积核rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))# 顶帽操作:突出亮色细节,抑制背景tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)open_img=cv2.morphologyEx(gray,cv2.MORPH_OPEN,rectKernel)cv_show('open',open_img)cv_show('tophat',tophat)形态学操作原理:
- 顶帽操作:原始图像减去开运算结果,突出比背景亮的细小区域
- 开运算:先腐蚀后膨胀,消除小物体,平滑边界
- 银行卡数字通常比背景亮,顶帽操作能有效突出这些数字
7.3 数字区域定位与提取
# 第一次闭操作:连接相近的数字closeX=cv2.morphologyEx(tophat,cv2.MORPH_CLOSE,rectKernel)cv_show('close1',closeX)# 自适应阈值二值化thresh=cv2.threshold(closeX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]cv_show('thresh',thresh)# 第二次闭操作:进一步连接数字区域thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)cv_show('close2',thresh)闭操作的作用:通过先膨胀后腐蚀,连接相邻的白色区域(数字),将分散的数字点连接成完整的数字组。
7.4 数字组轮廓检测与筛选
# 检测轮廓_,cnts,_=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# 可视化所有轮廓cnts_img=image.copy()cv2.drawContours(cnts_img,cnts,-1,(0,0,255),3)cv_show('cnts_img',cnts_img)# 筛选符合条件的轮廓(银行卡号区域)locs=[]forcincnts:(x,y,w,h)=cv2.boundingRect(c)# 获取外接矩形ar=w/float(h)# 宽高比# 筛选条件:宽高比和尺寸范围if2.5<ar<4.0:# 银行卡号区域的典型宽高比if(40<w<55)and(10<h<20):# 尺寸范围locs.append((x,y,w,h))# 按x坐标排序(从左到右)locs=sorted(locs,key=lambdax:x[0])筛选逻辑:
- 宽高比2.5-4.0:匹配银行卡号区域的长条形特征
- 宽度40-55像素,高度10-20像素:匹配标准尺寸
八、数字识别核心算法
8.1 单个数字组处理
output=[]# 存储最终识别结果# 遍历每个数字组(通常一组4个数字)for(gx,gy,gw,gh)inlocs:groupOutput=[]# 存储当前组的识别结果# 提取数字组区域,添加5像素边界group=gray[gy-5:gy+gh+5,gx-5:gx+gw+5]cv_show('group',group)# 对数字组进行二值化group=cv2.threshold(group,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]cv_show('group',group)# 检测组内每个数字的轮廓_,digitCnts,_=cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)digitCnts=myutils.sort_contours(digitCnts,method="left-to-right")[0]8.2 模板匹配识别数字
# 识别组内每个数字forcindigitCnts:# 提取单个数字区域(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(digit,digitROI)indigits.items():# 使用相关系数匹配法result=cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)(_,score,_,_)=cv2.minMaxLoc(result)scores.append(score)# 选择最高得分的数字groupOutput.append(str(np.argmax(scores)))
模板匹配原理:
cv2.matchTemplate():在输入图像中搜索与模板匹配的区域TM_CCOEFF:相关系数匹配方法,对光照变化鲁棒np.argmax(scores):返回最高得分的索引,即识别出的数字
8.3 结果可视化
# 绘制数字组矩形框(红色)cv2.rectangle(image,(gx-5,gy-5),(gx+gw+5,gy+gh+5),(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)九、最终结果输出
# 输出银行卡类型和完整卡号print("Credit Card Type: {}".format(FIRST_NUMBER.get(output[0],"Unknown")))print("Credit Card #: {}".format("".join(output)))# 显示最终处理结果cv2.imshow("Image",image)cv2.waitKey(0)cv2.destroyAllWindows()