PaddleOCR-VL-1.6_Demo/README.md

421 lines
11 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 子环境
├── ocr_app/ # CPU/GPU 共享业务代码
│ ├── cli.py # image/batch/pdf/verify 命令
│ ├── commands.py # 命令实现
│ ├── runtime.py # 设备验证、模型延迟加载
│ ├── pdf.py # 混合 PDF、断点续传、导出
│ ├── pdf_text.py # 文本层提取与质量评估
│ └── 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 控制台即使显示乱码,日志文件中的中文仍正常。
## 测试
```bash
uv run --project cpu pytest -q
```
当前测试覆盖:
- 页码范围解析
- 文本标准化
- 有效文本层判定
- 空文本/短文本自动回退条件
- 根入口设备解析
当前结果:
```text
17 passed
```
## 已验证状态
- CPU `verify`:通过
- 单图统一入口:假模型端到端通过,原真实模型能力此前已验证
- 批量统一入口:假模型端到端通过
- PDF `hybrid` 电子文本页:真实 PDF 验证通过,未加载模型
- PDF 扫描页自动回退 OCR假模型端到端通过
- PDF `text` / `ocr` 强制模式:通过
- GPU 环境路由:无环境时安全提示,不回退 CPU
- GPU 实机推理:尚未验证