OCR处理技巧
### pytesseract 基础用法(黄金九步法 notebook 中的做法) 从扫描 PDF / 图片中提取文本,用 `pytesseract + Pillow` 两行代码即可完成基础 OCR: ```python from PIL import Image from pytesseract import image_to_string YOUR_DEMO_DATA_PATH = "data/" # 请改成自己的路径 filename = YOUR_DEMO_DATA_PATH + "banjiu_752.png" text = image_to_string(Image.open(filename)) print(text) ``` **安装**:pytesseract 只是封装,底层依赖 tesseract 引擎。用 micromamba 从清华镜像安装: ```bash micromamba install -n nlp -y \ -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ \ tesseract pip install pytesseract pillow ``` **常用技巧**: ```python import pytesseract from PIL import Image, ImageOps img = Image.open("data/banjiu_752.png") # 1. 指定语言:中文+英文(chi_sim 需额外安装语言包) text = pytesseract.image_to_string(img, lang="chi_sim+eng") # 2. 限定页面分割模式(PSM):单行文本用 7,稀疏文字用 11 text = pytesseract.image_to_string(img, config="--psm 6") # 3. 预处理提升识别率:灰度 + 二值化 + 适当放大 img2 = img.convert("L") # 转灰度图 img2 = img2.point(lambda x: 255 if x > 160 else 0) # 二值化 img2 = img2.resize((img2.width*2, img2.height*2)) # 小字放大 text = pytesseract.image_to_string(img2) # 4. 获取带位置框的结果,便于后续版面分析 data = pytesseract.image_to_data(img, output_type=pytesseract.Output.DICT) ``` **局限**:tesseract 对艺术字、手写体、弯曲/倾斜文本、复杂背景效果都很差,遇到这类图片应换用下面的深度学习 OCR 工具。
### PaddleOCR:中文场景首选,对艺术字最稳 百度飞桨出品,PP-OCRv4/v5 模型对中文、艺术字、倾斜文本、复杂背景的鲁棒性远好于 tesseract。 ```bash pip install paddlepaddle paddleocr ``` ```python from paddleocr import PaddleOCR # lang="ch" 中英文混合模型;use_angle_cls=True 自动纠正倾斜/旋转文字 ocr = PaddleOCR(use_angle_cls=True, lang="ch") result = ocr.ocr("data/art_font.png", cls=True) for line in result[0]: box, (txt, conf) = line[0], line[1] print(f"文本: {txt} 置信度: {conf:.2f} 位置: {box}") ``` **使用技巧**: 1. **返回结构化结果**:每行给出 `文本 + 置信度 + 四点坐标框`,可以按 conf 过滤低质量识别,再按坐标排序恢复阅读顺序 2. **倾斜纠正**:`use_angle_cls=True` 处理 90°/180° 旋转的招牌、封面艺术字 3. **版面分析**:`PP-Structure` 可做表格还原、版面区域划分,适合扫描 PDF 4. **速度优化**:CPU 上用 `enable_mkldnn=True`;批量图片可开多进程 5. **私有化离线**:首次运行自动下载模型,之后完全离线,适合数据不能出内网的场景(模型下载细节见下方) ```python # 按置信度过滤 + 按 y 坐标排序恢复阅读顺序 lines = [l for l in result[0] if l[1][1] > 0.85] lines.sort(key=lambda l: l[0][0][1]) # 以左上角 y 排序 text = "\n".join(l[1][0] for l in lines) ``` ### 首次运行下载了什么模型?放在哪?能自定义吗? **下载的是三个模型**(PP-OCRv4/v5 系列,各司其职组成 pipeline): | 模型 | 作用 | 说明 | |------|------|------| | 检测模型 det | 找出图中所有文本框的位置 | 必须下载 | | 方向分类器 cls | 判断文本框内文字是否旋转了 180° | `use_angle_cls=True` 时才加载 | | 识别模型 rec | 对每个文本框逐个识别出文字 | 必须下载,与 `lang` 绑定 | 每个模型目录下包含 `inference.pdmodel`(网络结构)、`inference.pdiparams`(权重)、`inference.pdiparams.info`(权重校验)三类文件。只要目录和文件完整,下次运行就不会再联网。 **默认下载路径**(首次运行时自动创建): ```text Linux / macOS: ~/.paddleocr/ (2.x 部分老版本用 ~/.paddle/) Windows: C:\Users\<用户名>\.paddleocr\ ``` **三种自定义模型路径的方法**: ```python # 方法 1:初始化时显式指定模型目录(推荐,最可控) from paddleocr import PaddleOCR ocr = PaddleOCR( use_angle_cls=True, lang="ch", det_model_dir="models/ppocrv4/det", cls_model_dir="models/ppocrv4/cls", rec_model_dir="models/ppocrv4/rec", ) ``` ```bash # 方法 2:环境变量重定向缓存目录 export PADDLE_PDX_CACHE_HOME=/data/paddle_cache # 3.x 基于 PaddleX,默认 ~/.paddlex export PADDLEOCR_HOME=/data/paddle_cache # 旧版本约定 ``` ```text 方法 3:完全离线部署(内网机器) 1. 在有网的机器上跑一次(或从官网模型库下载压缩包),拿到三个模型目录 2. 拷贝到内网机器,用方法 1 的 det_model_dir 等参数指向本地目录 3. 路径必须指向包含 inference.pdmodel 的目录,否则仍会尝试联网下载 ``` **避坑提示**: - paddleocr 3.0.0 存在已知 bug(Issue #15594):传入本地模型目录仍可能触发联网下载,遇到时升级版本即可 - 模型和 `lang` 绑定:`lang="ch"` 下载中英文模型,换 `lang="en"`、`lang="japan"` 会再下载一套,多语言场景注意磁盘占用 - 大版本升级后模型命名/格式可能变化,旧缓存失效会重新下载;服务器部署建议锁定 paddleocr 版本 + 预下载模型入库
### EasyOCR:多语言、易用的 PyTorch 方案 支持 80+ 语言,接口极简,中文识别效果好,GPU 上速度快。 ```bash pip install easyocr ``` ```python import easyocr # lang_list 同时装中英文;gpu=False 在 CPU 上跑 reader = easyocr.Reader(['ch_sim', 'en'], gpu=False) result = reader.readtext("data/art_font.png") # 返回 (bbox, text, confidence) for bbox, txt, conf in result: print(f"{txt} ({conf:.2f})") ``` **使用技巧**: 1. **限定语言列表**:只装用得到的语言,减少模型加载时间 2. **输出整段文本**:`readtext(img, detail=0, paragraph=True)` 直接得到按段落合并的字符串,适合替代 `image_to_string` 3. **坐标做 ROI 二次识别**:第一次拿到文本框,把小字区域裁剪放大后再识别一遍,可显著提升小字号准确率 4. **批处理**:`reader.readtext_batched([img1, img2])` 批量识别比循环单张快 ```python # detail=0 只返回文本,paragraph=True 合并相邻行为段落 text_list = reader.readtext("data/banjiu_752.png", detail=0, paragraph=True) print("\n".join(text_list)) ```
### 艺术字识别对比与选型建议 | 工具 | 艺术字/手写 | 中文效果 | 速度(CPU) | 依赖体积 | 适用场景 | |------|------------|---------|-----------|---------|---------| | tesseract | ❌ 很差 | 一般 | 中 | 小 | 干净的印刷体扫描件 | | PaddleOCR | ✅ 较好 | ✅ 最佳 | 中 | 大 | 中文为主、复杂背景、生产环境 | | EasyOCR | ✅ 较好 | 好 | 慢(CPU)/快(GPU) | 大 | 多语言、快速原型 | | 多模态大模型 | ✅ 最强 | ✅ 强 | 慢、有成本 | - | 曲线艺术字、需要理解内容 | **艺术字场景的实用组合拳**: 1. **预处理仍是第一生产力**:灰度 → 去噪(中值滤波)→ 自适应二值化 → 放大 2~3 倍,能救活一大批"半艺术"文字 2. **检测和识别分开**:先用 PaddleOCR 检测文本框,再对每个框单独放大识别,必要时旋转摆正(透视变换) 3. **兜底交给大模型**:传统工具置信度普遍低于 0.7 的图片,直接走多模态大模型(见下一节) 4. **结果校验**:用置信度过滤 + 正则/词典校验(如日期、手机号格式),低置信的送人工复核 ```python # 典型 pipeline:置信度分流 if avg_conf > 0.9: # 传统 OCR 可信 use(paddle_result) else: # 艺术字/复杂背景,升级到大模型 use(vlm_ocr(image)) ```
大模型OCR与微调
### 用多模态大模型(VLM)做 OCR 的注意事项 大模型(GPT-4o / Qwen-VL / GLM-4V / InternVL 等)对艺术字、手写体、表格、公式、连环画文字的识别远超传统 OCR,但工程上有不少坑: **1. 幻觉问题(最致命)** - 模型可能"脑补"出图片上不存在的文字,尤其是模糊、残缺区域 - 对策:prompt 里明确要求"只输出图中实际存在的文字,看不清的输出 `[无法识别]`";关键业务字段用正则/词典二次校验;要求输出坐标(Qwen-VL 支持 grounding)便于回溯原图核对 **2. Prompt 设计直接影响输出格式** ```text 你是 OCR 助手。请逐字转录图片中的所有文字,要求: 1. 保持原文的阅读顺序和换行 2. 不要翻译、纠正错别字或添加任何解释 3. 表格用 Markdown 表格输出,公式用 LaTeX 输出 4. 看不清的文字用 [?] 占位,禁止猜测 ``` - 输出长度上限:整页密集文本可能被截断,长图建议先切片再逐块识别 - temperature 设为 0,保证结果可复现 **3. 成本与延迟** - 按 token 计费,图片 token 开销大(一张图几百到几千 token),批量扫描件场景成本远高于本地 PaddleOCR - 对策:分级策略——传统 OCR 先跑,低置信度样本才升级到大模型 **4. 隐私与合规** - 云端 API 意味着图片出网,身份证、合同、病历等敏感图片必须走本地部署的开源 VLM(Qwen2-VL、InternVL、MiniCPM-V 等) **5. 长文本/顺序问题** - VLM 可能改变阅读顺序、漏掉小字水印;密集文本场景召回率不一定比 PaddleOCR 高,务必在自己的测试集上评估而非只看 demo **6. 评估指标** - 构建 50~200 张带人工标注的测试集,统计:字符准确率(CER)、字段级精确率/召回率(业务字段如金额、日期)、幻觉率(输出中不在原图的文字占比)
### OCR 大模型的微调方法 通用 VLM 对特定领域(票据、方言手写、古籍、工业铭牌、艺术海报)往往不够准,微调是提升的关键手段。 **1. 什么时候需要微调** - 通用模型在你的测试集上 CER 高于业务可接受阈值(如 2%) - 有特殊的版面结构/字体/术语词典,通用模型不认识 - 手里有 500+ 对(图片, 标注文本)数据 **2. 数据准备(最重要的一步)** ```text 每条样本 = 图片 + 目标输出(含指令) { "image": "train/receipt_001.jpg", "conversations": [ {"role": "user", "value": "<image>\n逐字转录图中的文字"}, {"role": "assistant", "value": "XX超市\n小票号: 20240824-752\n合计: 86.50元"} ] } ``` - **数据来源**:人工标注最准但贵;可用强模型(如 GPT-4o)预标注 + 人工抽检修正;数据增强(旋转、加噪、透视、背景合成)提升鲁棒性 - **格式对齐**:目标输出格式要和推理时要求的格式完全一致,否则微调后格式反而变差 **3. 微调技术选型** | 方法 | 显存需求 | 适用 | |------|---------|------| | LoRA / QLoRA | 低(单卡 24G 可微调 7B) | ✅ 大多数 OCR 微调场景首选 | | 冻结视觉编码器,只调 LLM 部分 | 中 | 文本输出格式问题 | | 全量微调 | 很高 | 大厂大规模数据 | **4. 实操示例:LLaMA-Factory 微调 Qwen2-VL** ```bash git clone https://github.com/hiyouga/LLaMA-Factory cd LLaMA-Factory pip install -e ".[torch,metrics]" # 将标注数据注册为 ocr_train 数据集(放入 data/ 并在 dataset_info.json 中登记) # QLoRA 微调 llamafactory-cli train \ --stage sft --do_train \ --model_name_or_path Qwen/Qwen2-VL-7B-Instruct \ --dataset ocr_train \ --finetuning_type lora --lora_target all \ --lora_rank 16 --lora_alpha 32 \ --template qwen2_vl \ --output_dir saves/ocr-qwen2vl \ --per_device_train_batch_size 1 --gradient_accumulation_steps 8 \ --num_train_epochs 3 --learning_rate 1e-4 \ --bf16 --cutoff_len 4096 ``` **5. 训练与评估要点** - 划分 train/val/test(按图片来源划分,防止同一版式泄漏导致虚高) - 监控验证集 CER,早停防过拟合;LoRA 学习率 1e-4 ~ 5e-5,epoch 1~3 即可,训多了容易过拟合导致幻觉加重 - 用测试集对比"微调前 vs 微调后 vs 传统 OCR",确认收益再上线 - 上线后收集 badcase 回流标注,形成数据飞轮持续迭代 **6. 更轻量的替代:不微调的增强方案** - **RAG 词典注入**:把领域术语表、价目表放进 prompt,纠正专有名词 - **后处理纠错**:用领域词典 + 编辑距离对识别结果做纠错,成本远低于微调
参考