PaddleOCR-VL-1.6_Demo/README.md

543 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PaddleOCR-VL-1.6 本地 OCR
本项目使用 PaddleOCR-VL-1.6 实现统一的图片 OCR、批量图片 OCR 和 PDF 识别CPU/GPU 环境完全隔离,并通过根目录唯一入口 `ocr.py` 调用。
PDF 默认使用 **文本提取 + OCR 混合模式**:优先提取 PDF 原始文本层,仅当页面没有有效文本层或文本质量不足时才加载 PaddleOCR-VL 并 OCR。
> 当前开发机器只有集成显卡。CPU 功能已验证GPU 代码已实现,但必须在 NVIDIA CUDA GPU 机器上安装和测试。
## 目录结构
```text
ocr-VL1.6/
├── ocr.py # 唯一用户入口,自动选择 CPU/GPU 子环境
├── benchmark.py # data 参数矩阵测试与 Markdown 报告
├── ocr_app/ # CPU/GPU 共享业务代码
│ ├── cli.py # image/batch/pdf/verify 命令
│ ├── commands.py # 命令实现
│ ├── runtime.py # 设备验证、模型延迟加载
│ ├── pdf.py # 混合 PDF、断点续传、导出
│ ├── pdf_text.py # 文本层提取与质量评估
│ ├── output.py # 图片/PDF/批次统一输出路径和保存
│ └── logging_utils.py # UTF-8 结构化日志
├── cpu/
│ ├── pyproject.toml # CPU 独立依赖
│ ├── uv.lock # CPU 独立锁文件
│ ├── .python-version # Python 3.13
│ └── runner.py # 统一入口的 CPU 执行器
├── gpu/
│ ├── pyproject.toml # GPU 独立依赖
│ ├── .python-version # Python 3.11
│ ├── setup_env.py # CUDA Wheel 安装脚本
│ └── runner.py # 统一入口的 GPU 执行器
├── data/
│ ├── images/ # 测试图片
│ └── documents/ # 测试 PDF
├── outputs/ # PDF 输出git ignored
├── benchmarks/ # 单图 Benchmark JSON
├── logs/ # UTF-8 运行日志
└── tests/ # 共享逻辑测试
```
根目录不再保存 Paddle 虚拟环境或 Python 项目依赖。CPU 与 GPU 分别使用 `cpu/.venv``gpu/.venv`
## 安装
### CPU
```bash
uv sync --project cpu
```
### GPU
根据目标机器 CUDA/驱动和 PaddlePaddle 官方兼容表选择 Wheel
```bash
python gpu/setup_env.py --cuda cu118 --dry-run
python gpu/setup_env.py --cuda cu118
```
也支持:
```bash
python gpu/setup_env.py --cuda cu126
```
安装成功后脚本生成 `gpu/.gpu-ready`。统一入口只会调用已安装的 `gpu/.venv`,不会从默认 PyPI 误装 GPU 包,也不会回退到 CPU。
## 简化统一入口
主用法只有一种:
```bash
python ocr.py <文件或目录> --device cpu|gpu
```
程序自动判断输入类型:
| 输入 | 自动路由 |
|------|----------|
| `.png/.jpg/.jpeg/.bmp/.tif/.tiff/.webp` | 图片 OCR + Benchmark |
| `.pdf` | PDF 混合文本提取/OCR |
| 目录 | 发现其中的图片和 PDF逐个调用同一个单文件路由器 |
不支持的文件后缀会给出明确错误;目录模式会自动忽略不支持的文件。
### 验证环境
```bash
python ocr.py verify --device cpu
python ocr.py verify --device gpu
```
### 单张图片
```bash
python ocr.py data/images/手写01.png --device cpu
```
多轮 Benchmark
```bash
python ocr.py data/images/手写01.png \
--device cpu \
--warmup 1 \
--rounds 3
```
图片识别结果和 Benchmark 默认一起写入:
```text
outputs/images/<图片名_扩展名>/
```
### 单个 PDF
```bash
python ocr.py data/documents/sample.pdf --device cpu
```
PDF 默认使用混合模式。强制模式:
```bash
python ocr.py sample.pdf --pdf-mode text --device cpu
python ocr.py sample.pdf --pdf-mode ocr --device cpu
```
`--mode` 仍作为 `--pdf-mode` 的简写别名保留。
### 批量目录
```bash
# 扫描当前目录层级中的图片和 PDF
python ocr.py data/ --device cpu
# 递归扫描所有子目录
python ocr.py data/ --recursive --device cpu
```
目录模式的底层就是重复调用同一个单文件路由器,因此:
- 图片使用与单图片相同的 Benchmark 和日志逻辑
- PDF 使用与单 PDF 相同的混合路由、断点和导出逻辑
- 所有文件共享同一个延迟加载模型实例
- 电子 PDF 不会触发模型加载;首张图片或首个 OCR 页面才加载模型
- 保持串行处理,避免此前多进程造成卡顿、无响应和黑屏
递归目录的图片和 PDF 输出都会保留相对目录结构,避免不同子目录下同名文件相互覆盖。
## 统一输出目录
图片和 PDF 现在都会写入 `--output` 指定目录,默认是 `outputs/`
```text
outputs/
├── images/
│ └── <相对目录>/<图片名_扩展名>/
│ ├── result.md # Markdown 结果
│ ├── result.txt # 纯文本结果
│ ├── result.json # PaddleOCR 结构化结果
│ ├── benchmark.json # 模型/推理/导出耗时
│ └── assets/ # 识别结果中的图片资源(如有)
├── pdfs/
│ └── <相对目录>/<PDF名>/
│ ├── manifest.json
│ ├── document.md
│ ├── document.json
│ └── pages/
└── batches/
└── <目录名>-<时间戳>.json # 批量任务汇总
```
例如:
```bash
python ocr.py data/images/名片01.jpg --device cpu
```
会生成:
```text
outputs/images/名片01_jpg/result.md
outputs/images/名片01_jpg/result.txt
outputs/images/名片01_jpg/result.json
outputs/images/名片01_jpg/benchmark.json
```
图片目录名包含扩展名,避免 `same.png``same.jpg` 相互覆盖。
重构前生成的 PDF 结果可能仍直接位于 `outputs/<PDF名>/`。这些旧结果不会自动删除;新任务统一写入 `outputs/pdfs/<PDF名>/`,确认不再需要后可手动迁移或清理。
目录任务再次运行时:
- 已存在的 PDF manifest 自动断点续传
- 新加入的 PDF 自动创建新任务
- 图片重新识别,并使用原子写入覆盖对应输出文件
- `--overwrite` 会强制 PDF 重新处理
- 每次目录任务都会生成新的 `outputs/batches/*.json` 汇总
旧命令前缀 `image`、`pdf`、`batch` 暂时兼容,例如 `python ocr.py image a.png`,但推荐直接传入路径。
## PDF 混合模式
### 默认hybrid
```bash
python ocr.py data/documents/sample.pdf --device cpu
```
每页流程:
```text
读取 PDF 文本层
文本质量评估
┌────┴────┐
│ │
有效文本 无效/不足
│ │
直接保存 渲染页面 → PaddleOCR-VL
└────┬────┘
逐页 Markdown/JSON + 合并文档
```
如果整份 PDF 都具有有效文本层,模型完全不会加载。实测项目中的电子合同 PDF单页混合处理约 `0.06s`,且 `model_used=false`
### 强制文本模式
```bash
python ocr.py data/documents/sample.pdf \
--device cpu \
--pdf-mode text
```
所有页面只提取 PDF 文本层,不执行 OCR。扫描页可能得到空文本。
### 强制 OCR 模式
```bash
python ocr.py data/documents/sample.pdf \
--device cpu \
--pdf-mode ocr
```
所有页面都渲染后交给 PaddleOCR-VL适合模型一致性 Benchmark。
## 文本层质量判定
混合模式默认要求:
| 参数 | 默认值 | 含义 |
|------|-------:|------|
| `--text-min-chars` | 50 | 非空白字符最小数量 |
| `--text-min-printable-ratio` | 0.85 | 可打印字符最低比例 |
| `--text-min-content-ratio` | 0.60 | 字母、数字、CJK 字符最低比例 |
| `--text-max-replacement-ratio` | 0.02 | Unicode 替换字符最高比例 |
| `--text-min-density` | 25 | 页面文本密度最低值 |
例如某些扫描 PDF 只有页码或隐藏乱码层,混合模式会因为 `too_few_characters`、`low_content_ratio` 或 `high_replacement_ratio` 自动回退 OCR。
每页的判定结果会写入日志和 manifest
```json
{
"source_type": "ocr",
"routing_reason": "too_few_characters",
"text_layer": {
"usable": false,
"non_whitespace_chars": 8,
"printable_ratio": 1.0,
"content_ratio": 0.75
}
}
```
## PDF 页码与恢复
```bash
# 第 15 页、第 8 页、第 10 页到末页
python ocr.py sample.pdf --pages "1-5,8,10-" --device cpu
# 中断后继续
python ocr.py sample.pdf --resume --device cpu
# 删除旧输出并重做
python ocr.py sample.pdf --overwrite --device cpu
```
`--resume` 会校验:
- PDF SHA-256
- PDF 处理模式
- DPI
- 文本层判定阈值
- manifest 版本
旧纯 OCR manifest版本 1不能直接用于新混合模式请使用 `--overwrite` 重新生成。
## PDF 输出
```text
outputs/pdfs/<PDF名>/
├── manifest.json
├── document.md
├── document.json
├── pages/
│ ├── page-0001.md
│ ├── page-0001.json
│ └── ...
├── assets/
└── rendered/ # 仅 --keep-rendered 时存在
```
合并 Markdown 标题会标明页面来源:
```markdown
## Page 1 (text)
## Page 2 (ocr)
```
Manifest 汇总包括:
```json
{
"text_pages": 8,
"ocr_pages": 2,
"model_used": true,
"model_initialized_during_task": true,
"timing": {
"text_extract_total_seconds": 0.15,
"render_total_seconds": 0.8,
"ocr_total_seconds": 324.5,
"model_init_seconds": 40.0,
"task_total_seconds": 366.0
}
}
```
## PDF 常用参数
```bash
python ocr.py sample.pdf \
--device cpu \
--pdf-mode hybrid \
--pages "1-10" \
--dpi 144 \
--threads 18 \
--keep-rendered \
--log-file logs/pdf-sample.log
```
DPI 建议:
| 文档 | DPI |
|------|----:|
| 普通打印文字 | 120144 |
| 小字号 | 150200 |
| 手写/低质量扫描件 | 200250 |
DPI 只影响需要 OCR 的页面,不影响直接提取文本层的页面。
## 日志
统一日志格式:
```text
2026-07-16 15:07:45 | INFO | pid=33552 | PAGE_ROUTED page=1 source=text reason=usable_text_layer
```
默认目录:
```text
logs/input/
logs/verify/
logs/legacy/
```
主要事件:
- `RUNTIME_PREPARED`
- `MODEL_INITIALIZED`
- `FILE_ROUTED`
- `PAGE_ROUTED`
- `PAGE_FINISHED`
- `TASK_COMPLETED`
- `IMAGE_COMPLETED`
- `DIRECTORY_SUMMARY`
- `VERIFY_COMPLETED`
日志使用 UTF-8。Windows 控制台即使显示乱码,日志文件中的中文仍正常。
## 参数矩阵性能测试
新增 `benchmark.py`,会对 `data/` 中的图片和 PDF 按多组参数运行,统计速度并增量生成 Markdown 报告。
也可通过统一入口调用:
```bash
python ocr.py benchmark --profile smoke --device cpu
```
默认报告:
```text
benchmarks/parameter-comparison.md
```
每个用例的日志、输出和批次 manifest 保存在:
```text
benchmarks/runs/<运行时间>/<case-id>/
```
### 预设矩阵
```bash
# 较快:图片 token=64/128、PDF hybrid/text
python ocr.py benchmark --profile smoke --device cpu
# 常用对比:增加 CPU 线程、默认 token、PDF 阈值和 OCR DPI
python ocr.py benchmark --profile standard --device cpu
# 完整矩阵更多线程、token 和 DPI耗时最长
python ocr.py benchmark --profile full --device cpu
```
真实 CPU OCR 很慢,`standard/full` 可能运行数小时。建议先运行 `smoke`,或通过 `--case` 单独运行:
```bash
python ocr.py benchmark \
--profile standard \
--case image-threads-8 \
--case image-threads-safe \
--device cpu
```
查看用例列表而不执行:
```bash
python ocr.py benchmark --profile standard --list-cases
```
仅生成计划和空报告:
```bash
python ocr.py benchmark --profile standard --dry-run
```
### 自定义参数矩阵
示例配置:
```text
benchmarks/parameter-matrix.example.json
```
运行:
```bash
python ocr.py benchmark \
--config benchmarks/parameter-matrix.example.json \
--device cpu \
--report benchmarks/custom-comparison.md
```
每个 case 支持统一入口中的参数,例如:
```json
{
"id": "image-threads-8",
"title": "图片 8 线程",
"group": "image-threads",
"kind": "image",
"params": {
"threads": 8,
"max_new_tokens": 64,
"rounds": 1
}
}
```
### Markdown 报告统计
报告按参数组比较:
- 系统、CPU、逻辑核心和内存信息
- 测试文件清单、类型和大小
- Wall time
- 模型初始化耗时
- 图片核心推理 / PDF OCR 总耗时
- 平均每文件耗时
- 每分钟处理文件数
- 相对基准加速比
- 输出文本字符数
- 文本 SHA-256 是否一致
- PDF Text/OCR 页数
- PDF 路由原因统计
- 完整命令、日志和输出路径
报告每完成一个 case 就原子更新;按 `Ctrl+C` 中断时,已经完成的结果不会丢失。
其他参数:
```text
--data PATH 测试数据目录
--report PATH Markdown 报告位置
--run-root PATH 详细运行数据目录
--timeout SEC 单个用例超时
--case ID 只执行指定用例,可重复使用
```
## 自动化测试
```bash
uv run --project cpu pytest -q
```
当前测试覆盖:
- 页码范围解析
- 文本标准化
- 有效文本层判定
- 空文本/短文本自动回退条件
- 根入口设备解析
当前结果:
```text
21 passed
```
## 已验证状态
- CPU `verify`:通过
- 单图统一入口:假模型端到端通过,原真实模型能力此前已验证
- 批量统一入口:假模型端到端通过
- PDF `hybrid` 电子文本页:真实 PDF 验证通过,未加载模型
- PDF 扫描页自动回退 OCR假模型端到端通过
- PDF `text` / `ocr` 强制模式:通过
- GPU 环境路由:无环境时安全提示,不回退 CPU
- GPU 实机推理:尚未验证