本文基于之前学习的透视变换、和OCR识别,做了个简单的小票识别,如下:
import cv2
import numpy as np
from PIL import Image
import pytesseract as tess
dsize = (55, 88) # 统一尺度
# 展示图像,封装成函数
def cv_show_image(name, img):
cv2.imshow(name, img)
cv2.waitKey(0) # 等待时间,单位是毫秒,0代表任意键终止
cv2.destroyAllWindows()
# =========================================================
# ================读取图像进行预处理=========================
# =========================================================
# 读取原始的彩色图像
ocr_img = cv2.imread('images/ocr_qr_code.PNG')
h_src, w_src, c_src = ocr_img.shape
# 进行灰度值和二值化转换
ocr_img_gray = cv2.cvtColor(ocr_img, cv2.COLOR_BGR2GRAY)
# cv_show_image('template_gray', template_gray)
# 高斯滤波
ocr_img_gray = cv2.GaussianBlur(ocr_img_gray, (3, 3), 1)
# 二值化
ret, ocr_img_thresh = cv2.threshold(ocr_img_gray, 200, 255, cv2.THRESH_BINARY)
cv_show_image('template_thresh', ocr_img_thresh)
# 找到所有的轮廓。只需要外轮廓
ocr_img_contours, hierarchy = cv2.findContours(ocr_img_thresh,
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# =========================================================
# ================找到最大面积的那个轮廓=========================
# =========================================================
# 找到最大面积的那个轮廓
draw_img = ocr_img.copy() # 阶段性测试查看使用
# 最后的参数可以控制找到前几个最大的。比如哈,0表示最大的,2表示前三大的。
cont_max = sorted(ocr_img_contours, key=cv2.contourArea, reverse=True)[0] # 按照面积来排序,找到最大的,倒序。
# 画出这个轮廓,红色线条
x, y, w, h = cv2.boundingRect(cont_max)
draw_img = cv2.drawContours(draw_img, [cont_max], -1, color=(0, 0, 255), thickness=2) # 画出这个轮廓,会在原图上画
arcLength = cv2.arcLength(cont_max, True) # 求最大的轮廓的周长
# 这个原始轮廓可能是很多歌点哈,但是我们只需要四个点的四边形的轮廓就行了。这里需要进行轮廓近似运算。
# 不断的尝试提升阈值,增大近似范围,减少边数目。
rate = 0.01
approx_max = None
while len(cont_max) != 4:
# epsilon是原始轮廓到近似轮廓的最大距离,也是近似的判断阈值。 closed 是表示是个封闭的轮廓
approx_max = cv2.approxPolyDP(cont_max, epsilon=rate * arcLength, closed=True)
if len(approx_max) == 4:
print("rate={}, epsilon={}".format(rate, rate * arcLength))
break
rate += 0.01
print("approx: ", approx_max)
# 画出这个轮廓,绿色线条
draw_img = cv2.drawContours(draw_img, [approx_max], -1, color=(0, 255, 0), thickness=2) # 画出这个轮廓,会在原图上画
cv_show_image('rectangle_contours_img', draw_img)
del draw_img
# =========================================================
# ================得到了四个顶点,进行透视变换=========================
# =========================================================
# 先排序这个四个顶点,按照((左上),(右上),(右下),(坐下))的顺序来定义
# 最终这四个点将转成((0,0), (w,0), (w,h), (h,w)) + 平移(左上)的形式。
def sort_dotCnt(kps):
rect = np.zeros((4, 2), dtype='float32')
s = kps.sum(axis=1)
# 找出左上和右下
rect[0] = kps[np.argmin(s)]
rect[2] = kps[np.argmax(s)]
# 找出右上和左下
diff = np.diff(kps, axis=1)
rect[1] = kps[np.argmin(diff)]
rect[3] = kps[np.argmax(diff)]
return rect
print(approx_max.shape)
print(approx_max.reshape(4, 2))
rect_ordered = sort_dotCnt(approx_max.reshape(4, 2))
(top_left, top_right, bottom_right, bottom_left) = rect_ordered
# 原始图像中物体的四个顶点的信息
pts_src = np.array([top_left, top_right, bottom_right, bottom_left], dtype="float32")
# 目标物体中的物体的四个顶点信息
pts_dst = np.array([(0 + top_left[0], 0 + top_left[1]),
(w + top_left[0], 0 + top_left[1]),
(w + top_left[0], h + top_left[1]),
(0 + top_left[0], h + top_left[1])], dtype="float32")
# 是一个3x3的矩阵,根据对应的两个点,计算出变换矩阵,由此将原始图像进行转换。
M = cv2.getPerspectiveTransform(pts_src, pts_dst)
# 基于单应性矩阵,将原始图像转换成目标图像
im_out = cv2.warpPerspective(ocr_img_thresh, M, (w_src, h_src))
cv_show_image('im_out', im_out)
# =========================================================
# ================识别其数字=========================
# =========================================================
textInImage = Image.fromarray(im_out)
text = tess.image_to_string(textInImage)
print("\nocr detect result:%s" % text)
原图经过预处理后:
经过轮廓检测后,得到一个四个顶点的轮廓用绿色线画出
经过透视变换得到:
最后用过OCR识别得到:
目前智能识别到数字,下一次,我将去看看学习下怎么识别简体汉字