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

本文介绍了使用Python进行小票识别的过程,包括图像预处理、轮廓检测、透视变换和OCR识别数字。通过灰度化、二值化、高斯滤波等步骤优化图像,然后找到并绘制最大轮廓,进行透视变换以校正图像,最后利用Tesseract进行文字识别。文章展示了每个步骤的图像结果,并计划进一步研究汉字识别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

<think>嗯,用户的问题是关于购物小票图像中复杂字符的分割,涉及到OCR预处理的技术方案。首先,我需要理解用户的具体需求。购物小票的字符可能比较密集,排版复杂,还有可能有背景干扰或者光照不均的问题。所以,用户可能遇到了分割不准确的情况,导致后续OCR识别效果差。 接下来,我需要回忆一下相关的技术方案。根据引用中的资料,传统的OCR流程包括图像预处理、二值化、字符分割等步骤。比如引用[3]提到了高斯滤波去噪二值化,引用[4]提到使用OpenCVtesseract-OCR。但用户的问题是复杂字符的分割,可能需要更精细的处理。 首先,预处理阶段。用户提到复杂字符,可能图像质量不高,所以去噪很重要。高斯滤波或者中值滤波可以去除噪声,但如果是椒盐噪声,中值滤波更好。然后,可能需要处理光照问题,比如引用[3]中的方法,或者使用自适应直方图均衡化。 然后是二值化,全局阈值可能在光照不均时效果不好,所以应该用自适应阈值,比如引用[3]提到的。或者大津法,但要看具体情况。二值化后的图像需要清晰区分字符背景。 接下来是文本区域定位。投影分析(水平垂直投影)可以找到文本行字符间距,这在引用[2]的传统方法中有提到。但复杂字符可能粘连,投影分析可能不够。可能需要连通域分析,但连通域如果字符粘连也会有问题。这时候可能需要形态学处理,比如膨胀腐蚀来分离字符,但需要调整核的大小。 另外,基于深度学习的实例分割方法可能更有效,比如Mask R-CNN,可以精确分割每个字符,尤其是复杂排版的情况。但用户可能希望传统方法结合深度学习?需要看用户的应用场景资源。 字符校正部分,如果图像倾斜,需要做旋转矫正,引用[4]中的文档扫描方法可能适用,比如使用霍夫变换找边缘,然后透视变换。 最后的后处理,比如字符间隙修复或合并过分割区域,可能需要一些规则或者二次处理。 用户提供的引用里,引用[4]提到了使用tesseract-OCR,但分割是预处理的关键。可能用户已经尝试了传统方法,但效果不好,所以需要更高级的技术。 所以,综合起来,技术方案应该是分步骤的预处理,结合传统图像处理深度学习的方法。比如先用传统方法去噪、二值化、矫正,然后用投影或连通域分析,如果不行再用深度学习模型进行分割。同时,可能需要针对小票的特殊结构进行调整,比如先定位表格线,再分割单元格内的字符。 另外,用户的问题可能涉及复杂字符,比如连笔、重叠、特殊字体等,这时候传统方法可能不够鲁棒,需要考虑更高级的分割算法。或者使用基于深度学习的端到端模型,但用户可能希望先分割再识别,所以需要优化分割步骤。 可能的优化点包括:多尺度处理应对不同大小的字符,形态学操作调整结构元素,或者结合文本检测模型(如EAST或CRAFT)先检测文本区域,再进行分割。 最后,生成相关问题时要考虑用户可能进一步的问题,比如如何处理粘连字符,或推荐工具库,或者评估分割效果的方法。</think>### 分步解决方案 针对购物小票图像中复杂字符的分割问题,需结合图像预处理、字符分割OCR优化技术,具体步骤如下: #### **1. 图像预处理** - **去噪与增强** 使用高斯滤波(如OpenCV的`cv2.GaussianBlur`)去除噪声,并通过直方图均衡化或自适应阈值(如`cv2.adaptiveThreshold`)增强对比度,改善低光照或反光区域的字符可见性[^3][^4]。 示例代码: ```python import cv2 img = cv2.imread('receipt.jpg', 0) # 灰度读取 blurred = cv2.GaussianBlur(img, (5,5), 0) # 高斯去噪 thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) ``` - **文本区域定位** 利用连通域分析(`cv2.connectedComponents`)或投影分析(水平/垂直投影)定位文本行,结合形态学操作(如膨胀、腐蚀)分离粘连字符[^2]。 #### **2. 复杂字符分割** - **基于深度学习的实例分割** 若传统方法无法处理复杂排版(如重叠字符、艺术字体),可采用深度学习模型(如Mask R-CNN或YOLOv8-seg)直接定位并分割字符区域。 - **多尺度滑动窗口** 对图像进行多尺度切分,结合分类模型判断子区域是否包含有效字符。 #### **3. OCR优化** - **字符校正** 通过霍夫变换检测倾斜文本,并旋转矫正(`cv2.getRotationMatrix2D` + `cv2.warpAffine`)。 - **后处理规则** 根据小票结构特点(如固定栏目名称、金额格式),设计正则表达式修复分割错误(如合并碎片字符)。 #### **4. 工具与框架推荐** - 传统图像处理:OpenCV + Tesseract OCR - 深度学习方案:PaddleOCR(支持端到端多语言识别)或MMOCR(提供预训练分割模型) --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值