简介:这份资源面向需要快速实现文字、条形码与二维码识别的开发者,尤其适合制造业物料标签管理、快递包裹条码扫描等场景。它提供了一套可直接运行的识别方案,能自动读取不同供应商标签上的中英文文字,替代人工识别,降低耗时与出错风险,同时兼顾条码与二维码的快速解析。压缩包共16个文件,约15MB,包含4个Python脚本、3个pdiparams与3个pdmodel模型文件、3个info配置、1个xml、1个txt及1张png效果图,覆盖检测、分类、识别模型与推理代码,结构清晰,便于二次开发与集成。目前已有173人学习下载。读者可借助其中的模型与脚本,快速搭建OCR与条码识别流程,理解检测、分类、识别三阶段协作方式,并参考识别效果图验证结果,适合具备一定Python基础、希望将识别能力落地到实际业务中的开发者。
1. 从一张糊了的发票说起:这个项目到底在解决什么
上周帮朋友处理一批报销单据,手机拍出来的发票照片歪歪扭扭,条形码还反光。手动录入 200 多张,眼睛都快看瞎了。当时我就想,要是有个能同时搞定 OCR 文字识别、条形码和二维码识别的工具,直接批量跑一遍,能省下多少重复劳动。后来翻到这个项目,核心就是干这件事的——把图片里的中文、英文、条码、二维码一次性提取成结构化文本。它适合两类人:一类是手头有大量票据、证件、商品图需要批量转文字的开发者,另一类是正在做仓储、零售、物流类应用,需要把扫码和文字录入整合进同一个流程的团队。技术栈上,它走的是 Python 生态,底层依赖 OpenCV 做图像预处理,OCR 部分常见做法是接 PaddleOCR 或 Tesseract,条码识别用 ZBar 或 pyzbar,二维码用 OpenCV 自带的 QRCodeDetector。整个项目不复杂,但把几个识别能力串成了一条流水线,这才是它真正省事的地方。
2. 技术选型与目录结构:为什么是 OpenCV + PaddleOCR + pyzbar 这套组合
2.1 三个识别任务的技术拆解
文字识别、条形码识别、二维码识别,表面看都是“从图里读东西”,但底层逻辑差别不小。OCR 要处理的是像素级的字符分割和分类,中文还得考虑几千个字符集的映射;条形码是一维信号,靠黑白条纹的宽度比来解码;二维码是二维矩阵,有定位图案和纠错码。如果分别用三套独立方案,代码里会充斥大量重复的图像读取、灰度化、二值化逻辑。这个项目的做法是:统一用 OpenCV 做前置处理,然后按任务类型分发给不同的识别引擎。
常见做法是 OCR 用 PaddleOCR,因为它对中文场景的准确率明显高于 Tesseract,而且自带方向分类器,能处理旋转文本。条形码和二维码用 pyzbar,它封装了 ZBar 库,支持 EAN-13、Code128、QR Code 等主流格式,调用简单。也有开发者会用 OpenCV 的QRCodeDetector替代 pyzbar 做二维码识别,好处是不用额外装 ZBar,但识别率在低对比度图片上会差一些。
2.2 目录结构长什么样
一个典型的项目目录会这样组织:
project/ ├── main.py # 入口,命令行参数解析 ├── config.py # 路径、模型、阈值等配置 ├── core/ │ ├── preprocess.py # 图像预处理:灰度、去噪、二值化 │ ├── ocr_engine.py # OCR 封装,中英文识别 │ ├── barcode_engine.py # 条形码识别 │ └── qrcode_engine.py # 二维码识别 ├── utils/ │ ├── image_utils.py # 裁剪、旋转、透视校正 │ └── file_utils.py # 批量读取、结果导出 ├── models/ # OCR 模型文件(PaddleOCR 下载后放这里) ├── input/ # 待识别图片目录 └── output/ # 识别结果输出目录这个结构的好处是每个识别引擎独立成模块,方便替换。比如你觉得 PaddleOCR 太重,想换成 Tesseract,只需要改ocr_engine.py,其他模块不用动。preprocess.py单独抽出来也很关键,因为条形码和二维码对图像质量的要求跟 OCR 不一样——OCR 需要保留文字细节,条码识别反而需要强化黑白对比。
2.3 依赖安装与版本注意
安装依赖时,PaddleOCR 和 pyzbar 的版本兼容性是个容易翻车的地方。PaddleOCR 依赖 PaddlePaddle,而 PaddlePaddle 对 Python 版本和 CUDA 版本有要求。如果你用 CPU 推理,安装命令大致是:
pip install paddlepaddle==2.5.2 pip install paddleocr==2.7.0.3 pip install opencv-python==4.8.1.78 pip install pyzbar==0.1.9 pip install pillow==10.1.0注意:pyzbar 在 Windows 上需要额外安装 Visual C++ Redistributable,否则会报
ImportError: Unable to find zbar shared library。Linux 上一般用apt install libzbar0就能解决。
PaddleOCR 首次运行会自动下载模型文件到~/.paddleocr/目录,如果网络环境不稳定,可以手动下载后放到models/下,然后在代码里指定det_model_dir和rec_model_dir。这一步不做的话,每次换机器都要重新下载,批量部署时很浪费时间。
3. 核心模块拆解:从一张图到结构化文本的完整链路
3.1 图像预处理:别急着识别,先让图能看
很多识别失败的案例,根源不在模型,而在输入图片质量太差。这个项目的preprocess.py做了几件事:转灰度、高斯模糊去噪、自适应二值化、边缘检测。对于条形码,还会额外做形态学闭运算,把断开的条纹连起来。
import cv2 import numpy as np def preprocess_for_ocr(image_path): """OCR 预处理:保留文字细节,增强对比度""" img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 限制对比度自适应直方图均衡化,比全局均衡化更温和 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(gray) # 轻度高斯模糊,去掉噪点但保留笔画 blurred = cv2.GaussianBlur(enhanced, (3, 3), 0) return blurred def preprocess_for_barcode(image_path): """条码预处理:强化黑白对比,闭运算连接断裂条纹""" img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 自适应阈值,应对光照不均 binary = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2 ) # 闭运算:先膨胀后腐蚀,填补条纹间的空隙 kernel = np.ones((1, 3), np.uint8) closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) return closedclipLimit控制对比度增强的幅度,设太大反而会放大噪点,2.0 是个比较稳的值。tileGridSize决定局部均衡化的网格大小,8x8 适合大多数文档图片。条码预处理里的kernel形状很关键——用(1, 3)横向核是因为条形码的条纹是垂直的,横向闭运算能把断开的条纹连起来,同时不会把相邻条纹糊在一起。
3.2 OCR 引擎封装:中英文混排怎么处理
PaddleOCR 默认支持中英文混合识别,但需要显式设置lang='ch'。如果图片里英文占多数,可以设成lang='en'提升速度,但中文就会识别不出来。这个项目的做法是统一用ch模型,然后在后处理阶段过滤掉置信度低于阈值的文本行。
from paddleocr import PaddleOCR class OCREngine: def __init__(self, use_gpu=False): # use_angle_cls=True 开启方向分类,处理旋转文本 self.ocr = PaddleOCR( use_angle_cls=True, lang='ch', use_gpu=use_gpu, det_db_thresh=0.3, # 检测阈值,降低可召回更多文本 rec_char_dict_path=None # 用默认字典 ) def recognize(self, image_path, min_confidence=0.6): result = self.ocr.ocr(image_path, cls=True) texts = [] for line in result[0]: box, (text, confidence) = line if confidence >= min_confidence: texts.append({ 'text': text, 'confidence': round(confidence, 4), 'box': box # 四点坐标,可用于后续裁剪 }) return textsdet_db_thresh是文本检测的阈值,默认 0.3,调低会召回更多候选框,但误检也会增加。min_confidence是识别置信度过滤,0.6 是个经验值——低于这个值的文本通常是模糊或残缺的,强行保留反而干扰后续处理。返回的box是四个点的坐标,如果要做版面分析,比如判断哪段文字在表格里,这个信息就很有用。
3.3 条形码与二维码识别:pyzbar 的调用与容错
pyzbar 的接口很简单,但实际用起来有几个坑。它返回的data是 bytes 类型,需要 decode 成字符串;如果图片里同时有多个条码,它会全部返回,需要自己判断取哪个。
from pyzbar import pyzbar import cv2 def decode_barcodes(image_path): img = cv2.imread(image_path) # pyzbar 可以直接处理灰度图,先转灰度减少计算量 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) barcodes = pyzbar.decode(gray) results = [] for barcode in barcodes: # data 是 bytes,用 utf-8 解码,失败则用 latin-1 兜底 try: data = barcode.data.decode('utf-8') except UnicodeDecodeError: data = barcode.data.decode('latin-1') results.append({ 'type': barcode.type, # 'QRCODE' 或 'EAN13' 等 'data': data, 'rect': barcode.rect, # 边界框 'polygon': barcode.polygon # 多边形顶点 }) return resultsbarcode.type会返回具体格式,比如QRCODE、EAN13、CODE128。如果业务上只关心二维码,可以在循环里加个判断if barcode.type == 'QRCODE'。rect和polygon的区别在于,rect是正矩形,polygon是实际检测到的四边形,对于倾斜的条码,polygon更准确。
提示:pyzbar 对低分辨率图片的识别率会骤降。如果原图小于 300x300 像素,建议先用
cv2.resize放大 2 倍再识别,比直接调参有效得多。
4. 避坑与排查:识别率上不去时先查这五件事
4.1 现象:OCR 返回空列表,但图片肉眼看着很清楚
原因通常是图片通道数不对。PaddleOCR 内部期望的是 BGR 三通道图,如果你传进去的是灰度图或 RGBA 图,检测阶段就会直接跳过。解决方法是统一在入口处用cv2.imread读图,它会自动转成 BGR 三通道。如果图片本身是 PNG 带透明通道,加一行img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)。
4.2 现象:条形码识别结果乱码,或者 decode 报错
这是编码问题。有些老系统的条码数据不是 UTF-8,而是 GBK 或 Latin-1。pyzbar 返回的是原始 bytes,直接.decode('utf-8')遇到非 UTF-8 字节就会抛异常。稳妥的做法是写一个多编码尝试函数,按utf-8、gbk、latin-1的顺序试,哪个成功用哪个。另外,如果条码本身是二进制数据(比如某些工业条码),就不应该 decode,直接保留 bytes 做后续处理。
4.3 现象:二维码能识别但位置框偏移严重
这通常发生在透视畸变较大的图片上。pyzbar 返回的polygon是实际检测到的四边形顶点,而rect是外接正矩形。如果你用rect去画框,在倾斜角度大时就会偏。解决方法是优先用polygon绘制,或者先用 OpenCV 做透视校正,把二维码区域拉正再识别。
4.4 现象:批量处理时内存持续上涨,最后 OOM
PaddleOCR 的模型加载后会常驻内存,如果每处理一张图都重新实例化PaddleOCR,内存会爆。正确做法是在类初始化时创建一次实例,后续所有图片复用同一个self.ocr对象。另外,ocr.ocr()返回的结果里包含大量中间张量,如果不需要可视化,及时用del释放,或者把批量大小控制在 10 张以内。
4.5 现象:中文识别结果里夹杂大量英文误判
这是lang='ch'模型的通病——它会把一些形似字母的汉字部件识别成英文。比如“口”被识别成“O”,“日”被识别成“B”。缓解方法是在后处理阶段加一层规则过滤:如果某个文本行里中文字符占比低于 30%,且置信度低于 0.8,就丢弃。或者针对特定场景,用rec_char_dict_path指定自定义字典,把容易混淆的字符排除掉。
5. 二次开发与改造:从能跑到好用,我踩过的三个坑
5.1 把识别结果落成结构化 JSON
项目默认输出可能是纯文本,但实际业务里往往需要带位置信息的 JSON。改造思路是在main.py里加一个--output-format参数,支持txt和json两种。JSON 结构可以设计成:
{ "image": "invoice_001.jpg", "ocr": [ {"text": "发票代码", "confidence": 0.98, "box": [[10,20],[100,20],[100,40],[10,40]]} ], "barcodes": [ {"type": "QRCODE", "data": "https://example.com", "polygon": [[200,300],[250,300],[250,350],[200,350]]} ] }这样下游系统可以直接按坐标做版面还原,或者把条码数据和 OCR 文本做关联。我一般会在file_utils.py里加一个export_json函数,用json.dump时注意ensure_ascii=False,否则中文会变成 Unicode 转义。
5.2 加一层图像质量预检
不是所有图片都值得跑识别。如果一张图模糊到连人眼都看不清,直接跳过比硬跑更省时间。我加了一个简单的拉普拉斯方差检测:
def is_blurry(image_path, threshold=100): img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) variance = cv2.Laplacian(img, cv2.CV_64F).var() return variance < thresholdthreshold设 100 是个经验值,低于这个值基本可以判定为模糊。这个预检放在批量处理入口,能过滤掉 10% 到 20% 的无效图片,整体吞吐量提升明显。
5.3 用多进程加速批量识别
PaddleOCR 在 CPU 上是单线程推理,批量处理时 CPU 利用率上不去。改造方法是用multiprocessing.Pool把图片列表分片,每个进程独立加载一个 OCR 实例。注意进程数不要超过 CPU 核心数,否则上下文切换反而拖慢速度。我一般设processes=4,配合chunksize=2,在 8 核机器上能把处理速度提升 3 倍左右。
注意:多进程下每个进程都会加载一份模型,内存占用会翻倍。如果机器内存紧张,还是老老实实单进程跑,或者改用 PaddleOCR 的
predict_system批量接口。
从那以后我每次做批量识别,都强制先跑一遍模糊检测和尺寸检查,再进识别流程。这个习惯帮我省下了大量无效计算时间。希望帮到你。
本文还有配套的精品资源,点击获取