一文读懂 Indra OCR 的 auto 模式:它如何判断 PDF 文本层可信、何时回退到 OCR

原创2026-10-24 16:13:0340 阅读

一文读懂 Indra OCR 的 auto 模式:它如何判断 PDF 文本层可信、何时回退到 OCR

Indra OCR 是一个只有 1M 参数、跑在 CPU 上的 OCR 模型,速度约为 Tesseract 的 15 倍。它的默认 auto 模式并不盲目执行 OCR:对于数字 PDF,它会先读取页面自带的文本层(text layer),通过一套可信度检查后再决定——通过则直接读文本层(零识别错误、速度更快),不通过则回退到 OCR 重新识别。本文把这套"先验证、再选择"的判定机制完整拆开讲清楚。

背景:PDF 文本层是什么,为什么需要 auto 模式

数字 PDF 里藏着两层信息:

  • 视觉层:页面渲染后的"图片",人眼看到的样子;
  • 文本层:页面实际存储的文字、坐标和顺序,右键"复制"复制的就是它。

如果文本层是健康的,直接读它比 OCR 更快,且没有任何识别错误——这正是 auto 模式的价值。但现实中很多 PDF 的文本层是坏的:扫描件上压了一层不可见的 OCR 文字、遗留字体存了错误的字符编码、阿拉伯文按视觉顺序存储……直接读会得到乱码。

所以 auto 模式的本质是:为每一页做一次"文本层体检",可信就读文本层,不可信就交给 OCR。三种模式的含义定义在 cli.py:

模式 行为
auto(默认) 检查通过的页面读文本层,其余走 OCR
ocr 永不读文本层,每页都 OCR
text 即使检查失败也用文本层(仅在完全无文本的页面走 OCR)

效果有多明显?在 OmniParseBench 基准上,auto 模式(文本层 + OCR)总分为 66.7,而纯 OCR 只有 61.2;单页中位耗时 212 ms 对 344 ms——读文本层的页面省下了整个识别环节。

Indra OCR 各引擎在不同核心数下的吞吐量对比,auto 模式下 32 核达到约 44 页/秒

检查一:有没有文本层、文字是否在页面上

最基础的两类情况,判定逻辑在 textlayer.py:

  • 完全没有文本层(纯扫描件):直接返回 no text layer,交给 OCR。
  • 文字框跑到了页面外(超出裁剪框的字符会被丢弃),没有有效文本时同样回退 OCR。

检查二:不可见文本 —— 识别"扫描件 + OCR 层"的指纹

很多扫描 PDF 会在图片上方叠加一层渲染模式为 invisible 的 OCR 文字(肉眼看不见,复制能复制出来)。这是最典型的"假文本层"。

Indra 统计页面中不可见文本对象的比例,超过阈值 MAX_INVISIBLE_FRAC = 0.05(5%)就判定为 OCR 层并回退,见 textlayer.py 和 invisible_text_frac:

MAX_INVISIBLE_FRAC = 0.05      # 不可见文本占比超过 5%:视为 OCR 层
MAX_BAD_CHAR_FRAC = 0.005      # 坏字符占比超过 0.5%:字体映射损坏
MAX_NO_INK_LINE_FRAC = 0.03    # 无墨迹文本行超过 3%:文字错位或隐藏
MAX_VISUAL_RTL_FRAC = 0.25     # RTL 字符按视觉顺序排列的比例

检查三:坏字符 —— 字体映射损坏的检测

PDF 文本层存的是"字体字符 → Unicode"的映射。当映射损坏时,读出来的是替换符 U+FFFD、私用区(Private Use Area)或控制字符。Indra 通过 bad_char_count 逐字符检查 Unicode 类别(Co/Cs/Cn 及非制表符的 Cc),坏字符占比超过 0.5% 即判定 broken font map——文本层里存的根本不是文字,回退 OCR。

检查四:文字下方有没有墨迹 —— 错位与隐藏文字

健康的文本层,每个文字框下方都应该有对应的黑色笔画。Indra 把页面以 72 DPI 渲染成灰度图,检查每个文本行的包围盒内是否存在暗色像素(灰度低于 DARK = 160),实现见 no_ink_frac。超过 3% 的行"脚下无墨",说明文字被隐藏或位置错位,页面回退 OCR。

检查五:RTL 视觉顺序 —— 阿拉伯文乱序陷阱

