Parse Scanned PDFs for RAG with EasyOCR: Free OCR Gives You Words, Not a Document
TL;DR · AI 摘要
EasyOCR 仅能提取文本,无法解析文档结构,而 Docling 可提取文本、段落、图表等结构信息,更适合 RAG 系统。
核心要点
- EasyOCR 仅能提取文本,无法解析文档结构。
- Docling 可提取文本、段落、图表等结构信息。
- 传统 OCR 无法满足企业级 RAG 系统对文档结构的需求。
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- 解析扫描 PDF 的挑战
- 传统 OCR 的局限性
- 仅提取文本
- 无法解析文档结构
- Docling 的优势
- 提取文本、段落、图表
- 适合 RAG 系统
金句 / Highlights
值得收藏与分享的关键句。
Traditional OCR gives you the text. It gives you nothing else. The rest is the layout problem, and the layout problem is the harder half.
EasyOCR is the OCR floor: line_df only, no layout. The rest of the family adds structure.
The whole article is a setup for the head-to-head on a real public-domain 1974 scan in section 5.
使用 EasyOCR 解析扫描的 PDF 以实现 RAG:免费 OCR 提供文字,而非文档 | Towards Data Science
大型语言模型
使用 EasyOCR 解析扫描的 PDF 以实现 RAG:免费 OCR 提供文字,而非文档
企业文档智能 [Vol.1 #5quinquies] – 同样的 1974 年扫描 PDF,两个引擎。EasyOCR 恢复文本。Docling 恢复文本 + 章节 + 图表。结构上的差距使一个输出可以用于下游,而另一个则只是一个扁平字符串。
Kezhan Shi
2026 年 6 月 19 日
15 分钟阅读
分享
照片由 Aakash Chary 提供,来源:Pexels。
本文是《企业文档智能》系列文章的解析指南,该系列文章通过四个模块构建企业 RAG 系统。第 5 篇(文档解析)使用 PyMuPDF(fitz)构建了解析器,当扫描的页面没有文本层时,该解析器返回空值。本指南则将引擎替换为 EasyOCR,这是一个免费的 OCR 包,可以恢复文本。这是该系列中唯一一个新引擎提供更少信息的情况,而非更多:它只恢复文本,而没有其他内容,这种差距就是教训。
本指南的位置:它扩展了第 5 篇(文档解析),在第 II 部分(四个模块)中,使用不同的解析引擎 – 图片由作者提供
扫描的 PDF 并不能通过“仅仅使用 OCR”来解决。OCR 步骤可以恢复文本,这是必要的,但不足以构建企业 RAG 管道。管道还需要文本周围的其他信息:页面边界在哪里,哪些行是章节标题,什么是图表,什么是表格行与自由段落。传统的 OCR(用于文本检测 + 文本识别引擎的术语,如 EasyOCR、Tesseract、PaddleOCR)只提供文本。它不提供其他任何信息。其余部分是布局问题,而布局问题是更难的一半。
本文具体地展示了这一区别。传统的 OCR 引擎是 EasyOCR:最简单、最快、免费的 JaidedAI 文本检测 + 识别库(Apache 2.0,声明在项目 LICENSE 文件中)。布局感知引擎是 Docling(第 5ter 篇;MIT 许可,声明在项目 LICENSE 文件中)。两者都可以对扫描页面进行 OCR。它们的区别在于如何处理结果。整篇文章为第 5 篇中对一个真实公共领域 1974 年扫描的正面交锋做铺垫。
EasyOCR 是 OCR 的基础:只有 line_df,没有布局。其余的模块添加了结构 – 图片由作者提供
1. 传统 OCR 的作用(以及不作用的地方)
传统 OCR 读取像素并返回文本矩形。其他所有内容,如章节、表格、图表、阅读顺序,都是引擎拒绝处理的布局问题。其背后的两个模型是文本检测(找到图像中包含文本的矩形区域)和文本识别(读取每个区域的像素并返回带有置信度的字符)。输出是每个检测区域的扁平列表,包含(边界框,文本,置信度)。
这就是 EasyOCR(或 Tesseract、或 PaddleOCR)所做的一切。引擎读取像素并返回文本矩形。一个双栏页面返回为一个由 y 坐标交错的左和右文本框的扁平列表;引擎不知道有两栏。一个表格返回为一个无法区分普通段落的断开单元格网格。一个图表标题只是一个文本框。页面标题、页面页脚、页边注释都会显示为框。
任何需要“这段文字是小节标题”或“这四个框是一个表格行”的内容都需要在基础模型之上增加一个布局模型。布局模型会读取OCR输出结果和页面图像,并对每个区域(标题、段落、表格单元格、图表、图注、页脚等)进行分类,并将其按阅读顺序进行分组。这就是Article 5bis(Azure DI)、Article 5ter(Docling)和Article 5quater(vision LLM)在OCR步骤基础上所添加的内容。如果没有布局模型,你得到的只是“OCR输出”,而不是“解析后的文档”。
2. EasyOCR:传统的OCR典范
EasyOCR是对“传统OCR”这一类别最清晰的演示。该库体积小(首次调用时缓存约150 MB的模型权重),免费,默认仅使用CPU,本地运行。整个库的API只有两个调用:为所需语言构建一个Reader,然后将图像传递给readtext。每个检测结果返回一个三元组:文本周围的多边形、识别出的字符串以及识别器自身的置信度。
import easyocr
import fitz
import numpy as np
reader = easyocr.Reader(["en"], gpu=False) # 首次调用时下载约150 MB
# 将扫描的PDF第一页渲染为EasyOCR可读的numpy数组
page = fitz.open("data/contracts/scanned_amendment.pdf")[0]
pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) # 2倍缩放 = 约144 DPI
img = np.frombuffer(pix.samples, dtype=np.uint8).reshape(
pix.height, pix.width, pix.n,
)
# 识别器:输入一张图像,输出每个检测到的文本区域的三元组
detections = reader.readtext(img)
for quad, text, conf in detections:
# quad = [[x0,y0], [x1,y0], [x1,y1], [x0,y1]] 以像素坐标表示
print(round(conf, 2), text)parse_pdf_easyocr封装了这个循环。它遍历PDF的每一页,将每一页渲染为numpy数组,调用readtext,将像素空间中的多边形转换回PDF坐标,并将检测结果打包到与其它解析器相同的表格字典中,相同的line_df,相同的parsing_summary,相同的下游消费者,只是只有那两个键包含数据。其他所有插槽(page_df、image_df、toc_df、span_df、object_registry、cross_ref_df)都返回空的DataFrame。这不是一个缺失功能的错误;这正是“传统OCR”所指的含义。
parsed = parse_pdf_easyocr(
"data/contracts/scanned_amendment.pdf",
languages=("en",), # 添加"fr"、"de"等以支持多语言扫描
render_scale=2.0, # 2.0 = 约144 DPI;字体较小时可提高
gpu=False, # 默认仅使用CPU;如果可用CUDA,可设为True
confidence_threshold=0.0, # 如需过滤低置信度的检测,可设置阈值
)
parsed["line_df"] # 每个检测的文本 + 边界框 + 置信度
parsed["parsing_summary"] # 方法、页数、行数、渲染比例
# 所有其他键(page_df、image_df、toc_df、span_df、object_registry、
# cross_ref_df)都是空的DataFrame;EasyOCR在这些位置没有内容可填。签名的kwargs是唯一的调节参数:
- languages:ISO-639-1代码的元组(如en、fr、de、zh等)。一个包含多种语言的语料库会为每种语言加载一个Reader;get_easyocr_reader中的@lru_cache会在多次调用之间缓存几个这样的Reader。
- render_scale:在将每一页栅格化时,每PDF单位的像素数。1.0是原生的(约72 DPI,通常太小)。2.0是正文文本的理想值。字体非常小时可提高到3.0;如果内存受限,可降低。
- gpu:默认使用CPU,因此该模块可以在任何机器上运行。CUDA在文本密集的页面上可提供3-5倍的速度提升。
- confidence_threshold : 过滤掉低置信度的检测结果。0.0 保留所有内容(该列被保留下来以便后续代码进行过滤),0.3 可以在降质扫描中去除大部分噪声。
3. line_df 的结构
来自 NIST FIPS 199 封面的示例行(美国政府作品,在美国属于公共领域,详见 NIST 版权声明),每行对应一个检测到的文本区域:页面坐标、OCR 识别的文本以及识别器自身的置信度评分。这就是全部的输出。
与 fitz 的 line_df 具有相同的列结构,再加上 EasyOCR 自动添加的一列置信度 – 图片由作者创作
结构特意保持简洁:
- text + bbox : 识别器的输出内容,每行对应一个检测到的文本区域。
- confidence : 0 到 1 之间的浮点数,EasyOCR 的自我评分。既可以作为过滤器(在噪声扫描中丢弃低于 0.3 的内容),也可以作为反馈信号(文章 8 的生成可以将低置信度段落标记给用户)。
- character_count : 为了与其他解析器对称而保留;在 EasyOCR 中,它只是 len(text) 的值。
- 无列 / 阅读顺序列。一个双列页面返回为一个扁平列表,左右框根据 y 坐标混合排列。
返回字典(page_df、image_df、toc_df、span_df、object_registry、cross_ref_df)中的其他所有键都是空的 DataFrame。调用 parsed["image_df"] 的消费者不会崩溃;它会遍历一个空的框架。
4. 传统 OCR 无法识别的结构缺陷,逐项说明
RAG 管道需要的五个结构性特征,无论识别模型有多大,传统 OCR 都无法生成。每一个都会破坏整个系列依赖的下游操作。
- 目录 / 章节。交叉引用解析(文章 11)和章节范围内的语料库检索(文章 17)都依赖于 toc_df。EasyOCR 返回零行。调度器无法处理“在第 3.2 节中回答”的问题,因为第 3.2 节没有边界。
- 页面内的单个图表。一份 30 页的扫描合同可能包含嵌入在正文中的六张图表截图。EasyOCR 将整个页面视为一张图像,返回图表周围的文本;图表本身从未成为行。需要检索“第 14 页的图表”的下游管道没有可操作的引用。
- 多列或多区域页面的阅读顺序。一个双列的科学论文页面返回为从上到下跨两列混合排列:左行 1、右行 1、左行 2、右行 2……生成内容会读取垃圾信息。边栏、脚注、页边注释都会泄漏到主流程中。
- 表格单元格。扫描的费用表或保费表返回为一个不连贯文本框的扁平列表(行标签在一个列中,值在另一个列中,单位标题在其他地方)。这种“该值属于该标签”的关系丢失。文章 5(文档解析)正是从这种失败模式开始(“解析器逐单元格遍历表格并将其连接成一个扁平字符串”)。布局感知引擎运行一个独立的 TableFormer 风格模型以重建行 × 列 × 标题。
- 字体 / 粗细 / 大小信号。OCR 恢复字符形状,而不是其排版编码。“此行是粗体 18pt”是布局引擎从页面渲染中读取的信息;EasyOCR 丢弃了这些信息。标题、强调、脚注失去了用于分类的提示。
选择第三个因素,即阅读顺序,因为它会悄无声息地破坏答案。EasyOCR 按照文本框的 y 坐标对它们进行排序。在双栏页面上,两栏位于相同的高度,因此文本框会交错返回:左栏的第一行、右栏的第一行、左栏的第二行,依此类推。文本以锯齿状方式阅读,生成的引文也呈锯齿状。
没有布局模型时,文本框按照 y 坐标排序,因此双栏页面会交错成锯齿状 – 图片由作者提供
一句话:OCR 步骤恢复文本,布局步骤恢复使文本可用的结构。第 5ter 篇(Docling)和第 5bis 篇(Azure DI)在相同的 OCR 基础上增加了布局步骤。第 5quater 篇(视觉 LLM)将两者合并为一次调用。EasyOCR 仅停留在 OCR 步骤。
5. EasyOCR 与 Docling 在真实扫描 PDF 上的对比
在相同的 1974 年扫描文件上,Docling 提取了更多的字符(5,423 个 vs 4,952 个),页面边界、11 个目录条目和 4 个图像区域。EasyOCR 提取文本矩形并停止。这两个引擎在字符级别上达成一致,都使用相同识别器类别的准确率进行 OCR,但 Docling 的布局步骤将 OCR 输出转换为文档。
有趣的是,这次比较不是与 fitz(fitz 在扫描上返回零)进行,而是与下一个引擎进行比较:Docling,来自第 5ter 篇的本地布局感知解析器。比较看起来比实际更清晰:Docling 的默认 OCR 后端本身就是 EasyOCR。相同的识别器读取相同的像素;差异在于 Docling 围绕它构建的一切。
测试用例是一个真实的公共领域扫描文件:karg74.pdf 的第 1–5 页,1974 年美国空军 MULTICS 安全评估(Karger & Schell,ESD-TR-74-193 第 II 卷)。NIST 在他们的早期计算机安全论文档案中托管了该文件;该作品作为美国空军军官的成果,已进入公共领域。该 PDF 内置了 Adobe 的“Paper Capture”OCR 层,但我们忽略了它,两个引擎都从页面图像重新进行 OCR,这是嵌入的 OCR(如果存在)不可靠时的现实场景。
真正的比较。两个引擎都对页面图像重新进行 OCR;Docling 添加了布局 – 图片由作者提供
两栏讲述了不同的故事。
EasyOCR(左)。更快(59.7 秒 vs 134.4 秒,没有布局模型需要加载和运行),将识别器的置信度作为一列输出(在这次扫描中平均为 0.81),产生更多的行级检测(346 个框),因为页面上的每个文本区域都变成一行。没有结构:没有 page_df、没有 toc_df、没有 image_df。输出是文本框形式的文本,仅此而已。
Docling(右)。更慢(计算量是前者的 2.3 倍),将检测结果合并为 105 行/段落,而不是 346 个框,没有置信度列。结构上的优势是真实的:5 行 page_df、11 个 toc_df 条目(Docling 的布局模型将标题分类为章节)、4 行 image_df(在页面内检测到的图像作为独立对象)。在包含表格的 PDF 上,差距进一步扩大,Docling 的 TableFormer 可以识别行 × 列 × 标题,而 EasyOCR 完全无法做到。第 5ter 篇详细介绍了表格情况。
在这次 1974 年的扫描(Karger → “Karger” 由 EasyOCR,干净页面上 “Karger” 由 Docling)上,两个引擎在字符级别的错误率相似(退化区域在两者上产生相似的噪声,“Laboralory”,“und” 而不是 “and”)。Docling 内部的 OCR 引擎(根据安装情况为 EasyOCR 或 OnnxTR)并不比直接调用 EasyOCR 更神奇地准确。Docling 增加的是它如何组织 OCR 输出,而不是如何进行 OCR。
在企业级 RAG 应用中,选择 Docling 通常是正确的决定。2.3 倍的计算开销只需在摄入阶段支付一次(来自第 5 篇文章(文档解析)的解析缓存会永久复用结果);而结构化收益(目录、图表、表格单元格、阅读顺序)则会在每次下游查询中得到回报。Docling 唯一不提供的是 EasyOCR 的行级置信度信号,这种信号在大多数情况下都不值得牺牲文档的段落、图表和表格。
6. 传统 OCR 依然有其价值的场景
EasyOCR 是这个系列中的应急方案:对文档的可见性较低,依赖项更简单,当限制是操作性而非教学性时,部署速度更快。有四种特定场景仍然为 EasyOCR 保留了空间。
- 收据类文档。一页的发票或收据,没有标题、没有段落、没有图表、没有合并单元格的表格。布局非常简单;识别器就是整个任务。添加 Docling 的 2 倍计算开销,只是为了获得文档本身没有的结构。
- 按区域置信度作为生成反馈信号。生成(第 8 篇文章)可以从引用段落中读取行的置信度,并在答案依赖于 OCR 置信度为 0.3 的部分时提醒用户。Docling 不提供该列。对于依赖该信号的流水线,EasyOCR(或与 Docling 一起运行 EasyOCR)是答案。
- 大规模非拉丁文脚本。EasyOCR 提供了 80 多种语言的预训练模型,包括中文、日文、韩文、阿拉伯文、印地文、西里尔文。在撰写本文时,Docling 的 OCR 堆栈在非拉丁文覆盖范围上更为有限。
- 操作限制阻止使用 Docling。公司 SSL 检查会破坏 HuggingFace 模型下载。Windows 在没有开发人员模式的情况下会阻止符号链接。生产镜像有严格的依赖预算。无法传输 3GB 布局权重的隔离部署。在所有这些情况下,EasyOCR 的 150MB 缓存模型 + CPU 推理都能通过,而 Docling 则无法通过。你看到的文档内容更少,但你至少能看到一些内容。
在这些情况之外:默认使用 Docling 进行扫描,Azure DI 用于合规云环境,当文档包含手写体、签名或非文本语义层时使用视觉 LLM。第 10 篇文章中的自适应解析调度器会自动进行路由。
7. 结论
OCR 恢复字符。布局恢复的是使字符有用的部分,包括段落、图表、表格单元格和阅读顺序。扫描的默认引擎是能同时完成这两项任务的引擎。第 5 篇文章中的完整家族也按照同样的轴线排列:
EasyOCR 位于 OCR 基础层(仅 line_df);家族中的其他引擎则在 OCR 之上增加了一个布局步骤 – 图片由作者提供
EasyOCR 是 OCR 的基础层,当你只停留在识别字符,而从未问过“它们在页面上的位置”时,你得到的就是 EasyOCR。这个问题很重要。“在页面上的位置”决定了你得到的是文本框列表还是解析后的文档。第 10 篇文章中的调度器(自适应解析)会根据每一页选择合适的引擎;本文的存在是为了让调度器知道,当它选择便宜的引擎时,它放弃了什么。
8. 参考资料和进一步阅读
EasyOCR 是 2026 年最易获取的传统 OCR 引擎;PaddleOCR(百度)和 Tesseract(Google,已有数十年历史)与它同属一个家族。顶部的布局步骤是区分“OCR”与“文档解析”的关键;Docling(第 5ter 篇文章)和 Azure DI(第 5bis 篇文章)分别在本地硬件和云端添加了这一功能。右侧的横向阅读涉及布局文献(Smock 等人,2022 年关于表格结构;Auer 等人,2024 年关于完整布局级联)以及适用于非拉丁语脚本的替代 OCR 引擎。
与文章方向一致:
- JaidedAI, EasyOCR。本文所描述的库,包括 80 多种语言模型包。
- PaddleOCR(百度)。同级别的传统 OCR 引擎;中文覆盖率更好,但同样存在布局盲区。
- Tesseract OCR。已有数十年历史的经典工具,仍被广泛部署;与 EasyOCR 相同的架构设计(检测 + 识别,无布局处理)。
不同角度,不同背景:
- Auer 等人,Docling 技术报告,IBM 研究 2024 年(arXiv:2408.09869)。一个布局感知的级联系统,将 OCR 输出转换为解析后的文档;本文第 5 节的比较基准。
- Smock、Pesala、Abraham,PubTables-1M / Table Transformer(TATR),CVPR 2022 年(arXiv:2110.00061)。单元格级表格提取的研究背景,也是 EasyOCR 最大的缺失能力。
系列文章的早期内容:
- 文档智能:系列简介。系列逐步构建的内容及顺序。
- 基线企业 RAG,从 PDF 到高亮答案。一个四步骤的完整流程:输入 PDF,输出高亮答案。
- 嵌入向量并非魔法:RAG 检索的可预测失败模式。嵌入相似性在哪些情况下有效(同义词、拼写错误、改写),在哪些情况下会可预测地失效(未知术语、否定、术语与答案的相关性),以及如何使用它。
- 重排序器也并非魔法:何时使用交叉编码器层值得付出代价。交叉编码器相对于双编码器嵌入向量的额外优势,以及何时值得承担延迟成本。
- RAG 并不是机器学习,而机器学习工具箱解决的是错误的问题。为什么块大小调整和微调优化的是错误的目标;应根据问题类型进行路由。
- 从正则表达式到视觉模型:哪种 RAG 技术适合哪种问题。两个维度,文档复杂性和问题控制,用于选择每种情况下的技术。
- 我们在生产中反复看到的 10 个常见 RAG 错误。10 个生产错误,按步骤组织,并附有每个错误的修复方法。
- 超越 extract_text:驱动 RAG 质量的 PDF 的两个层次。解析步骤的第一部分:文档的性质、信号和摘要。
- 停止从 PDF 返回扁平文本:RAG 所需的关系结构。解析步骤的第二部分:每个下游步骤都会读取的关系表。
撰写者
查看 Kezhan Shi 的所有文章
文档处理
,
OCR
解析
RAG
分享这篇文章
- 在 Facebook 上分享
- 在 LinkedIn 上分享
- 在 X 上分享
Towards Data Science 是一个社区出版物。提交你的见解,以触达全球受众,并通过 TDS 作者支付计划获得报酬。
更新 href 为你的实际提交 URL
为 TDS 写作
✦ 结束 CTA ✦