
简介基于 OpenCV 的票据 OCR 识别项目适用于计算机相关专业在校学生、教师或企业员工可用于毕业设计、课程设计、大作业及初期项目演示。代码按模块划分图片数据存放于 /OCR_pic图片预处理与 OCR 识别测试位于 /ocrFile核心识别引擎集中在 /lib并附带 /lib/demoPy27/requirements.txt便于配置 OpenCV3、Tesseract、TensorFlow 运行环境。资源包共 143 个文件以 83 个 Python 脚本为主辅以 XML 配置、PNG 样例图片、PDF 说明文档、模型 checkpoint 及 Cython/CUDA 加速源码整体约 105.23MB。目前已有 402 人学习。项目已经过运行测试除可作为毕设框架直接使用外还可基于现有代码扩展不同票据识别场景是理解图像预处理、OCR 引擎与深度学习模型结合的较好范例。1. 从手工录入到自动识别票据 OCR 的落地场景每到月底财务科的工位上总有几个实习生对着发票一张张敲键盘。票据上的流水号、价税合计、发票代码每个字段都要进系统一天几百张的录入量错一个数字就要返回去翻纸票。把这件事自动化正是 OCROptical Character Recognition技术最典型的业务场景。Opencv 负责把照片里的票据找出来、摆正、去噪OCR 引擎再把文字从图像中提取成结构化字段。这套基于 Opencv 的票据 OCR 识别方案在毕业设计中既有视觉模块又有文本模块工作量饱满、难点清晰是 AI 应用方向常见的选题。下面就把 Python 源码的关键部分拆开讲从环境搭建讲到打包交付。2. 环境搭建与引擎选型把 OpenCV 4.5.2 和 OCR 工具链装通2.1 为什么优先选 OpenCV 4.xCode 128 条码解析带来便利在票据场景里发票代码、运单号这类字段很多时候不是普通文字而是 Code 128 条码。做过票据识别的工程师应该都有体会文字识别倒是好办条码解析如果单独引第三方库会增加部署体积和跨平台问题。打开 Opencv 4.5.2 的说明文档时可以发现原生已经支持 Code 128 条码检测BarcodeDetector 可以对图像中的一维码直接解码。这意味着在 OpenCV 内部就把条码字段解决掉不需要再额外安装 pyzbar 或 zxing 依赖对毕业设计的工期控制很有利。选引擎时的另一个关键点是识别精度尤其是中文场景。下面这个表是我的常用选型参考Tesseract、PaddleOCR、EasyOCR 三个都用过之后结论比较明确方案适用场景主要缺点OpenCV 4.x PaddleOCR中文票据、复杂背景模型文件体积偏大OpenCV 4.x Tesseract英文或印刷体清晰的票中文识别容易丢字OpenCV 3.x 传统图像处理无条码、版面固定泛化能力差我一般会优先选 PaddleOCR它离线的部署方式和自带的版面识别对票据更友好后面章节里的源码也按这个组合来写。2.2 用 pip 一次装齐 Python、OpenCV、PaddleOCR 依赖我的环境是 64 位 Windows / Ubuntu 20.04Python 3.8 到 3.10 都验证过。安装步骤固定是先装 OpenCV再装 PaddleOCR命令行如下pip install opencv-python4.5.2.52 pip install paddlepaddle2.4.2 pip install paddleocr2.6.1.3第一个命令装的是带 GUI 模块的标准版 OpenCV不装 headless 版本因为后面调试时要用 imshow 看处理结果。PaddleOCR 的版本没必要追新2.6.1.3 的接口和模型路径都比较稳定网上大量源码示例也是基于这个版本写的遇到报错容易搜到解决方案。装完之后不要急着写业务代码先做一次环境自检import cv2 import paddleocr print(OpenCV version:, cv2.__version__) print(PaddleOCR version:, paddleocr.__version__)这段代码的逻辑很简单如果 cv2 打印出 4.5.2 就说明核心库就绪导入 paddleocr 不报错才算真正装好。这里最容易翻车的点不是某个库装不上而是 Python 环境混乱pip 装到了一个虚拟环境IDE 用的是另一个解释器。我一般会先用where pythonWindows或which pythonLinux确认解释器位置再执行 pip install避免把包装错地方。提示如果你的电脑配置比较老跑不动 PaddleOCR 的深度学习模型可以退一步用 Tesseract 5.3.0 的 Windows 安装包印刷清晰的票据也够用只是中文准确率会明显差一截。3. 预处理三步把彩色票据变成 OCR 友好的干净图像票据来源要么是扫描仪要么是手机拍照图像质量差别很大。拍照件有透视畸变和反光扫描件大多只有偏斜和噪点。Opencv 在预处理阶段的全部工作就是把图像变成 OCR 引擎喜欢的样子前景文字和背景表线的对比度足够高、文字行水平或接近水平、尺寸不超过模型输入上限。3.1 灰度化与滤波控制噪点和表格线的干扰先做灰度化和高斯滤波这是 Opencv 图像处理里最常见的起步操作import cv2 import numpy as np img cv2.imread(ticket.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred cv2.GaussianBlur(gray, (5, 5), 0)cvtColor 把三通道图像压缩成单通道减少计算量并让后续二值化只面对一个通道。GaussianBlur 第二个参数是核大小按 (width, height) 传入5×5 对于 300 DPI 的票据图足够压掉传感器噪点核太大会把细小的金额数字模糊掉太小又压不住噪点。第三个参数 0 表示标准差由核大小自动推算一般不需要手动指定。滤波核大小的选择直接影响后续轮廓检测的稳定性我常用的参数范围如下参数常用值调整方向高斯核(5, 5)图模糊则减小到 (3, 3)二值化方式OTSU光照不均则改自适应阈值轮廓面积阈值min(原图像素/2000, 5000)票据占比小则调低如果扫描件本身足够清晰我一般会跳过滤波直接二值化减少一次参数调节。滤波的目的是压噪不是美图加太多反而会让字符边缘粘连。这一步的判断标准其实就一条放大到 200% 看字符边缘是不是干净利落。3.2 二值化与轮廓定位用 rect 和 cols/rows 找对票据边界预处理的核心环节是二值化和轮廓定位。OTSU 自动阈值在票据这类背景相对单调的图像上表现稳定_, binary cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)threshold 返回的第一个值是实际使用的阈值不一定等于 127因为 OTSU 会按灰度直方图自动计算最佳分割点第二个返回值才是处理后的二值图。findContours 的第二个参数用 RETR_EXTERNAL 只取最外层轮廓可以避免把发票上的表格单元格当成独立票据区域。找到所有轮廓后按面积排序取最大的若干个再用 cv2.boundingRect 得到外接矩形。这里要重点说 rect 函数的坑。cv2.boundingRect 返回的 (x, y, w, h) 中x 是列坐标y 是行坐标w 是宽度h 是高度。裁剪时如果写错切片维度输出就是黑图或者方向颠倒的错图。img.shape 返回的元组顺序是 (rows, cols)rows 对应图像高度cols 对应宽度和 rect 的参数方向完全相反。正确的裁剪写法是x, y, w, h cv2.boundingRect(contours[0]) ticket img[y:y h, x:x w]img[y:yh, x:xw] 的第一维是行也就是 y 方向第二维是列也就是 x 方向。很多人把 cols/rows 和 x/y 搞混写了一上午代码出来全是白图调试半天才发现是切片顺序反了。如果出现裁剪偏移或者图像被压扁的情况第一反应就查这一行。3.3 透视校正把手机拍照的歪票据摆正手机拍的票据四角不一定是严格的矩形文字一旦倾斜超过 5 度OCR 识别率会明显下降。透视校正分三步找到票据四个顶点、计算目标矩形尺寸、执行 warpPerspective。找顶点用 approxPolyDP 对最大轮廓做四边形逼近epsilon 0.02 * cv2.arcLength(max_contour, True) approx cv2.approxPolyDP(max_contour, epsilon, True)arcLength 计算轮廓周长epsilon 取周长的 2% 作为逼近精度这样轮廓点数会被压到 4 个左右。如果 approx 的长度不是 4说明前一步最大轮廓选错了对象或者票据被其他物体遮挡了。拿到四个点之后按照左上、右上、右下、左下排序再算目标矩形的宽度和高度传给 cv2.getPerspectiveTransform 得到变换矩阵最后由 cv2.warpPerspective 输出校正图。方法作用关键参数cv2.approxPolyDP多边形逼近epsilon 控制精度cv2.getPerspectiveTransform计算单应变换矩阵源 4 点、目标 4 点cv2.warpPerspective执行透视校正目标尺寸 (w, h)4. 识别与字段抽取用 PaddleOCR 把文字变成结构化数据预处理结束后图像已经比较干净进入真正的文字识别环节。PaddleOCR 是当前开源 OCR 引擎里中文场景落地最稳的选择之一检测加识别的两阶段模型结构对票据这类固定版式文本尤其友好。这一节直接给出可运行的识别代码并说明如何从识别结果里抽出需要的字段。4.1 为什么选择 PaddleOCR 而不是 TesseractTesseract 是 OCR 领域的老牌开源方案英文和印刷体清晰的文件上表现不错但中文票据上经常把金额小数点后的数字识别丢或者把“零”识别成“O”。一次发票识别里出现三五个错字对做账系统来说就很致命。PaddleOCR 采用检测加识别两阶段架构检测网络能适应表格线复杂的版面识别网络基于 CRNN 结构对中文铅印字的鲁棒性比传统特征匹配方案强很多。开源 OCR 引擎里常见选型就是 Tesseract、PaddleOCR、EasyOCR 三者EasyOCR 上手快但依赖 PyTorch打包体积大不适合交付场景。我一般把 PaddleOCR 作为主力引擎理由是它离线可用模型文件在自己机器上不依赖外部服务。行业里也在关注 DeepSeek OCR 这类端到端多模态方案对整页版面理解更强但部署自由度远不如 PaddleOCR 成熟毕业设计选后者更稳妥且更容易解释清楚。识别效果上中文票据的发票号码、价税合计这类字段PaddleOCR 的准确率在测试集上能稳定跑到 98% 以上这个数据比我两年前用 Tesseract 时的成绩好一个量级。4.2 用 PaddleOCR 批量识别票据字段初始化 PaddleOCR 并执行识别代码非常简洁from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) result ocr.ocr(ticket_crop.jpg, clsTrue) for line in result[0]: box line[0] text line[1][0] conf line[1][1] if conf 0.6: print(低置信度需要人工复核:, text) continue print(text, round(conf, 4))use_angle_clsTrue 会启用方向分类器把旋转 180 度的文字先摆正。发票代码、价税合计这类纯数字字段一旦倒置识别结果就完全不可用所以这个参数必须打开。langch 加载中文模型show_logFalse 关掉调试日志否则控制台会刷一大片推理信息。ocr.ocr 的返回值是嵌套列表每个元素包含框坐标和识别结果。0.6 是我在票据场景常用的置信度下限低于这个值的文本行自动标记为人工复核比错误入库安全得多。如果连续很多行都低于 0.6先不要急着调阈值回头去看 3.3 节的透视校正步骤多半是图像歪了或者焦距没对准。实际项目里这个环节还会接一个字段解析函数把识别出来的文本按关键词匹配比如通过“价税合计”定位金额所在行再配合正则表达式抽取出数字部分。正则和关键词匹配比纯依赖文本相似度算法更可控因为票据版式相对固定。4.3 解析 Code 128 条码并结构化输出票据底部或者左侧常有 Code 128 编码的条码比如发票代码。OpenCV 4.5.2 的原生 BarcodeDetector 可以直接尝试解码detector cv2.barcode_BarcodeDetector() ok, decoded_info, decoded_type, corners detector.detectAndDecode(img) if ok: print(条码内容:, decoded_info) print(条码类型:, decoded_type) else: print(原生解码失败用 pyzbar 兜底)detectAndDecode 返回四个值ok 表示是否解码成功decoded_type 在条码是 Code 128 时会返回 CODE_128。原生模块解不出来的时候我会再加一道 pyzbar 兜底这不是技术倒退而是真实项目里常用的双保险方案。批量扫描几百张票据时还要注意 PaddleOCR 的并发设置默认配置是按单张图推理可以通过调整 batch 大小控制内存占用和 GPU 利用率。如果你用的是 Intel A770 这类显卡PaddleOCR 支持通过 oneDNN 做推理加速但纯 CPU 在发票识别上单张也只要几百毫秒不一定需要上 GPU。5. 交付与验证从准确率评估到打包可运行程序5.1 用编辑距离评估 OCR 识别质量毕业设计答辩时必被问到的问题是识别准确率多少要回答这个问题不能只靠肉眼抽查。准备几张有标准答案的票据图片用编辑距离算法计算字符错误率 CERCharacter Error Rate是一个常见的指标这类指标在 OCR 领域的基准测试里经常用到比如经典的 iiit5k 英文场景数据集就是靠这类指标衡量模型好坏。import difflib def cer(pred, gt): seq difflib.SequenceMatcher(None, pred, gt) changes sum(opcode ! equal for opcode in seq.get_opcodes()) return changes / max(len(gt), 1) pred 价税合计(大写) 肆佰贰拾元整 gt 价税合计(大写) 肆佰贰拾元整 print(CER , cer(pred, gt))SequenceMatcher 会把两根字符串对齐get_opcodes 返回每段操作类型把 equal 之外的替换、删除、插入全部计为错误。对票据这种固定格式文本CER 低于 5% 是非常好的成绩超过 10% 就要回头调预处理的二值化参数或者透视校准则。注意这里计算的是字符级错误率不是字段级准确率两者含义不同写项目说明时要把指标口径交代清楚。5.2 用 PyInstaller 把 Python 源码打包成独立可执行文件项目说明和源码交付时对方机器不一定有 Python 环境打包是必须做的一步。PyInstaller 是 Python 打包最常用的工具命令如下pip install pyinstaller pyinstaller -D --hidden-importpaddleocr --hidden-importpaddle main.py-D 生成目录模式后续把 PaddleOCR 模型目录复制到 release 文件夹下比单文件模式更好处理模型加载路径问题。打包时经常遇到 ImportError原因是 PyInstaller 静态扫描看不到部分动态导入的模块解决方案是在 spec 文件的 hiddenimports 列表里手动把它们加全。打包完成后找一台干净电脑跑一遍冒烟测试看是否缺少 dll 或者模型路径不对验证通过再提交。项目说明里除了写清楚运行步骤我一般还会建议把识别置信度低于阈值的票据截图另存到 error 目录后续收集样本可以做模型微调这样交付物里就多了一段可执行的迭代思路。本文还有配套的精品资源点击获取