Making a PDF’s Images Searchable for RAG, Without Paying to Read Them All
TL;DR · AI 摘要
无需对PDF中所有图片进行模型调用,即可实现图像内容的可搜索性,通过分层过滤和OCR技术降低成本。
核心要点
- 使用图像大小和形状作为初步过滤条件,可排除无检索价值的图片。
- 通过OCR和视觉模型分层处理,仅对高价值图像进行深度解析。
- 根据文档上下文动态选择需要处理的图像,提升检索效率。
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- PDF图像可搜索性处理
- 图像定位
- image_df定位所有图像
- 图像大小和形状过滤
- 图像处理方法
- 低成本过滤器
- OCR处理
- 视觉模型处理
- 上下文驱动选择
- 根据文档内容选择图像
- 根据查询内容选择图像
金句 / Highlights
值得收藏与分享的关键句。
Too small. An image whose shortest side is a few dozen pixels, or whose total area is below a small floor, is an icon or a bullet, not a figure.
A body line that reads 'Figure 3 below shows...' is the cue to read that figure with a vision model, and not its neighbours.
Choosing which images to pay for, per document and per query, is adaptive parsing.
在不阅读所有内容的情况下,使 PDF 中的图像对 RAG 可搜索 | Towards Data Science
大型语言模型
在不阅读所有内容的情况下,使 PDF 中的图像对 RAG 可搜索
企业文档智能 [Vol.1 #5sexies] – image_df 告诉你每张图片的位置。将少数重要的图片转换为可搜索的文本是一项单独的、按成本排序的任务
Kezhan Shi
2026 年 6 月 20 日
17 分钟阅读
分享
照片由 Roman Kraft 提供,来源:Unsplash。
本文是《企业文档智能》系列的一篇文档解析配套文章,该系列通过四个模块构建一个企业级 RAG 系统。它扩展了第五篇文章(文档解析)中的一个表格:image_df,该表格在不阅读任何图片的情况下定位 PDF 中的每一张图片。本部分构建了阅读工具箱:一个按成本排序的级联(一个廉价的过滤器、一个类型检查、传统的 OCR、一个视觉模型),将少数值得付费的图片转换为可搜索的文本。
该配套文章的位置:它扩展了第五篇文章(文档解析),在第二部分(四个模块)中,阅读解析器仅定位的图片 – 图片由作者提供
解析模块为你提供 image_df:PDF 中每张图片一行,包括其页面、边界框、大小、内容哈希。这可以定位每一张图片。但它没有说明任何一张图片的内容。对于检索来说,这与没有这些图片是一样的:一个边界框不是用户可以搜索的东西,而图片的文本槽(描述应该存在的地方)是空的。
本能的反应是将视觉模型应用于每一张图片,然后完成任务。这是错误的默认做法。一份真正的文档中充满了图像,这些图像对读者来说没有任何搜索价值:每一页页眉中的公司标志、用 2 像素高的图片绘制的水平线、项目符号字符、装饰性横幅。用视觉 LLM 对这些图像进行描述,相当于让模型重复描述一个标志三百次。
因此,任务分为两部分。第一部分是将图像转换为文本的方法,以及每种方法的成本:一个廉价的过滤器、一个类型检查、传统的 OCR、一个视觉模型。第二部分是,在给定的运行中,哪些图像实际上值得投入资源。第二部分由上下文驱动。一段正文内容为“下面的图 3 显示了……”是使用视觉模型读取该图的提示,而不是其邻居;所提出的问题进一步缩小了范围。本文介绍了这些方法,并展示了每种方法返回的内容,按成本排序。选择每份文档和每条查询中要付费的图像,是自适应解析,它有自己的一篇文章(第 10 篇)。在这里,我们构建工具箱。
一张提取的图像输入,输出一个可搜索的描述,使用最便宜的方法来读取它 – 图片由作者提供
1. 大多数图像不值得调用模型
第一步不花费任何成本。在任何 OCR 或视觉调用之前,一个廉价的过滤器查看 image_df 中已有的信号,加上一些像素统计信息,并丢弃那些没有检索价值的图像:
- 太小。最短边只有几十像素,或总面积低于某个小阈值的图像,是一个图标或项目符号,而不是图表。一个尺寸阈值可以去除大部分这样的图像。
- 形状错误。非常长且非常细的图片是一个规则或分隔线,而不是内容。一个宽高比保护可以捕捉到这些。
- 在所有地方重复。文档中大多数页面上具有相同内容哈希的图像属于装饰元素:页眉标志、页脚标记、水印。统计图像哈希出现在多少页上,可以将其标记为装饰,而不是信息。
is_worth_analyzing 按照每张图像应用大小和形状规则,而 flag_worth_analyzing 首先根据内容哈希从每页中推导出重复频率,然后在 image_df 中添加一个 worth_analyzing 列。这两个函数都位于 docintel.parsing.pdf.images 中。这些阈值是故意设置得较为宽松的:误判保留一张图像只会增加一次模型调用的成本,而误判丢弃一张图像则会导致内容丢失且无法追溯,因此在有疑问时,过滤器会保留图像。那些过于大而无法通过大小测试的扁平、无内容图像(比如一个纯色面板)不会被强制通过此步骤;它们会在下一步被识别为装饰性图像并被跳过。
输入:image_df(加每张图像的像素统计信息)。输出:带有 worth_analyzing 标志的相同表格。
在一份典型的报告中,仅此一步就能在任何模型运行之前移除大部分图像。剩下的那少数几张图像才是真正具有意义的。
2. 图像属于什么类型?
通过过滤器的图像并非都以相同方式处理。一张表格的截图是文本:传统的 OCR 可以廉价且精确地读取它。而一张折线图则完全不是文本;它的含义在于坐标轴和趋势,只有视觉模型才能将其转化为文字。将图表发送给 OCR 只能返回几个散落的坐标轴标签;而将截图发送给视觉模型则需要为 OCR 免费完成的任务支付图表级别的费用。
因此,第二步将每张保留的图像分类为以下类型之一:
- 装饰性:空白或几乎统一的面板。跳过。
- 文本:截图、扫描区域、以图像形式呈现的表格。使用 OCR 读取。
- 图表/示意图/照片:含义是视觉性的。使用视觉模型读取。
classify_image 根据廉价的像素信号返回一个 ImageType:像素变化的程度、饱和度、图像中接近白色背景的部分比例、边缘的密集程度。一个接近统一的面板是装饰性图像。这个测试值得深入探讨,因为显而易见的版本是错误的:你不能通过计算颜色数量来检测空白面板。一个真正的“全黑”或“全白”区域从不会像素级完美;传感器噪声和 JPEG 压缩会使其拥有数百种几乎相同的颜色,因此颜色计数会直接忽略它。在空白面板上,无论噪声如何,像素值的分散度(标准差)会保持接近零。低分散度意味着空白,无论颜色数量如何,因此这是信号。黑色油墨在白色页面上,接近零的饱和度和真实的笔画结构,是文本。高度饱和、无白色边距的图像是一张照片。其余所有情况,所有不确定的情况,都会被归类为图表。
注意列表中没有包含的内容:一个决定“这看起来像一个标志”的步骤。这是有意为之,与空白面板的教训相同。一个标志可以是两种纯色,白色背景上的黑色文字标志,或者带有柔和边缘的全彩色渐变。统计颜色可以捕捉到第一种情况,却会错过第二种情况,更糟糕的是,双色测试还会捕捉到你想要阅读的真实文本的二值化扫描。外观无法告诉你它是一个标志。行为才能说明这一点:一个标志是 chrome(浏览器界面元素),因为它会重复,每个页面标题中都有相同的标志。这个信号已经在过滤器中运行过,它会丢弃那些内容哈希值在多个页面中重复出现的图片,不管它有多少种颜色。只出现一次的标志,比如封面页上的一个标志,不值得特殊处理;它和其他内容一样被处理,文字标志交给免费的 OCR,图形则交给单次视觉调用。贯穿始终的规则是一样的:只跳过你确定是空白或 chrome 的内容,其余内容都要读取,因为错误的跳过会导致内容无声丢失。
这种传递到图表的处理方式是另一个重要的设计选择。仅凭廉价信号来区分图表、示意图和照片是不可靠的,因此分类器并不试图聪明地处理:它只在确定图片是干净的单色文本时,才将图片传递给廉价的 OCR,其余所有情况都发送给视觉模型,由视觉模型来读取图表、示意图、照片以及其中可能包含的任何文本。这种偏差是有意为之的不对称。错过的 OCR 快捷方式只消耗一次视觉调用;对示意图运行 OCR 会返回一些零散的坐标轴标签和无意义的内容。因此,当存在疑问时,分类器会选择支付视觉调用的费用。分类本身保持廉价,不需要模型调用,因为它必须比它试图避免的分析更便宜。
输入:通过过滤器的图片。输出:它的 ImageType 。
3. 级联:能够读取它的最便宜方法
类型决定方法。METHOD_BY_TYPE 将每种类型映射到三种操作之一,按成本排序,并且 describe_figure 根据该操作进行处理。对于你实际在文档中遇到的情况,整个决策都可以用一张表来表示:什么捕捉了图片,它的成本是多少,以及你得到什么结果。
对于你在实际文档中遇到的每一种图片类型,从免费到付费 – 图片由作者提供
从上到下阅读这张表,你就能按顺序理解级联的处理方式。前三行根本不会到达任何模型:过滤器会根据大小、形状或重复性将它们丢弃。下一行会被分类器识别为一个空白面板并跳过。只有底部的五行会消耗资源,而这五行中只有真正的文本图片会走免费路径。其余的图片都会到达视觉模型,这正是你希望你的钱被使用的地点。
注意:横向的图表。一个宽表格或横向的图表通常会旋转90度以适应纵向页面。这种旋转很少出现在你首先查看的地方:页面的旋转标志保持为0,而角度则位于图片自身的放置矩阵中。如果直接渲染,图表会以侧向的方式传递给 OCR 或视觉模型,这样 OCR 会返回噪声,而视觉模型会以错误的置信度读取它,且没有任何警告表明它遇到了困难。因此,级联会读取放置角度并在任何方法看到它之前进行反向旋转:自动、精确,不需要猜测方向。唯一剩余的情况是扫描图像中已经将旋转嵌入像素中,没有矩阵可供读取;在这种情况下,OCR 分支会尝试四分之一旋转并保留得分最高的结果。
3.1. 跳过:为噪声支付零费用
装饰性:不调用。一个空白或几乎统一的面板会保留其空的文本插槽。结合过滤器已经丢弃的图片(太小的、形状不正确的、重复的Chrome),这是大多数干净文档的图片最终会到达的地方,这正是其目的所在。
3.2. 用于文本图像的经典OCR
文本:截图、扫描表格、真正由渲染文本构成的图示。经典OCR可以在毫秒内本地读取,且无需费用。该系列使用EasyOCR(docintel.parsing.pdf.easyocr);Tesseract是另一个常见选择。OCR在干净的打印文本上是精确的,且从不会编造词语,这正是当图片是文本时你想要的效果。其配套文章(第5 quinquies篇)全面涵盖了OCR作为解析后端的内容;在这里,它是级联中的一条分支。
问题在于手写体。手写的笔记在分类器看来像是文本,但经典OCR是基于印刷体训练的,它将连笔字视为一串猜测。解决方法是让OCR报告其确定程度。EasyOCR会为每一行返回一个置信度分数,因此describe_figure会读取文本及其平均置信度:置信度高的读取结果会直接返回,置信度低的读取结果被视为失败尝试,图片会传递给视觉模型,视觉模型处理手写体的效果要好得多。同样的路径也涵盖了分类器将非文本图像误判为文本的罕见情况。因此,OCR分支并不是“盲目信任OCR”;而是“尝试免费的阅读器,只有在它确定时才保留其答案,否则付费使用视觉模型”。
3.3. 用于图表、图示和照片的视觉LLM
图表、图示、照片:唯一真正意义上具有视觉意义的图片。视觉模型查看图片并写出简短的描述,例如“自2022年以来商品价格的折线图,第三季度后先上升后趋于平稳”,“Transformer架构,由N个堆叠层组成的编码器,馈送给解码器”。这句话是文本,因此检索最终可以匹配它。这是任何文本解析器都无法做到的一件事,而且这是最昂贵的步骤,因此整个级联流程的存在是为了确保只有这些图片到达这里。视觉调用本身通过docintel.core.analyze_image进行,该函数是系列中所有模型调用的唯一位置(与llm_parse和llm_chat并列);其成本是第5 quater篇(视觉阅读)的主题。
分类器已经知道类型,因此提示是针对该类型进行调整,而不是使用一个通用的“描述这张图片”。对于图表,会询问其坐标轴、单位和趋势;对于图示,会询问其组件及其连接方式,以及所有标签的转录;对于以图像形式呈现的表格,会要求其以Markdown格式返回行;对于照片,会询问其展示的内容。正确的问题会引导出正确的答案:向图表询问趋势,你会得到趋势;向它询问“描述这张图片”,你会得到关于颜色的一句话。调用者仍然可以传递一个显式的提示来覆盖类型特定的提示,这就是项目范围或用户编辑的指令如何流动的方式。
输入:一个已分类的图片。输出:一个简短的描述,或者None表示跳过。
4. 将描述写回
只有当检索可以找到描述时,描述才有用。图片已经在line_df中有一个行插槽(图片位于页面上的某个位置,因此占据一行,有一个空的文本单元格,如第5B篇(关系数据模型)所述)。级联会将描述写入该单元格。describe_image_df会向image_df添加一个描述列,调用者会将其重新连接到图片的行上。
效果是,“架构图”或“收入图表”现在可以通过与任何其他行相同的关键词和嵌入路径检索到正确的页面。下游的任何内容都不需要知道文本来自图片。
该增强功能在设计上是逐步进行的。你可以选择在解析小语料库时立即运行级联处理,或者延迟处理,仅在给定运行实际需要的图片上运行。文本槽位在被填充之前是空的,而填充它不会改变合同:它仍然是一行、一行文本值。何时填充它,是本文留给自适应解析(文章10)的开放问题:与其一开始就读取所有图表,不如先读取便宜的文本,然后在该文本中引用的图表(例如“下面的图3显示了收益”)会触发对所引用图表的视觉调用。此处的方法就是该策略将调用的;策略本身是下一篇文章的内容。
整个级联处理作为一个调用进行。将它传递给从parse_pdf得到的image_df以及它被解析的pdf_path,然后读取回相同的帧,其中级联处理填充了三个新列。
parsed = parse_pdf("data/paper/1706.03762v7.pdf") # image_df 定位图片
enriched = describe_image_df(parsed["image_df"], pdf_path="data/paper/1706.03762v7.pdf")
# describe_image_df 向 image_df 添加了三列:
enriched[["page_num", "worth_analyzing", "image_type", "description", "prompt"]].head()
# worth_analyzing : 便宜过滤器的判断结果 (True/False)
# image_type : "decorative" | "text" | "chart" | "diagram" | "photo" | None
# description : 写入图片行槽位的可搜索文本
# prompt : 发送给视觉模型的指令 (OCR 时为 None / 跳过)这也是用户可以看到并纠正级联处理的部分。下图是桌面文档应用程序在NIST AI 100-1(AI风险管理框架,美国政府作品,公共领域)上运行相同流程的截图:图片标签列出了解析器定位到的每一张图片,所选图表带有gpt-4.1为其撰写的描述,且描述可以进行编辑。每张图片的控制选项可以重新运行OCR或强制视觉模型处理,当便宜路径处理错误时。
级联处理向用户展示:每一张定位到的图片、写入文档模型中的描述,以及每张图片的控制选项,可以重新运行OCR或强制视觉模型处理 – 图片由作者提供
5. 成本和延迟:按图片付费,而不是按页面付费
级联处理的整个目的就是让成本与价值匹配。便宜的过滤器和分类器在每张保留的图片上运行,但成本几乎可以忽略不计。OCR是本地的且免费的。视觉模型,也就是真正产生费用和耗时的项目,只在图表、示意图和照片上运行,而这些在大多数企业文档中只占图片的一小部分,以及页面的极小一部分。
另一种方法,即使用视觉模型为每张图片添加标题,无论图片是标志还是图表,每张图片的费用都是一样的,而大多数图片都是标志。级联处理将原本每张图片都使用视觉模型的费用,替换为一个过滤器、一个便宜的分类器,以及只在没有任何其他方式能够读取图片时才调用视觉模型。对于每页有一个标志和两个真实图表的报告,只需要两次视觉调用,而不是几十次。
同一张图片也永远不会被收取两次费用。过滤器已经去掉了在大多数页面上重复出现的 chrome 元素,但真正的图表仍可能出现在少数页面上(例如参考图示、重复的展品)。由于级联是基于内容哈希进行的,因此一张在十页中出现的图片只会被读取一次,其余九次都会复用其描述。无论图片出现多少次,只使用一张图片,一次模型调用。
6. 结论
image_df 定位每一张图片;它不会读取任何图片。读取图片是另一个独立的模块,本文介绍了其方法,按成本排序:免费去除噪声,廉价分类剩余内容,使用 OCR 读取干净文本,并将视觉模型保留用于图表和图示,这些内容的意义是真正视觉化的。每种方法都会将结果存入图片的文本插槽,从此之后,一张图片就只是另一条可搜索的文本行。本文刻意没有决定在给定的处理过程中应读取哪些图片:提前读取所有图表通常不是你想要的,而根据上下文进行选择,让周围的文本和问题来决定,这是自适应解析(文章 10)。先提供工具箱,再制定策略。
参考资料和进一步阅读
- 文章 5(解析)和文章 5B(关系表)介绍了 image_df 以及描述被写回的文本插槽。
- 文章 5 quater(视觉阅读)涵盖了视觉-LLM 后端及其成本。
- 文章 5 quinquies(EasyOCR)涵盖了经典 OCR 作为解析后端的内容。
- 文章 10(自适应解析)是本文推迟决定的地方:在特定运行中应读取哪些图片,从廉价文本逐步升级到仅在上下文需要时才进行视觉调用。
系列早期内容:
- 文档智能:系列简介。系列构建的内容,一块一块地,以及构建顺序。
- 基线企业 RAG,从 PDF 到高亮答案。四块砖的完整流程:PDF 输入,高亮答案输出。
- 嵌入向量并非魔法:RAG 检索的可预测失败模式。嵌入相似性在哪方面胜出(同义词、拼写错误、改写),在哪方面可预测地失效(未知术语、否定、术语与答案的相关性),以及如何使用它。
- 重排序器也不是魔法:交叉编码器层是否值得成本。交叉编码器相对于双编码器嵌入向量增加了什么,以及何时值得其延迟。
- RAG 不是机器学习,而机器学习工具箱解决的是错误的问题。为什么块大小扫描和微调优化了错误的东西;应根据问题类型进行路由。
- 从正则表达式到视觉模型:哪种 RAG 技术适合哪种问题。两个轴,文档复杂性和问题控制,为每种情况选择技术。
- 我们在生产中反复看到的 10 个常见 RAG 错误。十个生产错误,按模块逐一组织,附有每个错误的修复方法。
- 超越 extract_text:驱动 RAG 质量的 PDF 的两个层次。解析模块的第一部分:文档的性质、信号和摘要。
- 停止从 PDF 返回平铺文本:RAG 需要的关系结构。解析模块的第二部分:每个下游模块读取的关系表。
撰写人
查看 Kezhan Shi 的所有文章
图像处理
,
解析
RAG
视觉语言模型
分享这篇文章
- 在 Facebook 上分享
- 在 LinkedIn 上分享
- 在 X 上分享
Towards Data Science 是一个社区出版物。提交您的见解,以触达我们的全球受众,并通过 TDS 作者支付计划获得报酬。
将 href 更新为你的实际提交 URL
为 TDS 撰写文章
✦ 结束 CTA ✦