Skip to content

[任务] 图片和扫描件的文字识别 #47

Closed

Description

@JunieXD

关联与状态

路线图 #2,一期。状态:已完成,图片附件的识别是 @FengshuoChen 的 PR #74,选了 small;扫描的 PDF 在 PR #103,由 pypdf 取出每页的图片识别,不另加渲染的库,都已合并。能不能让 DeepSeek 直接读图,另在 #102 做实验。

目标

不少通知把报名表、日程放在图片或扫描的 PDF 里,不识别出文字,抽取就看不到。这个任务给附件里的图片和没有文字层的 PDF 做文字识别(总体设计 §7.4)。

已经有的

  • 项目骨架:crawler/ 是 Python 3.14 + uv 的项目,装依赖 uv sync,跑全部检查 ./check.sh(格式、lint、Pyright 严格模式、测试)。命令见 AGENTS.md。
  • 一轮的流程:crawl/rounds.py 的 crawl_source 已经把一个来源的一轮串好:读列表 → 初筛 → 读正文和附件 → 抽取 → 交结果。四步作为 Steps 传进来,签名和交接的数据写在那里,测试的写法见 rounds_test.py。抓不成时抛 FetchError 或 StepError,流程会把原因记进抓取记录(开发约定 §2)。
  • 请求层:所有对来源网站的请求都经过 crawl/fetch.py 的 Fetcher,它管 UA、同一网站一次一个请求和间隔,不要自己用 httpx 发请求。
  • 和网站交接的类型:crawl/contract.py 由网站的接口定义生成,不手改(接口 §6)。
  • 候选的库和实测:RapidOCR,它运行 PaddleOCR 的 PP-OCR 模型,中文有 PP-OCRv6 的 tiny、small、medium 等几档,默认 small。small 识别一张 20 行的中文页面约 1 秒,进程峰值约 690MB(总体设计 §5.1、§5.2)。
  • 资源:抓取节点只有 4 核、3.6GB 内存,一次只识别一张(§16)。

范围

  1. 在 parse 里给图片附件和没有文字层的 PDF 做文字识别,结果作为附件的文字:
  2. 一次只识别一张,模型加载一次、多次使用;识别时间和内存记日志。
  3. 用自己做的图片测试,比如用 Pillow 画几行字。实测速度和内存写进总体设计 §5.2。

建议的第一步

  1. 用来源里 15 到 20 张真实图片和几页扫描的 PDF,比较 PP-OCRv6 tiny、small、medium,看关键信息(日期、时刻、网址、邮箱)认得对不对,以及在 4 核、3.6GB 下的耗时和内存,做法见下面的评论。网址列在这里,图片不提交。加依赖用 uv add,先在这里说一声。
  2. 先做图片:用 Pillow 画几行字做测试,写识别的函数和测试,开一个 Draft PR。扫描的 PDF 等 [任务] 读正文和附件 #40 选定库后再接。

验收标准

  • 图片附件和扫描的 PDF 能取出文字,同时只识别一张
  • 用真实图片比较过几档模型,结果写进总体设计 §5.2,选定的写进 §5.1
  • 测试用自己生成的图片,./check.sh 通过

