主题
LiteParse 的 is_complex 命令用来判断一篇文档是否“复杂”。也就是查看图片、损坏/乱码文字、大量矢量图形以及稀疏文字。
用它来:
- 给文档分流,交给更便宜或更重的解析/OCR 后端。
- 拒收或标记你处理不了的文档 —— 比如在
--no-ocr模式下运行前,先找出那些会返回空内容的页。 - 提前估算成本,方法是统计到底有多少页真的需要 OCR。
工作原理
复杂度是按页计算的。每一页都会得到一个 needs_ocr 结论,以及一份解释它为何被标记的 reasons 列表。只要文档中任意一页需要 OCR,这篇文档就是“复杂”的。
这些理由来自几路信号:
| Reason | 含义 |
|---|---|
scanned | 一整张位图覆盖了几乎整页,其后几乎没有文字 —— 扫描页或拍摄页。 |
no-text | 几乎抽不出文字,也没有整页图片 —— 空白页、封面或分隔页。 |
sparse-text | 有一些真实文字,但只覆盖页面很小一部分 —— 通常是一幅图配一行短说明。 |
embedded-images | 大量内嵌位图与原生文字并排存在。 |
garbled | 原生文字解码出来是乱码,文字很可能不可读。 |
vector-text | 文字被当作填充的矢量轮廓画在文本层之外,因此没有原生文字项来表示它。 |
annotation-text | 有意义的文字存放在 PDF 批注里,而不是页面内容流中。 |
随着分流器学会推荐更重的流程,理由的集合还会继续增长。请把
reasons当成一个开放的列表,按你关心的取值来分流,不要假设它已经穷尽。
版面信号
除了 OCR 结论,每一页还带一个嵌套的 layout 对象,描述这一页排版有多难 —— 多栏正文、表格、密集图形。这些信号是在空间投影之后算出来的,因此它们看到的是重建出的区域树,而不是原始文字条目。
版面复杂度是刻意与 needs_ocr 分开的。一份清晰、完全数字化的双栏论文根本不需要 OCR,但版面很复杂;版面理由永远不会让 needs_ocr 变成 true。按你真正关心的那一路来分流。
| Reason | 含义 |
|---|---|
multi-column | 该页被拆成一列以上的文本栏。 |
table-likely | 在该页检测到了有边框或无边框的表格结构。 |
dense-graphics | 图形占据了页面很大一部分。 |
| 字段 | 说明 |
|---|---|
column_count | 检测到的文本栏数,总是 ≥ 1。 |
ruled_table_count | 由表格线检测出的表格数量。 |
ruled_table_coverage | 有框表格覆盖页面的比例(0–1)。 |
text_table_run_count | 检测到的无边框、按文本对齐的表格段数量。 |
figure_count | 检测到的图形区域数量。 |
figure_coverage | 图形覆盖页面的比例(0–1)。 |
is_complex | 专门针对版面的结论。等价于 reasons 非空。 |
reasons | 该页被标记上的所有版面理由(见上表)。 |
CLI
终端窗口
lit is-complex document.pdf该命令始终把每页的 JSON 打到 stdout,把人类可读的结论打到 stderr,并用退出码反映结果 —— 你可以按自己工作流的习惯去消费它。
// stdout
[
{
"page_number": 1,
"text_length": 0,
"text_coverage": 0.0,
"has_substantial_images": false,
"image_block_count": 0,
"image_coverage": 0.0,
"largest_image_coverage": 0.0,
"full_page_image": true,
"uncovered_vector_area": null,
"is_garbled": false,
"page_area": 482400.0,
"needs_ocr": true,
"reasons": ["scanned"],
"layout": {
"column_count": 2,
"ruled_table_count": 1,
"ruled_table_coverage": 0.18,
"text_table_run_count": 0,
"figure_count": 3,
"figure_coverage": 0.31,
"is_complex": true,
"reasons": ["multi-column", "table-likely", "dense-graphics"]
}
}
]# stderr
COMPLEX — 1/1 page(s) need OCR只要有任意一页需要 OCR,退出码就非零,因此这条命令可以直接当作 shell 判断条件:
终端窗口
# Only parse with --no-ocr when the document is simple
lit is-complex document.pdf --quiet && lit parse document.pdf --no-ocr把 JSON 管道给 jq 就能针对单页做处理。下面这个例子会列出需要 OCR 的页码:
终端窗口
lit is-complex document.pdf --compact | jq '[.[] | select(.needs_ocr) | .page_number]'选项
| 参数 | 说明 |
|---|---|
--compact | 输出紧凑、不含空白的 JSON,而不是美化排版。 |
--max-pages <N> | 最多检查多少页(默认:1000)。 |
--target-pages <SPEC> | 只检查指定页,例如 "1-5,10,15-20"。 |
--password <PASS> | 加密/受保护文档的密码。 |
-q, --quiet | 抑制 stderr 的日志输出。 |
在解析时顺带检查
lit is-complex 是一次独立的预解析检查。如果你本来就要解析文档,只希望这些信号跟内容一起出来,那就给 lit parse 传 --complexity —— 它会在 JSON 输出的每一页上加一个 complexity 对象,字段与这里描述的完全一致。
终端窗口
lit parse document.pdf --format json --complexity这个开关默认关闭:计算版面信号需要完整的路径投影,因此只在你要求时才运行。
lit is-complex一定会填充layout。而在lit parse里,只有设置了--complexity时才会出现complexity对象(以及它的layout)。
库
同样的检查也能以编程方式调用,它每页返回一条记录。
- TypeScript
- Python
- 浏览器(WASM)
import { LiteParse } from "@llamaindex/liteparse";
const parser = new LiteParse({ ocrEnabled: false });
const pages = await parser.isComplex("document.pdf");
const complex = pages.some((p) => p.needsOcr);
if (complex) {
// Route to a heavyweight pipeline, like LlamaParse
...
} else {
// Cheap path — skip OCR entirely
const result = await parser.parse("document.pdf");
}
// Inspect why specific pages were flagged
for (const page of pages.filter((p) => p.needsOcr)) {
console.log(`Page ${page.pageNumber}: ${page.reasons.join(", ")}`);
}from liteparse import LiteParse
parser = LiteParse(ocr_enabled=False)
pages = parser.is_complex("document.pdf")
if any(p.needs_ocr for p in pages):
# Route to a heavyweight pipeline, like LlamaParse
...
else:
# Cheap path — skip OCR entirely
result = parser.parse("document.pdf")
# Inspect why specific pages were flagged
for page in pages:
if page.needs_ocr:
print(f"Page {page.page_number}: {', '.join(page.reasons)}")import { LiteParse } from "@llamaindex/liteparse-wasm";
const parser = new LiteParse();
const pages = await parser.isComplex(pdfBytes); // Uint8Array
const complex = pages.some((p) => p.needsOcr);库和 CLI 都既接受原始字节,也接受文件路径,因此你可以对已经加载进内存的文档直接跑这项检查。
每页字段
每条记录都带着结论背后的原始信号,因此你可以自己定阈值,而不必只依赖 needs_ocr:
| 字段 | 说明 |
|---|---|
page_number | 从 1 开始的页号。 |
needs_ocr | 结论:该页不能只走廉价的纯文本路径。等价于 reasons 非空。 |
reasons | 该页被标记上的所有理由(见上表)。needs_ocr 为 false 时恰好为空。 |
text_length | 可用原生文字的长度(乱码/无法映射的文字不计入)。 |
text_coverage | 原生文字覆盖页面面积的比例(0–1)。 |
has_substantial_images | 是否存在被计入的行内位图图形。 |
image_block_count | 被计入的位图对象数量(整页背景不计入)。 |
image_coverage | 图片边界框面积之和除以页面面积,上限为 1。 |
largest_image_coverage | 被计入的最大单张图片面积除以页面面积,上限为 1。 |
full_page_image | 一整张位图覆盖了页面 ≥90% —— 这是把扫描件与空白页区分开来的信号。 |
uncovered_vector_area | 未被原生文字覆盖的填充矢量轮廓面积(pt²)。当更廉价的信号已经能定论时为 null/undefined。 |
is_garbled | 原生文字是否解码成乱码。 |
page_area | 页面面积(pt²)。 |
layout | 该页嵌套的版面信号。未计算版面时不出现。 |