OCR 文字识别管线设计
图片和 PDF 的 OCR 文字识别:技术选型(pytesseract/PaddleOCR/VLM)、图像预处理、结构化提取、准确率与局限。
#synthesis
#growing
#ai
#python
[!info] related notes
- 所属 MOC: Python MOC
- 相关: 光学字符识别 OCR, deepseek-ocr, umi-ocr
OCR 文字识别管线设计
技术选型
| 方案 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| pytesseract + Tesseract | 可离线、Python 接口简单 | pytesseract 是包装器,仍需安装 Tesseract 引擎与对应语言数据 | 印刷体、已有本地 OCR 工作流 |
| PaddleOCR | 提供多语言与文档处理组件 | 模型、运行时和部署成本随版本而异 | 需要本地文字检测/识别或文档组件的场景 |
| DeepSeek-OCR / VLM | 可结合文字与版面输出 | 可本地或通过服务部署;资源成本、漏字和生成错误需评估 | 复杂文档的候选方案 |
| 云服务(阿里/腾讯等) | 托管 API,减少本地模型运维 | 计费、网络与数据传输要求;准确率需同样本比较 | 可接受云端处理且接口符合需求的场景 |
处理流程
文件上传 → 判断类型(图片/PDF)
→ 图片:预处理 → OCR
→ PDF:按页/区域检查文本层
→ 文本可用:直接提取
→ 缺失或质量不足:渲染对应页/区域 → OCR
→ 纯文本 → 结构化提取(正则或 LLM)
图像预处理
from PIL import Image, ImageOps
def preprocess(img: Image.Image, threshold: int | None = None) -> Image.Image:
# 返回处理后的图,保留原图供对比;先按 EXIF 矫正方向。
result = ImageOps.exif_transpose(img).convert("L")
if threshold is not None:
if not 0 <= threshold <= 255:
raise ValueError("threshold must be between 0 and 255")
result = result.point(lambda value: 0 if value < threshold else 255)
return result
上述函数依赖 Pillow,只演示方向矫正、灰度与可选阈值;不包含 OCR 调用。二值化、滤波或放大都可能损伤细字与颜色信息,不能保证提高识别率,应与原图结果比较。
PDF 文本层与扫描区域
- 有可用文本层时可用 PyMuPDF 的 page.get_text();仍须检查编码、阅读顺序和区域完整性。
- 同一 PDF 甚至同一页可同时包含文本和扫描区域;不能因提取到少量文本就跳过所有图片区域。
- 需要 OCR 的页/区域再渲染;分辨率在细字可读性、内存和耗时之间选择,高 DPI 不自动恢复原图丢失的细节。
结构化提取
OCR 输出可能包含文本、坐标和结构;字段提取应保留页码/区域与原文证据。以下只从简单模式中提取 BMI 候选值:
import re
text = 'BMI:23.5'
bmi = re.search(r'\bBMI\s*[::]?\s*(\d+(?:\.\d+)?)(?![\d.])', text, re.IGNORECASE)
print(bmi.group(1) if bmi else None) # 23.5
复杂格式可尝试规则、专用文档模型或 LLM;输出仍需 schema/范围校验并回查原文。文档文本是待处理数据,其中的指令不能改变提取器的规则或权限。
准确率
没有固定样本集、版本与配置时,不给工具排“高/中/低”准确率。用目标文档的标注集分别检查:
- 文字:字符/词错误率,关键数字的逐字段正确率。
- 版面:阅读顺序、表格单元格对应关系与页码定位。
- 系统:单页耗时、内存、失败率和人工复核量。
手写、倾斜、模糊与混合语言应单独分组,避免平均指标掩盖关键失败。
设计要点
- 可直接获得可靠文本时优先复用;扫描文档则以 OCR 为主要输入路径。
- 保留原文件和预处理参数,依据样本选择是否二值化/去噪。
- 按页与区域决定是否 OCR,避免重复文字或漏掉扫描区域。
- 字段结果保留来源,低置信度或相互矛盾的值进入复核。