最近学习openCV,在网上找了个小项目,信用卡数字识别,这里做一下笔记,识别信用卡数字的一般处理流程为如下
处理模板
- 读取模板:读取模板图片,包含标准的数字图片,使用到cv2.imread
- 灰度处理:将彩色图片处理为灰度值,因为默认读入的图片为彩色图片,处理起来比较麻烦,所以我们需要把图片转换为二维的灰度图片使用到cv2.cvtColor(https://blog.csdn.net/xf8964/article/details/100187031)
- 二值处理:将灰度图片转换为像素只有0和255的值的图片,使用到的函数cv2.threshold
- 查找轮廓:在二值图片上查找图像的轮廓,这是为了方便获取图片的方框图
- 显示轮廓:将上面查找到的轮廓显示在原图片上,可以直观的看到效果
- 轮廓排序:因为我们的模板图片是从数字0到数字9依次排序的,所以,我们需要将得到的轮廓按照x坐标排序排序,其思路就是根据轮廓图找到方框图,根据方框图的x坐标来从左到右排序图片
- 得到数字和其图片数组的字典:我们得到排序的轮廓后,使用轮廓的位置信息在原图中取得图片的数组信息,这样我们就得到了一个字典,字典的key是数字,value是图片的数组,用于比较
输入图片处理
- 定义卷积核:其实就是定义一个矩阵,来对图片进行卷积运算cv2.getStructuringElement
- 读取图片
- 调整大小
- 灰度处理
- 突出明亮区域,对图片处理,突出轮廓
# 显示图片函数
def cv_show(name, img):
""""
use opencv show a image (picture)
"""
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyWindow(name)
def get_template(path, debug=True):
"""
处理模板图片,获得单个数字图片的字典
:param path: the path of template
:param debug: 是否显示识别过程中的中间图片,默认显示
:return: a dictionary, key is index number, value is image
"""
# open image
img = cv2.imread(path)
print(type(img))
print(img.shape)
if debug:
cv_show("img", img)
# 灰度处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
if debug:
cv_show("gray", gray)
# 二值处理
ret, binary = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY_INV)
if debug:
cv_show("binary", binary)
# 查找轮廓
ref_, contours, hierarchy = cv2.findContours(binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 显示轮廓
cv2.drawContours(img, contours, -1, (0, 0, 255), 3)
if debug:
cv_show("img", img)
# 轮廓排序
# print(binary.shape)
template = {}
contours = utils.sort_contours(contours, method="left-to-right")[0]
for (i, c) in enumerate(contours):
(x, y, w, h) = cv2.boundingRect(c)
roi = binary[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
template[i] = roi
# 返回轮廓字典
if debug:
for i in range(len(template)):
cv_show("template", template[i])
return template
def process_input_picture(path, debug=True):
# 定义卷积核 返回一个矩阵MORPH_RECT为矩形
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sq_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 读入图片
image = cv2.imread(path)
if debug:
cv_show("input", image)
# 调整大小
image = utils.resize(image, width=300)
# 灰度处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
if debug:
cv_show("gray", gray)
# 突出明亮区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rect_kernel)
if debug:
cv_show("tophat", tophat)
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=3)
gradX = cv2.convertScaleAbs(gradX)
if debug:
cv_show("gradX", gradX)
gradY = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=3)
gradY = cv2.convertScaleAbs(gradY)
if debug:
cv_show("gradY", gradY)
gradXY = cv2.addWeighted(gradX, 0.5, gradY, 0.5, 0)
if debug:
cv_show("gradXY", gradXY)
# # 闭:先膨胀,再腐蚀,将突出方块,我想这是为了方便二值处理
gradXY = cv2.morphologyEx(gradXY, cv2.MORPH_CLOSE, rect_kernel)
if debug:
cv_show("gradXY", gradXY)
# 二值处理,找到方框
thresh = cv2.threshold(gradXY, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
if debug:
cv_show('thresh', thresh)
# 再一次闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sq_kernel)
if debug:
cv_show("thresh", thresh)
# 查找组合数字的轮廓
thresh_, thresh_contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cur_img = image.copy()
cv2.drawContours(cur_img, thresh_contours, -1, (0, 0, 255), 2)
if debug:
cv_show("img", cur_img)
digits = []
# 获取组合数字快
for (i, c) in enumerate(thresh_contours):
# 计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
if ar > 2.5 and ar < 4.0:
if (w > 40 and w < 55) and (h > 10 and h < 20):
# 保留
digits.append((x, y, w, h))
digits = sorted(digits, key=lambda x: x[0])
output = []
for (i, (x, y, w, h)) in enumerate(digits):
group_output = []
# 根据坐标提取每一组,也就是一个快
group = gray[y-5:y+h+5, x-5:x+w+5]
if debug:
cv_show("group", group)
# 二值处理
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
if debug:
cv_show("group", group)
group_, digit_contours, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
digit_contours = contours.sort_contours(digit_contours, method="left-to-right")[0]
for c in digit_contours:
(x1, y1, w1, h1) = cv2.boundingRect(c)
roi = group[y1:y1 + h1, x1:x1 + w1]
roi = cv2.resize(roi, (57,88))
scores = []
#匹配模板中每一个数字
for (digit, digit_roi) in template.items():
result = cv2.matchTemplate(roi, digit_roi, cv2.TM_CCOEFF)
#print(result)
(_, score, _, _) = cv2.minMaxLoc(result)
#print(score)
scores.append(score)
#print(scores)
group_output.append(str(np.argmax(scores)))
print(group_output)
cv2.rectangle(image, (x-5,y-5), (x+w+5,y+h+5), (0,0,255),1)
# 像图片中输入文字
cv2.putText(image,"".join(group_output),( x, y-15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0,0,255), 2)
output.extend(group_output)
cv2.imshow("Image", image)
cv2.waitKey(0)
return digits
调用关系
if __name__ == '__main__':
print("hello word")
# 解析模板,返回模板的数字和图片快的字典
template = get_template(TEMPLATE_PATH, debug=False)
print("digits len is ", len(template))
digits = process_input_picture(CREDIT_CARD_PATH, debug=False)
print("digits len is ", len(digits))
# 处理预测图片