本文是 docs/ancient-book-support.md 的执行版:评估文档回答"够不够用、缺什么",
本文回答"先做什么、换哪个引擎、怎么验收"。撰写于 2026 年 10 月,基于对
benchmark/V..pdf 的真实测量与 #85–#89 的实施结果。
- 不更换双引擎分歧 → 风险 → 复核 → 决策的骨架。 这是本仓库唯一别人不提供的 东西;任何 OCR 模型都只是它的输入。
- 换主引擎,不改架构:主识别用 PaddleOCR-VL(0.9B, Apache-2.0),
副识别使用 PP-OCRv4(CPU 可跑,原生
return_word_box),形成"快引擎 × 强引擎"的分歧组合。 v5/v6 的当前 Transformers 识别器不提供该原生词框输出,故不能用行框伪装 token。 - 版面/阅读顺序引入独立第二意见:Surya(只作结构证据,不作文本)。 它的价值在"页面结构"而非"字形",正对 #86 之后的剩余缺口。
- 竖排代码路径已实现,但真实识别能力尚未验证:3 页刻本的 Oracle 回归覆盖了方向、配对、阅读顺序与列拼接;R4/R5 仍是能力声明的硬门槛(见 §5)。
- VLM 只做复核,永不做主识别:它会静默改写原文,与证据链模型冲突。
| Issue | 内容 | 交付物 | 验证方式 |
|---|---|---|---|
| #85 | 预处理层(deskew / deshadow / contrast / 按需 denoise、binarize、crop) | domain/preprocess.py、adapters/image_preprocess.py、pipeline/preprocessing.py、schema v5 |
benchmark/analysis/preprocess_benchmark.py:真实页面派生 8 种退化,spine-shadow 191.5→228.8 分离度、faded 0.0→228.6、skew 残余 0.00° |
| #86 | worker 契约 v2(块类型、行级几何、置信度、方向) | workers/protocol.py、domain/ocr.py、MinerU/Paddle worker |
契约测试 + worker 载荷测试 |
| #87 | 章节 / 印刷页码 / 页面角色 | domain/structure.py、pipeline/structure.py、schema v6、EPUB 章节化导航 |
结构测试 55 项 + 导出测试(嵌套 nav、印刷页码标题) |
| #88 | 繁简/异体规范化(只影响比较) | domain/normalize.py |
折叠/风险/裁定三层测试 |
| #89 | 页眉页脚剔除(块 + 页边重复 + 连续页码) | domain/furniture.py |
误删防护测试 + 装配集成测试 |
两个在实施中被实测发现、此前只在纸面上的假设:
- "预处理总是有益"是错的:中值滤波在 300 DPI 真实页面上使墨/纸分离度从
225.8 降到 222.6。默认关闭,只有整页判定为脏/阴影/褪色时才动像素
(见
docs/preprocessing.md§3.1)。 - "块证据已经在用"也是错的:#86 只改了域模型与 worker 输出,
ocr_blocks从未落库,结构检测读回时 blocks 恒为空(见docs/book-structure.md§2)。
| 角色 | 引擎 | 许可 | 体量/显存 | 为什么是它 |
|---|---|---|---|---|
| 主识别(准) | PaddleOCR-VL 0.9B | Apache-2.0 | 0.9B,约 2 GB 显存 | 官方明确覆盖繁体、竖排、手写、旧文档;与现有 Paddle 生态同源,paddle_worker 改动最小 |
| 副识别(快) | PP-OCRv4 + PP-StructureV3 | Apache-2.0 | 数十 MB 级,CPU 可跑 | 原生 return_word_box 提供真实字符/词框,行级 rec_score 仍保留;PP-Structure 提供版面标签 |
| 版面/阅读顺序证据 | Surya(仅结构) | 代码 Apache-2.0,权重 OpenRAIL-M | 650M | 版面、阅读顺序、表格的第二意见;不参与文本判定 |
| 竖排第三意见 | CnOCR 或 Indra OCR | Apache-2.0 / 见仓库 | CPU | 两者都明确支持竖排,成本低,适合交叉验证 |
| 古籍分栏 | Kraken(评估) | Apache-2.0 | 小,CPU | 专为历史文献分栏设计,可自训字形 |
| 对照基线 | Tesseract chi_tra_vert |
Apache-2.0 | 极小 | 只在回归测试中作基线,不作生产引擎 |
| 方案 | 原因 |
|---|---|
| 9B 级 VLM 端到端出全书 Markdown(Chandra / olmOCR / DeepSeek-OCR) | 不可溯源、会幻觉,与"证据级、绝不静默改写"的铁律冲突;显存门槛也排除普通用户 |
| 云端 OCR API | 违背 local-first |
| 自研/自训 OCR 模型 | 成本远超收益,开源方案已足够 |
| TongGuOCR(2026-08 学术工作,中文历史文献) | 论文报告 M5HisDoc AR 93.76%、NED 10.43→6.15,很值得跟踪;但权重/许可以及稳定性未核实,不投产 |
| OpenCC 全量繁简转换 | 作为派生层可选引入(#88 已留出接口),但绝不能覆盖原文字段 |
许可注意:Surya/Marker 的权重是 OpenRAIL-M(不是 Apache-2.0), 若将来把模型文件随发行版分发,必须单独标注许可与使用限制;MinerU 为 Apache-2.0 但附带归属与收益条款。此项必须在打包前复核,不能只写在文档里。
PaddleOCR-VL 与 Surya 都不需要新的适配器类型——docs/ocr-worker-contract.md
的 v2 文档已能表达它们的输出:
{
"schema_version": 2,
"engine": "paddleocr-vl",
"engine_version": "0.9b",
"page_index": 12,
"direction": "vertical-rl",
"lines": [{"text": "...", "box": [0.72, 0.11, 0.03, 0.62], "confidence": 0.94, "tokens": null}],
"blocks": [{"type": "text", "box": [0.70, 0.10, 0.08, 0.66], "line_start": 0, "line_end": 24}]
}- Surya 作为结构引擎接入时只填
blocks与direction、lines可为空—— 下游text_line_indexes()会把"无块证据"当作全部保留,因此结构引擎不会误删内容。 - 新增引擎只需:runtime planner 增加一条模型条目 + 一个 worker 脚本 + 基准条目, 不需要改动 pipeline。
| 项 | 文件 | 说明 |
|---|---|---|
| PaddleOCR-VL worker | workers/paddle_vl_worker.py |
调用 PaddleOCRVL().predict(),把 parsing_res_list 映射为 v2 行/块;行序保留模型自己的区域顺序(那就是它的阅读顺序) |
| Surya 结构 worker | workers/surya_worker.py |
只出 blocks(按 position 排序),lines 恒为空;不声明方向 |
| 运行时计划 | runtime/planner.py |
paddleocr-vl、surya 两条 RuntimeBackendPlan,均标 optional=True |
| 基准注册 | cli.py _BENCHMARK_BACKENDS |
--engine paddleocr-vl / --engine surya 可直接进基准 |
三条与"诚实性"有关的设计约束:
- 可选后端不进默认安装。 两者都是数 GB 的模型栈,
install会跳过optional的后端,需要时用runtime repair --backend surya单独装。 - 不声明方向 ≠ 声明横排。
result_document(direction=None)直接省略该字段; MinerU/PP-OCR 没有方向结论时也不写。写一个常数horizontal-lr会被下游当作 引擎断言,从而压制几何方向检测——那正是竖排书逐页配错的成因。 - 未验证的部分写在文档里,不写进 README 能力声明。 两个 worker 的上游 API 与 JSON 字段名来自文档而非实跑;解析器对字段缺失/改名做降级(宁可"无块证据" 也不崩页),但 R1/R2 的"真实权重跑通"仍是未完成的验收动作。
| 阶段 | 内容 | 验收标准(可测量) | 状态 |
|---|---|---|---|
| R1 | PaddleOCR-VL worker 接入(--image/--page-index 契约不变) |
6 页基准上双引擎均产出 v2 文档;normalize 后分歧率低于 PP-OCRv4 × MinerU 组合 |
|
| R2 | Surya 结构 worker(只出 blocks/direction) | 基准页 block 数量与非文本区(图表)排除结果人工抽查通过 | |
| R3 | 竖排对齐、阅读顺序与列拼接(#79 + P0-2) | 3 页真实刻本的 Oracle 全链路回归通过;真实 OCR 引擎输出仍待验证 | ✅ 代码与回归测试完成 |
| R4 | 竖排/繁体基准语料(公有领域古籍扫描) | 语料 ≥ 20 页,含页眉页脚、双行夹注、竖排正文;人工转写双人校订 | |
| R5 | 端到端 CER 基准 | 报告 benchmark_report.md:每引擎/每组合的 CER,横排与竖排分开 |
|
| R6 | 效率(页级并发、PDF 句柄复用、结果缓存) | 562 页端到端时间下降 ≥ 40% | ⏳ 尚未开始 |
门槛声明:R4/R5 尚未完成,README 不得宣称"支持古籍"。当前状态是 垂直布局的代码路径和真实页面回归已具备,真实 OCR 准确率与 R4 语料仍缺。
方向感知对齐和阅读顺序已不再是待实现事项。domain/direction.py、
domain/alignment.py 与 domain/reading_order.py 分别承担页面方向、配对轴与
独立阅读顺序;domain/paragraphs.py 在竖排列之间拼接时不插入行内空格。EPUB/DOCX
均有竖排拼接回归测试,benchmark/corpus/guji/ 的 3 页真实刻本也由
--oracle-ceiling 全链路验证(识别错误被强制为零,不等于真实 CER)。
仍待验证的是能力,而不是上述代码路径是否存在:
- 真实引擎输出:确认实际 OCR worker 对竖排页面按列输出、方向声明正确, 并测量方向漏检/误检率;当前 3 页 oracle 测试不调用真实 OCR 模型。
- R4 语料:从 3 页扩至至少 20 页,覆盖双行夹注等复杂版面并完成双人校订。
- R5 识别准确率:在装有真实模型权重的机器上生成可复核的逐引擎与全链路 CER。
具体语料数量、许可与转写状态见 benchmark-corpus.md;
古籍支持门槛仍保持关闭。
- 当前基准包含 6 页现代简体横排和 3 页繁体竖排刻本;3 页竖排样本仍不足以通过
R4。两份 manifest 均显式声明
ground_truth_review_status: single-pass,基准报告 会展示转写校订状态与说明。 - 本仓库至今没有真实 OCR 准确率数字。在 R5 之前,任何"识别率"表述都必须 标注为未测量。已有的是可复现的像素级测量(预处理分离度、歪斜残差), 它们不能替代 CER。
| 文档 | 内容 |
|---|---|
docs/ancient-book-support.md |
能力评估、缺口分析、调研清单(本路线的问题来源) |
docs/preprocessing.md |
#85 的实现与实测依据 |
docs/ocr-worker-contract.md |
引擎接入契约(v1 兼容,v2 当前) |
docs/book-structure.md |
#87 章节/页码模型 |
docs/glyph-variants.md |
#88 规范化层 |
docs/page-furniture.md |
#89 页眉页脚剔除 |
docs/development-plan.md |
阶段计划(Phase 5 对应本路线) |