|
### 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))
```
|
|
### 用多模态大模型(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,纠正专有名词
- **后处理纠错**:用领域词典 + 编辑距离对识别结果做纠错,成本远低于微调
|