阿拉伯文、希伯来文等从右到左(RTL)书写的语言,健康文本层按逻辑顺序存储(先存最右边的字)。但部分 PDF 生成器(如 Chrome 打印)会按视觉顺序(从左到右)存储,直接读会得到字母倒序、空格在单词中间的结果。

Indra 统计同一文本行内相邻 RTL 字符对中"从左往右走"的比例,健康层通常在 0–1%,而 Chrome 打印的阿拉伯文页面高达 52–94%。超过 25%(且样本对数 ≥ 20)即判定 right-to-left text in visual order,回退 OCR。核心实现是 visual_rtl_frac,配套工具在 rtl.py。

这个修复的收益很大:Chrome 打印的 Wikipedia 页面,阿拉伯文词召回率从 0.17 提升到 1.00,波斯文从 0.25 提升到 0.89。

上下颠倒的报纸页:Indra OCR 通过 PDF 文本层识别页面旋转 180 度并转正阅读,此页来源标注为 PDF text layer

检查六:文本层与页面图像对得上吗

前五道检查都是"内部一致性",还有一道"外部一致性":文本层说的文字,和页面上实际印的文字是同一种文字吗?

背景是遗留字体:许多印地语 PDF 使用 Kruti Dev、DevLys 等字体,它们用拉丁字母编码代替天城文存储文本层——复制出来全是拉丁乱码,但页面图像上是正常的印地语。

auto 模式下,Indra 会抽取文本层中至多 6 行(AGREE_LINES),用行分类模型看这些行的图像是什么文字:如果文本层是拉丁字符(LATIN_CODES = 0.9 阈值)而图像明显是另一种文字,该行判为"不一致";不一致比例超过 25%(AGREE_SHARE)就整页回退 OCR。逻辑见 document.py,测试用例可看 test_layer_agreement.py。这类印地语页面的词准确率从 0.01–0.20 提升到 0.85–0.89,代价仅约 5 ms/页。

通过检查后:文本层页面是怎么读出来的

六关全过并不等于"读文字就完事"。Indra 仍然会运行行检测模型找到视觉行,然后把文本层字符按坐标分配到对应的行里(字符落在哪个检测框内就归哪一行,见 assign_runs)。这样做的好处:

  • 跨两个表格单元格的文本行会被正确拆开;
  • 行内公式、上下标、脚注标记归位到所在行;
  • RTL 语言的一行内部按从右到左排序。

如果某条检测出的行里一个文本层字符都没有——通常意味着文字在图片里——这一行会单独走 OCR 识别,整页来源标记为 hybrid(文本层 + 图像 OCR)。相关逻辑在 _text_page。用 --no-image-ocr 可以关闭图像内 OCR。

双栏学术期刊页:Indra OCR 读取 PDF 文本层并正确排出两栏阅读顺序、识别章节标题,来源为 PDF text layer

验证结果如何观察:source 与 note

每一页的输出都带有自我解释的字段,方便你验证 auto 模式的决策(定义见 pipeline.py):

  • source:text(读文本层)、ocr(走了 OCR)、hybrid(文本层 + 图像内 OCR)、error;
  • note:这页为什么走了这条路径,例如 invisible text 80% (OCR layer)、broken font map、text layer does not match the page。

两个观察方式:

indra-ocr paper.pdf --format json   # 每页一个 JSON 对象,含 source/note/lines
indra-ocr paper.pdf --timings       # stderr 打印每页的来源与耗时

--timings 的输出形如 paper.pdf page 3: text, 42 ms,一眼就能看出哪些页读了文本层(通常只要几十毫秒)、哪些页回退到了 OCR。

实战建议:什么时候该切换模式

场景 建议
普通数字 PDF、混合文档(部分扫描页) 保持默认 --mode auto
需要绝对统一的视觉识别结果 / 对比模型表现 --mode ocr
追求极致速度、确定文本层健康 --mode text
文本层页面中图片里的文字不需要 --no-image-ocr

一句话总结 auto 模式的决策链:

无文本层 / 不可见文字过多 / 坏字符过多 / 文字无墨迹 / RTL 视觉乱序 / 层与图像不符 —— 命中任何一条,该页交给 OCR;全部通过,则读取文本层,并只对"藏在图片里的文字"补做 OCR。

这套"先验证、再选择"的思路让 Indra OCR 既拿到了数字 PDF 的速度优势,又保住了扫描件上的识别能力——这也是它能在 OmniParseBench 上以 212 ms/页的中位耗时取得 66.7 总分的关键设计之一。更多真实输出样例可参考 EXAMPLES.md,安装使用见 README.md。

登录后查看全文
indra