Activity

  1. added
    task可以认领的具体工作
    crawler抓取节点:抓取、初筛、抽取
    on Oct 27, 2026
  2. FengshuoChen commented on Oct 28, 2026

    @FengshuoChen
    Contributor

    认领!

  3. JunieXD commented on Oct 28, 2026

    @JunieXD
    ContributorAuthor

    @FengshuoChen 谢谢认领,已经指派给你了。

    原来写着要等 #40,但 #40 还没人认领,怕你卡住,正文改了一下:

    • 图片现在就能做:在 parse 里写一个从图片取出文字的函数,用 Pillow 画几行字做测试;[任务] 读正文和附件 #40 读附件时对图片调用它。
    • 扫描的 PDF 后接:要先把每页渲染成图片再识别,用哪个库渲染跟着 [任务] 读正文和附件 #40 选定的 PDF 库定(不用 AGPL 许可的 PyMuPDF)。

    建议先在本地试 RapidOCR 的效果和内存,结果贴在这里;加依赖前也在这里说一声。

  4. FengshuoChen commented on Oct 31, 2026

    @FengshuoChen
    Contributor

    计划先完成图片附件的文字识别和测试;扫描版 PDF 等 #40 选定 PDF 库后再接。

    参照规则

    • 按 [任务] 图片和扫描件的文字识别 #47 的范围和验收标准,在 crawler/src/crawler/parse/ 实现文字识别。
    • 按 docs/design.md §5.1、§5.2、§7.4 和 §16 处理选型、实测与内存限制。
    • 按 docs/development.md 的模块、日志和测试约定编写;遵守 AGENTS.md 和 CONTRIBUTING.md。
    • 附件下载和接入一轮的工作属于 [任务] 读正文和附件 #40;本任务先提供供其调用的图片识别函数,不手改生成的 crawl/contract.py。

    实现思路

    1. 先用 RapidOCR 和 ONNX Runtime 试自制中文图片,记录识别效果、首次及重复调用耗时、进程内存峰值。一次本地初试中,21 行中文图片识别出 21 行;同一模型实例首次调用约 3.49 秒,重复调用约 1.42 秒。三次识别期间每 10 毫秒采样进程 RSS,最高约 762 MiB。测试环境为 Windows、Python 3.14.5、RapidOCR 3.10.0 和 ONNX Runtime 1.31.1;这不是 4 核、3.6 GB 抓取节点上的实测结果。
    2. 在本 Issue 说明拟新增的 RapidOCR、ONNX Runtime 及内存测量依赖后,再用 uv add 加入。图片识别函数接收图片字节,按识别顺序返回文字行,不猜测或改写原文。
    3. 在进程内复用 OCR 模型,并串行执行识别,保证同一时刻只处理一张图。日志记录每次识别的耗时和进程内存,不记录图片或识别正文。
    4. 将选型和注明环境、测量方法的实测结果写入 docs/design.md §5.1、§5.2。扫描版 PDF 的逐页渲染跟随 [任务] 读正文和附件 #40 的选型,不使用 AGPL 许可的 PyMuPDF。

    测试和交付

    • 用 Pillow 现场生成测试图片,覆盖中文、数字与时间,以及没有文字的图片;自动测试不连接外网。
    • PR 额外附上输入图片 test.jpg 和由最终函数生成的完整 OCR 文本 test-output.txt,供直接核对实际效果;这份真实通知样例不作为自动测试数据。
    • 测试文件与实现放在 parse/ 中,命名为 *_test.py;提交前运行完整 crawler/check.sh。
  5. JunieXD commented on Nov 6, 2026

    @JunieXD
    ContributorAuthor

    @FengshuoChen 抱歉回晚了:10-31 到 11-04 我的 GitHub 账号被停用,这几天的回复都没发出来。图片的函数写得很干净:模型只加载一次,一次只识别一张,耗时和内存都记了日志,测试也是现场生成的图片。

    方向:先用我们来源里的真实图片比较几档模型,按我们的需要选定,再继续写。这是我们 Issue 写漏了:「建议的第一步」只写了「试 RapidOCR」,而总体设计 §5.1 要的是「用真实的附件比较后定」。正文已经按下面的做法改过了。

    RapidOCR 不是一个模型,它是运行 PaddleOCR 那套 PP-OCR 模型的工具。3.10.0 里中文能用的有 PP-OCRv4、v5(mobile、server)和 v6(tiny、small、medium)。RapidOCR() 默认用的是 PP-OCRv6 small,安装包里只带了这一档,其余的第一次用时下载。

    我们要的是什么

    1. 关键信息一个字都不能错:日期、时刻、网址、邮箱,错一个数字截止时间就错了。普通文字错几个字,抽取的模型还能读懂。
    2. 不能漏,也不能编:识别出的文字存进快照当原文,证据要能在里面原样找到。所以要逐字照着图片识别,不改写、不补全;拿不准的,宁可让人看,也不能悄悄丢掉。
    3. 跑在抓取节点上:4 核、3.6GB,没有 GPU,同时还开着最多 2 个浏览器页面(总体设计 §16)。
    4. 量不大:只有一部分通知有图片,一张十来秒也能接受。速度不是重点,效果和内存才是。

    所以这次不比 Tesseract(中文认得差,还要另装系统依赖),也不比本地的多模态模型(比如 PaddleOCR-VL,0.9B 参数,在 CPU 上建议 16GB 内存,抓取节点放不下)。按次收费的云端 OCR 和多模态大模型接口先不考虑:要多一个 key,大模型还会顺手补全文字。只有本地几档都认不对的时候才考虑。

    比较怎么做

    1. 数据:从 data/sources.csv 的来源里找 15 到 20 张真实图片,再加 3 到 5 页扫描的 PDF。要覆盖:通知截图、海报、长图、表格(日程、名额)、盖章的扫描件。网址列在这里,图片不提交。每张手工写出关键信息(日期、时刻、网址、邮箱)作为答案。
    2. 候选:同一个 RapidOCR,换三档模型:PP-OCRv6 tiny、small、medium。另外两个默认参数也要试:
      • max_side_len(默认 2000,超过就缩小):长图被缩小后字会很小。长图用默认值和调大的值各跑一次。
      • text_score(默认 0.5,低于它的行直接丢掉):看看有没有因为它丢掉的关键信息。
    3. 指标:
      • 每张图关键信息全对的比例,逐项原样比较;
      • 丢掉或认错的关键信息,逐条列出;
      • 每张的耗时和峰值内存。在 Linux 上测,用 docker run --cpus=4 --memory=3600m 限成抓取节点的配置;
      • 每档模型文件的大小。
    4. 怎么选:选关键信息认得和最好的一档一样准的里面,最小的那档。几档都认不对的类型(比如表格)写清楚,这类通知交给人审核,不猜。small 以外的档要另外下载模型,写明 Docker 镜像里怎么带上它,测试也不能联网下载。
    5. 写回:比较结果写进总体设计 §5.2,选定的写进 §5.1,替换现在 实现图片附件的文字识别 #74 里写的那几句。

    #74 要改的

    • 删掉整个 crawler/examples/:通知的正文和附件不进仓库(AGENTS.md「底线」),自动测试里已经现场生成图片了。效果改成贴在 PR 描述里:图片直接拖进评论框上传,识别结果贴成代码块。现在描述里的图片链接指向分支上的文件,文件删了链接就失效了,所以要重新上传。
    • 比较做完以后,按选定的那一档改 RapidOCR() 的参数,§5.1、§5.2 按比较结果改。

    有问题随时在这里问。

  6. JunieXD commented on Nov 14, 2026

    @JunieXD
    ContributorAuthor

    @FengshuoChen 扫描的 PDF 我们在 #103 接上了:取不出文字的页,用 pypdf 取出页上的图片交给你在 #74 写的识别。扫描件一页就是一张图片,所以用不着另加渲染页面的库。图片的部分辛苦你了,谢谢!这个任务就完成了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

crawler抓取节点:抓取、初筛、抽取task可以认领的具体工作

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions