【opencv学习】基于透视变换和OCR识别的小票识别

本文基于之前学习的透视变换、和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识别得到:
请添加图片描述
目前智能识别到数字,下一次,我将去看看学习下怎么识别简体汉字

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>