阅读文本 / Reading text
先分流,再 OCR:用 PyMuPDF 给每一页留一个可复核的判断
收到一份几十页的 PDF,第一反应常常是“整本跑一遍 OCR”。这很省思考,却把三种完全不同的页面交给了同一种处理方式:本来就有文本层的页、只有扫描图像的页,以及没有正文的页。更麻烦的是,三种页面可能同时出现在一个文件里。本文先做一件小事:为每页记录处理路线和判断依据,给后续提取、翻译、复核留下入口。
把结论写成“候选”,而不是“已经识别正确”
下面的规则刻意保守。有可提取文本,标为文本候选;没有文本但显示了图像,标为 OCR 候选;两者都没有,进入复核。这不是扫描件分类器,更不是识别质量评分。比如一张只有页码的扫描页也会进入文本候选,所以记录路线之后还必须看缩略图,不能把这个标签当成全页内容已提取的证明。
这一页该走哪条路?
可点击的教学示例;不上传文档,不调用接口。
全屏
图 1
可切换的判断示意。仅演示处理规则,不代表产品界面或真实效果。
表 1
分流信号与容易误判的情况
观察
路线
不能据此认定
抽到非空文本
文本候选
文本层正确、顺序正确或覆盖全页
无文本,有显示图像
OCR 候选
图像一定含文字
无文本,无显示图像
复核
这一页一定可删除
用三页合成样本跑出一个确定结果
示例在内存中创建英文文本页,把它渲染成图片后插入第二页,最后加一张空白页。它不读取客户文件,也不依赖识别服务。安装 PyMuPDF 后保存代码为 page_routes.py,再运行 python page_routes.py。下列连续代码段应按顺序复制到同一文件。
import pymupdf
def inspect(page):
text = page.get_text("text").strip()
images = page.get_image_info()
if text:
route = "text_candidate"
elif images:
route = "ocr_candidate"
else:
route = "review_empty_or_vector"
return {"page": page.number + 1, "route": route,
"characters": len(text), "images": len(images)}
# Synthetic fixtures, not customer files.
with pymupdf.open() as source:
p = source.new_page(width=300, height=160)
p.insert_text((20, 60), "Invoice DEMO 1200.50")
png = p.get_pixmap(dpi=120, alpha=False).tobytes("png")
with pymupdf.open() as doc:
doc.insert_pdf(source)
p = doc.new_page(width=300, height=160)
p.insert_image(p.rect, stream=png)
doc.new_page(width=300, height=160)
rows = [inspect(p) for p in doc]
assert [r["route"] for r in rows] == [
"text_candidate", "ocr_candidate",
"review_empty_or_vector"]
assert rows[0]["characters"] > 0
assert rows[1]["characters"] == 0
assert rows[1]["images"] == 1
for row in rows:
print(row["page"], row["route"])
本机在 Python 3.13.11、PyMuPDF 1.28.0 上执行得到:第 1 页 text_candidate,第 2 页 ocr_candidate,第 3 页 review_empty_or_vector。断言同时检查了第二页抽不到文本但确实显示一张图像。这只验证分流程序在这三个可控样本上的行为;本文没有运行 Tesseract,也没有给出真实扫描件的识别准确率。
接入 OCR 时最容易漏掉的一步
PyMuPDF 的 OCR 接口依赖额外安装的 Tesseract 及对应语言数据。拿到 OCR TextPage 后,后续提取要明确传入 textpage;再次直接对原页抽取文本,并不会自动使用刚才的识别结果。官方文档给出的复用模式如下。此片段未在本次实验中执行,需先安装语言数据。
# page 必须仍属于一个打开的 Document
tp = page.get_textpage_ocr(language="eng", dpi=200, full=True)
text = page.get_text("text", textpage=tp)
full=True 的完整页识别不是对所有文件都最合适。混合页需要决定保留已有文本还是重新识别;不同版本对部分 OCR 的行为也可能变化,应锁定依赖版本再做回归。不要用一个 OCR 结果覆盖原始文本字段,建议分别保留 raw_text、ocr_text、engine_version、language 和人工选择结果。页面旋转、中文语言包、印章与小字号都要有独立样本。
交付前验收的是证据链
至少保存文件摘要、页号、抽取路线和失败原因,并抽查文本在原页上的位置。把金额、表格与关键条款单独复核,不用“字符数很多”代替准确性。对于无文本的页面,既不要直接删除,也不要静默写入空字符串后宣告成功。
如果你在 OmniDoc 中组织文档处理与翻译工作,可以把这张分流清单作为输入检查附件:保留原稿,让处理结果有页号可追溯,再进入后续翻译和交付。这里演示的是独立的开源代码流程,不是对 OmniDoc 内部识别算法或效果的承诺。
参考与产品入口
PyMuPDF:OCR 操作指南
PyMuPDF:Page API
OmniDoc 产品入口
- 1 -
更新于 2026-10-10