5.形态学变化
5.1腐蚀
cv2.erode(image,kernel,iterations)
- image:需要进行操作的图片对象
- kernel:领域的大小
- iterations:迭代的次数
腐蚀的原理很简单,就是用规定领域内像素的最小值代替当前的像素值,迭代次数就是腐蚀一边之后,是否还要腐蚀多次,腐蚀越多,图片的像素越小,图片也会越黑。
5.2膨胀
cv2.dilate(img,kernel,iterations)
- image:需要进行操作的图片对象
- kernel:领域的大小
- iterations:迭代的次数
膨胀的原理和腐蚀非常相似,只不过膨胀是用邻域内像素的最大值代替当前的像素值,其他的参数意义一样。
5.3开运算和闭运算、礼帽和黑帽
开运算与闭运算以及下面所要介绍的礼帽黑帽所用的API的是一样的,只不是是op参数不同。
cv2.morghologyEx(image,op,kernel)
- cv2.MORPH_OPEN:开运算,腐蚀—>膨胀
- cv2.MORPH_CLOSE:闭运算,膨胀—>腐蚀
- cv2.MORPH_TOPHAT:礼帽,开运算减原图 得到噪声图像
- cv2.MORPH_BLACKHAT:黑帽 闭运算减原图 得到图像内的孔洞
开运算:
含义:先腐蚀后膨胀,去除背景(黑)中的噪声
闭运算:
含义:先膨胀后腐蚀,去除背景(白)中的黑色孔洞
礼帽:
cv2.
含义:开运算与原图做差
黑帽:
cv2.
含义:闭运算与原图做差
6.项目实战
6.1读取图片转化为灰度图
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @Project :OpenCV识别银行卡数字 @File :6.1导入包读取图片.py @IDE :PyCharm @Author :咋 @Date :2023/1/14 19:54 """ import cv2 import numpy as np # 读取图片 image = cv2.imread("bank_card41.jpg") # 转化为灰度图 image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 调整图片大小 image = cv2.resize(image,(4*image.shape[1],4*image.shape[0])) cv2.imshow("image",image) cv2.waitKey(0) # 销毁窗口 cv2.destroyAllWindows()
6.2自适应阈值处理
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @Project :OpenCV识别银行卡数字 @File :6.2自适应阈值处理.py @IDE :PyCharm @Author :咋 @Date :2023/1/14 19:57 """ import cv2 import numpy as np # 读取图片 image = cv2.imread("bank_card4.jpg") # 转化为灰度图 image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 调整图片大小 image = cv2.resize(image,(640,480)) # 自适应阈值处理 threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,3) # 取反操作 thresh = cv2.bitwise_not(threshold_img) cv2.imshow("image",np.hstack((image,thresh))) cv2.waitKey(0) # 销毁窗口 cv2.destroyAllWindows()
6.3第一次寻找合适的轮廓
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @Project :OpenCV识别银行卡数字 @File :6.3寻找合适的轮廓.py @IDE :PyCharm @Author :咋 @Date :2023/1/14 19:59 """ import cv2 import numpy as np # 读取图片 image = cv2.imread("bank_card41.jpg") # 转化为灰度图 image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 调整图片大小 image = cv2.resize(image,(4*image.shape[1],4*image.shape[0])) # 自适应阈值处理 threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,3) # 取反操作 thresh = cv2.bitwise_not(threshold_img) # 寻找合适的轮廓 contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) print(contours) # 判断合适的轮廓 for i in range(len(contours)): # 根据轮廓的面积 if cv2.contourArea(contours[i]) < 60: thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1) cv2.imshow("image",np.vstack((image,thresh))) cv2.waitKey(0) # 销毁窗口 cv2.destroyAllWindows()
6.4黑帽+腐蚀操作
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @Project :OpenCV识别银行卡数字 @File :6.4黑帽+腐蚀py @IDE :PyCharm @Author :咋 @Date :2023/1/14 20:12 """ import cv2 import numpy as np # 读取图片 image = cv2.imread("bank_card41.jpg") # 转化为灰度图 image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 调整图片大小 image = cv2.resize(image,(4*image.shape[1],4*image.shape[0])) # 自适应阈值处理 threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,3) # 取反操作 thresh = cv2.bitwise_not(threshold_img) # 寻找合适的轮廓 contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) print(contours) # 判断合适的轮廓 for i in range(len(contours)): # 根据轮廓的面积 if cv2.contourArea(contours[i]) < 60: thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1) # 黑帽操作 kernel = np.ones((15,15),dtype=np.uint8) blackhat = cv2.morphologyEx(thresh, cv2.MORPH_BLACKHAT, kernel) # 腐蚀操作 kernel = np.ones((3,3),dtype=np.uint8) erosion = cv2.erode(blackhat,kernel,iterations = 1) cv2.imshow("image",np.vstack((image,erosion))) cv2.waitKey(0) # 销毁窗口 cv2.destroyAllWindows()
6.5再次寻找轮廓+膨胀操作
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @Project :OpenCV识别银行卡数字 @File :再次寻找轮廓+膨胀操作.py @IDE :PyCharm @Author :咋 @Date :2023/1/14 20:21 """ import cv2 import numpy as np # 读取图片 image = cv2.imread("bank_card41.jpg") # 转化为灰度图 image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 调整图片大小 image = cv2.resize(image,(4*image.shape[1],4*image.shape[0])) # 自适应阈值处理 threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,9,3) # 取反操作 thresh = cv2.bitwise_not(threshold_img) # 寻找合适的轮廓 contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) print(contours) # 判断合适的轮廓 for i in range(len(contours)): # 根据轮廓的面积 if cv2.contourArea(contours[i]) < 60: thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1) # 黑帽操作 kernel = np.ones((15,15),dtype=np.uint8) blackhat = cv2.morphologyEx(thresh, cv2.MORPH_BLACKHAT, kernel) # 腐蚀操作 kernel = np.ones((3,3),dtype=np.uint8) erosion = cv2.erode(blackhat,kernel,iterations = 1) contours, hierarchy = cv2.findContours(erosion,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) for i in range(len(contours)): x,y,w,h = cv2.boundingRect(contours[i]) aspect_ratio = float(w)/h A = float(w)*h if A < 1800: erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1) else: if aspect_ratio > 0.6 or aspect_ratio < 0.57: erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1) cv2.imshow('erosion2',erosion) kernel = np.ones((7,7),dtype=np.uint8) dilation = cv2.dilate(erosion,kernel,iterations = 1) # cv2.imshow('dilation',) cv2.imshow("image",np.vstack((image,dilation))) cv2.waitKey(0) # 销毁窗口 cv2.destroyAllWindows()
6.6 模板匹配
numTemplate = cv2.imread('bankCardNumTemplate.jpg') numTemplate_GRAY = cv2.cvtColor(numTemplate, cv2.COLOR_BGR2GRAY) ret, numTemplate_GRAY = cv2.threshold(numTemplate_GRAY, 200, 255, cv2.THRESH_BINARY) # cv2.imshow('thresh',thresh) # cv2.imshow('numTemplate_GRAY', numTemplate_GRAY) contours, hierarchy = cv2.findContours(numTemplate_GRAY, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # print(np.size(contours)) def sequence_contours(dst_Binary, method, Rect_width, Rect_height): if method == "Left_to_Right" or method == 2: contours, hierarchy = cv2.findContours(dst_Binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) n = np.size(contours) RectBoxes0 = np.ones((n, 4), dtype=int) # print(n) for i in range(0, n): RectBoxes0[i] = cv2.boundingRect(contours[i]) # print(RectBoxes0) RectBoxes = np.ones((n, 4), dtype=int) for i in range(0, n): sequence = 0 for j in range(0, n): if RectBoxes0[i][0] > RectBoxes0[j][0]: sequence = sequence + 1 RectBoxes[sequence] = RectBoxes0[i] RectImgBoxes = [[] for i in range(n)] for i in range(0, n): img = dst_Binary[RectBoxes[i, 1]:(RectBoxes[i, 1] + RectBoxes[i, 3]), RectBoxes[i, 0]:(RectBoxes[i, 0] + RectBoxes[i, 2])] # cv2.imshow('number'+ str(i), img) img = cv2.resize(img, (Rect_width, Rect_height)) ret, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY) RectImgBoxes[i] = img # print(RectBoxes) # print(np.size(RectImgBoxes)) return RectBoxes, RectImgBoxes RectBoxes_Temp, RectImgBoxes_Temp = sequence_contours(numTemplate_GRAY, method="Left_to_Right", Rect_width=50, Rect_height=80) # print(RectBoxes) # cv2.imshow('numberTemp', RectImgBoxes_Temp[3]) RectBoxes, RectImgBoxes = sequence_contours(dilation, method="Left_to_Right", Rect_width=50, Rect_height=80) # cv2.imshow('numberfin', RectImgBoxes[3]) # print(len(RectImgBoxes)) result = [] for i in range(len(RectImgBoxes)): score = np.zeros(len(RectImgBoxes_Temp), dtype=int) for j in range(len(RectImgBoxes_Temp)): score[j] = cv2.matchTemplate(RectImgBoxes[i], RectImgBoxes_Temp[j], cv2.TM_CCOEFF) # print(score) min_val, max_val, min_indx, max_indx = cv2.minMaxLoc(score) result.append(max_indx[1]) # 定义添加数字到图片的函数 def add_num(image,num_list): text = "" for i in num_list: text += str(i) font = ImageFont.truetype("msyh.ttc",50) # 创建一个pillow的图片 pil_img = Image.fromarray(image) # 绘制图片 draw = ImageDraw.Draw(pil_img) # 利用draw去绘制中文 draw.text((0, 0), text , font=font, fill=0) # 后面的fill即颜色,RGBA # 重新变为ndarray image = np.array(pil_img) return image print(result) image = add_num(image,result) cv2.imshow("image",image) # cv2.imshow("image",np.vstack((image,dilation))) cv2.waitKey(0)
7.完整代码
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @Project :OpenCV识别银行卡数字 @File :模板匹配.py @IDE :PyCharm @Author :咋 @Date :2023/1/14 20:29 """ import cv2 import numpy as np from PIL import ImageFont,ImageDraw,Image # 读取图片 image = cv2.imread("bank_card41.jpg") true_img = cv2.imread("bank_card4.jpg") # 转化为灰度图 image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 调整图片大小 image = cv2.resize(image,(4*image.shape[1],4*image.shape[0])) # 自适应阈值处理 threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,9,3) # 取反操作 thresh = cv2.bitwise_not(threshold_img) # 寻找合适的轮廓 contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # print(contours) # 判断合适的轮廓 for i in range(len(contours)): # 根据轮廓的面积 if cv2.contourArea(contours[i]) < 60: thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1) # 黑帽操作 kernel = np.ones((15,15),dtype=np.uint8) blackhat = cv2.morphologyEx(thresh, cv2.MORPH_BLACKHAT, kernel) # 腐蚀操作 kernel = np.ones((3,3),dtype=np.uint8) erosion = cv2.erode(blackhat,kernel,iterations = 1) contours, hierarchy = cv2.findContours(erosion,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) for i in range(len(contours)): x,y,w,h = cv2.boundingRect(contours[i]) aspect_ratio = float(w)/h A = float(w)*h if A < 1800: erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1) else: if aspect_ratio > 0.6 or aspect_ratio < 0.57: erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1) # cv2.imshow('erosion2',erosion) kernel = np.ones((7,7),dtype=np.uint8) dilation = cv2.dilate(erosion,kernel,iterations = 1) # 模板匹配 numTemplate = cv2.imread('bankCardNumTemplate.jpg') numTemplate_GRAY = cv2.cvtColor(numTemplate, cv2.COLOR_BGR2GRAY) ret, numTemplate_GRAY = cv2.threshold(numTemplate_GRAY, 200, 255, cv2.THRESH_BINARY) # cv2.imshow('thresh',thresh) # cv2.imshow('numTemplate_GRAY', numTemplate_GRAY) contours, hierarchy = cv2.findContours(numTemplate_GRAY, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # print(np.size(contours)) def sequence_contours(dst_Binary, method, Rect_width, Rect_height): if method == "Left_to_Right" or method == 2: contours, hierarchy = cv2.findContours(dst_Binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) n = np.size(contours) RectBoxes0 = np.ones((n, 4), dtype=int) # print(n) for i in range(0, n): RectBoxes0[i] = cv2.boundingRect(contours[i]) # print(RectBoxes0) RectBoxes = np.ones((n, 4), dtype=int) for i in range(0, n): sequence = 0 for j in range(0, n): if RectBoxes0[i][0] > RectBoxes0[j][0]: sequence = sequence + 1 RectBoxes[sequence] = RectBoxes0[i] RectImgBoxes = [[] for i in range(n)] for i in range(0, n): img = dst_Binary[RectBoxes[i, 1]:(RectBoxes[i, 1] + RectBoxes[i, 3]), RectBoxes[i, 0]:(RectBoxes[i, 0] + RectBoxes[i, 2])] # cv2.imshow('number'+ str(i), img) img = cv2.resize(img, (Rect_width, Rect_height)) ret, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY) RectImgBoxes[i] = img # print(RectBoxes) # print(np.size(RectImgBoxes)) return RectBoxes, RectImgBoxes RectBoxes_Temp, RectImgBoxes_Temp = sequence_contours(numTemplate_GRAY, method="Left_to_Right", Rect_width=50, Rect_height=80) # print(RectBoxes) # cv2.imshow('numberTemp', RectImgBoxes_Temp[3]) RectBoxes, RectImgBoxes = sequence_contours(dilation, method="Left_to_Right", Rect_width=50, Rect_height=80) # cv2.imshow('numberfin', RectImgBoxes[3]) # print(len(RectImgBoxes)) result = [] for i in range(len(RectImgBoxes)): score = np.zeros(len(RectImgBoxes_Temp), dtype=int) for j in range(len(RectImgBoxes_Temp)): score[j] = cv2.matchTemplate(RectImgBoxes[i], RectImgBoxes_Temp[j], cv2.TM_CCOEFF) # print(score) min_val, max_val, min_indx, max_indx = cv2.minMaxLoc(score) result.append(max_indx[1]) # 定义添加数字到图片的函数 def add_num(image,num_list): text = "" for i in num_list: text += str(i) font = ImageFont.truetype("msyh.ttc",40) # 创建一个pillow的图片 pil_img = Image.fromarray(image) # 绘制图片 draw = ImageDraw.Draw(pil_img) # 利用draw去绘制中文 draw.text((15, 80), text , font=font, fill=0) # 后面的fill即颜色,RGBA # 重新变为ndarray image = np.array(pil_img) return image print(result) true_img = add_num(true_img,result) cv2.imshow("image",true_img) # cv2.imshow("image",np.vstack((image,dilation))) cv2.waitKey(0)
附上图片和代码链接:欢迎需要的小伙伴自取链接https://pan.baidu.com/s/1UeXrFjLXn3LWiDoUudW13w?pwd=3hye
提取码:3hye
8.总结
识别银行卡可算是Opencv的经典项目了,尤其是模板匹配,算得上是计算机视觉的精髓所在了。通过这个案例对二值化、轮廓检测、形态学变化腐蚀膨胀、开运算闭运算、礼帽黑帽有所了解,并能够组合在一起运算,算得上有所进步。
以此类推,模板匹配可以应用在很多项目中,关键的难点就在于怎么把图片和模板调整好,通过上述的方法以及参数的调整,可以达到这样的效果,还是需要多练习,才能真正掌握Opencv’的精髓。