Skip to content

实现图片附件的文字识别 - #74

Merged
JunieXD merged 6 commits into
mousebaoyan:mainfrom
FengshuoChen:image-ocr-results
Nov 14, 2026
Merged

JunieXD merged 6 commits into
mousebaoyan:mainfrom
FengshuoChen:image-ocr-results

Conversation

@FengshuoChen

@FengshuoChen FengshuoChen commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

关联

关联 #47 的图片识别阶段。#98 已接通正文和附件读取;扫描 PDF 的渲染库另在 #47 后续 PR 里选择,本 PR 不关闭 #47。

改了什么

  • 用来源清单中的 9 张真实招生通知图片和一份 4 页 PDF 比较 PP-OCRv6 tiny、small、medium。
  • 对三档模型分别比较 max_side_len=2000/4000,统一使用默认 text_score=0.5。
  • 在缩到 2000 的副本上找行、从原图切行,每批识别 1 行;比较 text_score=0.5/0.3 后保留默认 0.5。
  • file_text 按文件头识别 PNG、JPEG、GIF、BMP、TIFF 和 WebP,并调用图片识别;模型只加载一次,用锁保证同一时刻只识别一张。
  • 使用 RapidOCR 安装包自带的 PP-OCRv6 small,不再下载或显式配置模型路径;运行和测试都能断网。
  • 比较和最终实现都沿用 RapidOCR 默认开启的方向分类,与原先实际运行的设置一致。
  • 日志记录耗时和 Linux 进程峰值内存,不记录识别正文;测试经 file_text 现场生成中文图片和空白图片。
  • 在运行 Playwright 安装器前补上基础镜像缺少的 libatomic1,使 crawler/Dockerfile 能完整构建。
  • 删除整个 crawler/examples/,不把真实通知图片和识别结果提交进仓库。
  • 把比较方法、结果、选型和内存边界写入总体设计 §5.1、§5.2 和 §16。

“完全正确”的标准

本次只把日期、时刻、网址和邮箱作为关键信息,先逐项人工抄录图片原文作为答案。一项关键信息满足下面全部条件才算正确:

  1. OCR 输出中存在这项信息;
  2. 信息出现在同一条 OCR 文本行中,不把跨行内容人工拼接;
  3. 与图片原文逐字一致,不人工纠错、补全或规范化;
  4. 字段内部的数字、字母、大小写、标点、连字符、斜杠、冒号和空格等任何字符不同都记为错误;只忽略字段外围的版式空白;
  5. 图片中重复出现的值按出现次数分别核对,不能用一次识别结果代替多次出现。

一张图片或一页 PDF 只有在所有人工标注的关键信息都满足以上条件时,才计入“关键信息全对”。漏掉或错识别任何一项,整张(页)都不算全对。普通正文中的少量错字不影响这个指标;如果 OCR 输出图片中不存在的关键字段,也必须交给人审核,不能作为原文证据。

比较结果

测试环境为 Linux 容器、RapidOCR 3.10.0 和 ONNX Runtime 1.31.1,使用 --cpus=4 --memory=3600m --network none;模型提前放入镜像,每档只创建一个实例并顺序识别。

设置 PP-OCRv6 关键信息全对 13 张(页)总耗时 峰值内存 模型文件
2000 tiny 2/12 85.19 秒 784.1 MiB 6.58 MiB
2000 small 4/12 289.04 秒 1151.2 MiB 30.28 MiB
2000 medium 4/12 842.72 秒 2077.9 MiB 132.88 MiB
4000 tiny 3/12 137.21 秒 1694.2 MiB 6.58 MiB
4000 small 5/12 354.25 秒 2058.8 MiB 30.28 MiB
4000 medium 7/12 1112.38 秒 3305.9 MiB 132.88 MiB
找行 2000、原图切行、每批 1 行 small 7/12 240.87 秒 726.0 MiB 30.28 MiB

4000/0.5 的逐样本结果如下;“—”表示该页没有关键信息,不进入分母。

样本 tiny small medium
图片 1 错 全对 全对
图片 2 错 错 全对
图片 3 全对 错 全对
图片 4 全对 全对 全对
图片 5 错 错 错
图片 6 错 错 错
图片 7 错 错 错
图片 8 错 错 错
图片 9 错 错 全对
PDF 第 1 页 错 全对 错
PDF 第 2 页 — — —
PDF 第 3 页 错 全对 全对
PDF 第 4 页 全对 全对 全对

最终设置(small、找行 2000、原图切行、每批 1 行、text_score=0.5)的逐样本结果如下。

样本 结果
图片 1 全对
图片 2 全对
图片 3 全对
图片 4 全对
图片 5 错
图片 6 错
图片 7 错
图片 8 错
图片 9 错
PDF 第 1 页 全对
PDF 第 2 页 —
PDF 第 3 页 全对
PDF 第 4 页 全对

2000/0.5 的逐样本结果如下。

样本 tiny small medium
图片 1 全对 全对 全对
图片 2 错 错 错
图片 3 错 错 错
图片 4 错 错 错
图片 5 错 错 错
图片 6 错 错 错
图片 7 错 错 错
图片 8 错 错 错
图片 9 错 错 全对
PDF 第 1 页 错 全对 错
PDF 第 2 页 — — —
PDF 第 3 页 错 全对 全对
PDF 第 4 页 全对 全对 全对
max_side_len=2000 每张(页)的耗时和容器内存峰值
样本 tiny 秒 / MiB small 秒 / MiB medium 秒 / MiB
图片 1 7.00 / 467.9 21.19 / 807.3 70.68 / 1262.5
图片 2 7.71 / 551.1 27.11 / 1073.2 75.60 / 1303.1
图片 3 8.38 / 611.3 31.79 / 1151.2 76.65 / 1514.1
图片 4 7.70 / 614.9 30.21 / 1098.5 85.55 / 1645.6
图片 5 9.80 / 655.9 35.16 / 883.3 98.63 / 1826.3
图片 6 6.21 / 627.0 24.70 / 1122.2 71.00 / 1696.2
图片 7 5.80 / 664.1 21.29 / 1007.9 78.20 / 1660.2
图片 8 9.19 / 660.5 31.01 / 983.1 92.40 / 1622.5
图片 9 7.40 / 655.6 26.61 / 1039.8 87.01 / 1665.4
PDF 第 1 页 4.91 / 753.6 11.09 / 1149.4 31.99 / 1903.1
PDF 第 2 页 4.30 / 783.5 12.10 / 1097.4 27.80 / 2077.5
PDF 第 3 页 4.29 / 784.1 11.38 / 1097.7 32.60 / 2076.9
PDF 第 4 页 2.50 / 783.8 5.41 / 1085.2 14.62 / 2077.9
max_side_len=4000 每张(页)的耗时和容器内存峰值
样本 tiny 秒 / MiB small 秒 / MiB medium 秒 / MiB
图片 1 10.84 / 941.5 23.28 / 1105.4 76.91 / 1642.9
图片 2 16.11 / 1498.9 37.55 / 1775.6 105.69 / 3305.9
图片 3 13.03 / 1070.1 38.42 / 1295.2 111.87 / 2133.3
图片 4 16.49 / 1554.0 38.40 / 1697.4 127.23 / 2914.0
图片 5 11.76 / 1111.2 34.53 / 1435.0 124.74 / 1886.5
图片 6 13.30 / 1634.3 32.91 / 2058.8 100.59 / 3246.3
图片 7 12.50 / 1694.2 32.98 / 1990.5 101.25 / 3157.1
图片 8 12.10 / 1224.3 38.52 / 1693.9 126.17 / 2445.5
图片 9 14.59 / 1427.0 37.03 / 1848.3 119.41 / 3102.1
PDF 第 1 页 4.80 / 1173.6 12.74 / 1335.7 36.54 / 2333.2
PDF 第 2 页 4.11 / 1332.2 11.00 / 1575.6 30.98 / 2541.2
PDF 第 3 页 4.69 / 1332.2 11.21 / 1575.1 34.70 / 2540.8
PDF 第 4 页 2.89 / 1332.3 5.69 / 1574.9 16.31 / 2541.3

三档模型在 2000/0.5 下的具体错误

以下只列日期、时刻、网址和邮箱的错误;普通正文错字不列。

PP-OCRv6 tiny

  • 图片 2:报名网址被识别为 https: //yjspt.. xidian. edu. cn/gsapp/sys/wdyjsbm/ent.rance. do;导师网址被插入空格并把 dsjies 拆错;cs@xidian.edu.cn 被识别为 cs@xidian. cdu. cn。
  • 图片 3:2026-08-28 被识别为 2025-08-28;报名网址被识别为带空格且把 TMS 写成 TM5 的 https://zhaosheng.ucas.ac.cn/sign_ up/TM5/views/index.aspx。
  • 图片 4:http://gsas.seu.edu.cn 被识别为 http://gsas.scu.cdu.cn;2025年7月2日17:00—8月1日24:00 中两个“日”被识别为“口”;rjyjszs@126.com 被识别为 rjyjszs(@126.com。
  • 图片 5:导师网址、两个报名网址和结果网址都不可用;2026年9月14日14:00前 被识别为 2028年9J14口14:00前;日程中的日期、时刻被漏掉或拆行;两个邮箱均未识别出来;末尾 2026年9月7日 被识别为 2026年3月7日。
  • 图片 6:https://ssyjsbm.xmu.edu.cn/ 被识别为 https://ssy.jsbm.xmu. edu.cn/;邮箱和学院网址被插入空格。
  • 图片 7:https://ssyjsbm.xmu.edu.cn/ 在 .c 后换行,http://yz.chsi.com.cn/tm 在 chsi. 后换行。
  • 图片 8:导师网址、报名网址和结果网址均被错识别;日程中的空格、冒号和拆行与原文不一致;scseyjs@uestc.edu.cn、shensuyjs@uestc.edu.cn 均被错识别。
  • 图片 9:学院网址被插入空格;博士报名网址末尾的 .aspx 被识别为 .asp×。
  • PDF 第 1 页:7月6日10:00-8月10日17:00 被识别为 7月6日 10:00-8月 10日 17:00。
  • PDF 第 3 页:7月13日至8月25日 被识别为 7月 13 日至8月 25日。

PP-OCRv6 small

  • 图片 2:导师网址中的 yjsjy 被识别为 xjsjy;cs@xidian.edu.cn 被识别为 cs@xidian.cdu.cn。
  • 图片 3:2026-08-28 被识别为 2025-08-28;报名网址所在整行没有可用结果。
  • 图片 4:http://gsas.seu.edu.cn 被识别为 http://gsas.scu.cdu.cn。
  • 图片 5:导师网址、两个报名网址和结果网址均被错识别;2026年9月14日14:00前 被识别为 202±年9,14日14:00前;日程中的日期、时刻被漏掉或拆行;rjgczs@uestc.edu.cn 未识别出来,rjss@uestc.edu.cn 被识别为 rjss@uestc.eru.cr。
  • 图片 6:https://ssyjsbm.xmu.edu.cn/ 被识别为含内部空格且缺少末尾 / 的 https://ssyjsbm. xmu.edu.cn。
  • 图片 7:两个报名网址分别在 .c 和 chsi. 后换行。
  • 图片 8:导师网址的域名、路径和查询参数均有错字;报名网址中的 edu 被识别为 odu;结果网址被插入空格并把 scse 写成 sese;多项日程被拆行或把半角冒号识别为全角冒号。
  • 图片 9:jsj_yjsjw@bupt.edu.cn 被识别为 jsj-yjsjw@bupt.edu.cn。

PDF 中的关键信息均能原样识别。

PP-OCRv6 medium

  • 图片 2:cs@xidian.edu.cn 被识别为 cs@xidian.cdu.cn。
  • 图片 3:报名网址所在整行没有可用结果。
  • 图片 4:http://gsas.seu.edu.cn 被识别为 http://gsas.scu.cdu.cn。
  • 图片 5:导师网址、两个报名网址和结果网址均被错识别;2026年9月14日14:00前 被识别为 2028年9月14□14:00前;日程中的日期、时刻被漏掉或拆行。
  • 图片 6:https://ssyjsbm.xmu.edu.cn/ 被识别为带空格的 https://ssyjsbm. xmu.edu.cn/。
  • 图片 7:两个报名网址分别在 .c 和 chsi. 后换行。
  • 图片 8:导师网址路径和查询参数有错字;完全漏掉包含 2026年9月15日下午17:00 的报名截止时间整行;一处报名网址把 yzbm 写成 vzbm 并漏掉点号;研招网和结果网址被插入空格;多项日程被拆行,9月20日 被漏掉。
  • PDF 第 1 页:7月6日10:00-8月10日17:00 被识别为 7 月6日10:00-8 月 10日17:00。

三档模型在 2000 像素下都会漏掉或错识别关键字段;small 和 medium 同为 4/12 张(页)全对,但 medium 更慢、内存和模型文件更大。密集表格、跨行网址和日程仍须交给人审核,不能根据上下文补全。

选型和边界

在 4000/0.5 下,medium 的识别效果最好;它比 small 多完整识别图片 2 和图片 3,图片 9 也只有 medium 全对。但 medium 在图片 2 上的容器内存峰值已经达到 3305.9 MiB,距 3600 MiB 上限只剩约 294 MiB,而且这次没有同时运行两个浏览器页面,不能满足抓取节点还要运行浏览器的约束。

因此排除 medium,选择 PP-OCRv6 small:在缩到 2000 的图上找行、从原图切行,每批识别 1 行。它有 7/12 张(页)关键信息全对,13 个输入共 240.87 秒,进程峰值 726.0 MiB;与同时打开 2 个浏览器页面的约 450 MiB 合计约 1.2 GiB。

把 text_score 从默认的 0.5 降到 0.3 后,13 个输入的关键信息一项没变,只在图片 2、图片 8 各多出一行乱码,也没有降低峰值,因此保留 0.5。最终设置仍会拆开长网址和密集表格中的日程,相关字段必须交给人审核,不能根据上下文补全。

最终设置中未全对的 5 张图片,具体错误如下:

  • 图片 5:密集日程表把 9月14日14:00前 识别成单独的 9月14,另有多项日期、时刻漏掉或拆行。
  • 图片 6:https://ssyjsbm.xmu.edu.cn/ 漏掉末尾 /。
  • 图片 7:https://ssyjsbm.xmu.edu.cn/ 和 http://yz.chsi.com.cn/tm 都在域名中间断行。
  • 图片 8:报名网址把 logon 识别成 1ogon,并漏掉 9月15日下午17:00 中的 17:00;多项日程的空格和半角冒号也与原文不同。
  • 图片 9:jsj_yjsjw@bupt.edu.cn 被识别为 jsj-yjsjw@bupt.edu.cn。

输入图片

共 9 张输入图片,点击缩略图可查看原图。

样本分别来自:中国科学院自动化研究所、西安电子科技大学计算机科学与技术学院、中国科学院计算技术研究所、东南大学计算机科学与工程学院、软件学院、电子科技大学信息与软件工程学院、厦门大学人工智能研究院、厦门大学信息学院、电子科技大学计算机科学与工程学院和北京邮电大学计算机学院。仓库中不提交这些图片、PDF 或 OCR 全文。

图片 1
输入图片 1
图片 2
输入图片 2
图片 3
输入图片 3
图片 4
输入图片 4
图片 5
输入图片 5
图片 6
输入图片 6
图片 7
输入图片 7
图片 8
输入图片 8
图片 9
输入图片 9

怎么验证的

  • 在受限 Linux 容器中完成 tiny、small、medium 的 9 张真实图片和 4 页 PDF 比较。
  • 两组参数均使用 --cpus=4 --memory=3600m --network none,模型在离线环境中读取。
  • 已核对每档 13 个文本结果、13 个结构化结果和运行清单均完整生成。
  • 用当前 crawler/Dockerfile 成功构建镜像,并在 --network none 下识别现场生成的图片;日志确认三份 small 模型都来自 RapidOCR 安装包。
  • 最终设置在相同限制下分别运行 text_score=0.5/0.3;0.5 为 240.87 秒、726.0 MiB,0.3 为 257.09 秒、715.0 MiB。
  • 在 4 核、3600 MiB 的 Linux 容器中运行 crawler/check.sh:接口类型生成检查、Ruff 格式和 lint、严格 Pyright 均通过,pytest 268 项测试全部通过。

影响

  • 新增 RapidOCR、ONNX Runtime 运行依赖及 Pillow 测试依赖;去掉 psutil,不修改网站接口或 MCP 工具。
  • 图片附件现在经 file_text 调用 OCR;坏图片和其他坏附件一样转成 ValueError,由现有抓取流程记录失败。
  • 抓取节点直接使用 RapidOCR 安装包自带的约 30.28 MiB small 模型,不增加模型下载步骤,运行和测试可断网。
  • crawler/Dockerfile 安装 libatomic1,供 Playwright 自带的 Node 在 Python 3.14 slim 基础镜像中启动;不增加部署单元或端口。
  • 扫描 PDF 仍需另选页面渲染库并在 [任务] 图片和扫描件的文字识别 #47 后续 PR 接入;pypdf 继续只读已有文字层。

@FengshuoChen
FengshuoChen marked this pull request as ready for review November 8, 2026 14:26
@JunieXD

JunieXD commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

@FengshuoChen 看到你把 PR 转成了待审。分支上还是 10-31 的两个提交,#47 里 11-06 说的两件事还没做:

  1. 用来源里的真实图片比较 PP-OCRv6 tiny、small、medium 三档,选定一档,结果写进总体设计 §5.1、§5.2;
  2. 删掉整个 crawler/examples/,效果改成直接上传到 PR 描述里。

是还在做比较,还是推送时漏了提交?改好后在这里说一声,我们就复审。比较的做法有疑问也可以在 #47 里问。

@FengshuoChen FengshuoChen changed the title 实现图片附件的文字识别并展示效果 实现图片附件的文字识别 Nov 12, 2026
- 用真实附件比较 PP-OCRv6 三档模型并选 small
- 删除真实通知示例,测试改为现场生成
- 在构建阶段准备模型,运行和测试保持离线
@FengshuoChen

Copy link
Copy Markdown
Contributor Author

@JunieXD 已补推提交 27cf2e9:

  • 用当前 9 张真实通知图片和 4 页 PDF 比较了 PP-OCRv6 tiny、small、medium,并测试了 max_side_len=2000/4000、text_score=0.5;综合关键信息准确率和 3.6GB 节点内存,选择 small、max_side_len=4000。
  • 比较方法、完整结果、错误类型和内存边界已写入总体设计 §5.1、§5.2、§16,PR 描述中列出了逐模型错误和输入图片。
  • 已删除整个 crawler/examples/,自动测试改为现场生成图片;模型由构建阶段显式准备,运行和测试不联网下载。

请复审。

@JunieXD JunieXD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢!这次的比较做得很扎实:判对的标准写得清清楚楚,每档模型认错了哪些日期、网址、邮箱都逐条列出来了;medium 最准,却因为单独识别就占 3.3GB、给浏览器留不出余量而排除,这个取舍和我们要的一致。crawler/examples/ 也删干净了,测试改成现场生成图片。我们拉到本地装了依赖、跑了 ./check.sh,18 个测试全部通过;还读了 RapidOCR 的源码,用 PR 描述里的 9 张图片重跑了几种设置,核对了下面第 3、4 条。

剩下的问题主要是:分支落后 main,合进去以后图片还没接上读附件;模型的准备有几处多余;识别的设置改一下,内存能降一半以上;文档要按合并后的现状改。

必须改

  1. 先合进最新的 main,解决冲突:分支基于 10-31 的 main,之后 main 又合进了 #98(读正文和附件)、#100(入口和镜像)等 25 个提交,crawler/pyproject.toml、crawler/uv.lock、docs/design.md 冲突。有冲突时 GitHub 不跑这个 PR 的 CI,所以 27cf2e9 没有检查结果。

    • pyproject.toml 保留 main 的全部依赖、[project.scripts] 和类型桩,再加上这个 PR 的;uv.lock 用 uv lock 重新生成,不直接取哪一边;
    • design.md 以 main 为底,只把文字识别的几处改上去(见第 7 条);
    • 合进 main 和自己的改动分成两个提交,复审时能看出合并以后改了什么。
  2. 把识别接进读附件(crawler/src/crawler/parse/files.py:26-43):#40 已经在 #98 里合并了,现在图片附件落到 file_text 最后的 return "",文件开头的注释写着等 #47;image_text 没有调用方,合并后图片附件的文字还是空的。#47 的范围就是「#40 读附件时对图片调用它」,这一步在这个 PR 里做:

    • file_text 像认 PDF、ZIP 那样按文件头认出图片(PNG、JPEG 等),调 image_text;识别出错时由 file_text 已有的 except 转成 ValueError,和别的坏文件一样记一条日志;
    • 改掉 files.py 开头注释里等 #47 的那句;
    • crawl/notice.py 已经用 asyncio.to_thread 调 file_text,加上 image_text 里的锁,不会卡住事件循环,同一时刻也只识别一张,那边不用改。
  3. 模型用安装包自带的,删掉下载:我们装完依赖看过,rapidocr 的安装包里自带 PP-OCRv6 small 的检测、识别模型和方向分类模型;rapidocr download_models --config ocr-models.yaml 只输出两次 File exists and is valid,什么也没下。所以:

    • 删掉 .github/workflows/ci.yml:64 新加的那一步和 crawler/ocr-models.yaml,Dockerfile 也不用加下载;
    • image.py:25-33 拼模型路径、检查文件在不在的那段删掉,:38-42 的 model_type 和三个 model_path 也不用写:small 本来就是默认的一档,安装包里的模型在,它就不会联网下载。参数只写第 4 条里和默认不同的几项;
    • 方向分类现在两处不一致:ocr-models.yaml 写的是 use_cls: false,可 image.py 不读这个文件,RapidOCR 默认 use_cls 是 true,实际在用。比较时用的是哪种就照哪种写,关掉的话参数里加上 "Global.use_cls": False;
    • 改完用 crawler/Dockerfile 构建一次镜像,在容器里识别一张生成的图片,确认镜像里不缺系统库、不联网下载,结果写进 PR 描述。
  4. 在缩小的图上找行、从原图上切行识别,一次认一行(image.py:34-44):我们拆开测了一次识别的内存。模型和库约 100MB;检测(找出哪里有字)的内存跟送进去的像素数成正比,max_side_len=4000 时单做检测就占 2.0 到 2.6GB;识别默认一次送 6 行、按其中最宽的一行补齐,网页截图的整行很宽,这一步又多占约 1GB。4000 认得准,是因为 RapidOCR 先把整张图缩到 max_side_len,再在缩过的图上切行识别,字大了才认得清;其实找行用缩小的图就够,切行可以从原图上切。改成:

    _engine = RapidOCR(
        params={
            # Find lines on a copy capped at 2000 px but crop them from the original, so small text stays legible.
            "Global.use_preprocess_img": False,
            "Det.limit_type": "max",
            "Det.limit_side_len": 2000,
            # One line per batch: padding a batch of six to its widest line doubled the peak.
            "Rec.rec_batch_num": 1,
        }
    )

    我们在 Mac(M4)上用 PR 描述里的 9 张图片比较过,每张图单独一个进程;关键信息取你在描述里写明原文的 18 项日期、网址和邮箱:

    设置 认对 9 张里最高峰值 9 张总耗时
    默认(max_side_len=2000) 6/18 2094MB 26 秒
    max_side_len=4000 12/18 2298MB 33 秒
    上面的设置 12/18 965MB 12.5 秒

    一次认一行,认出的文字和一次 6 行只有空格、全角半角这类差别,18 项一项没变。检测限到 3000 多认对 1 项,峰值涨到 1.7GB,18 项里差 1 项算误差,不值得;长图横切成几段分别识别也试过,没有更好。Mac 上的绝对值和 Linux 不同(默认设置你在容器里测的是 807 到 1151MiB,我们这边是 1367 到 2094MB),请在你那个 4 核、3600MiB 的容器里,用同样的 12 个样本把这一档重跑一遍,内存和关键信息全对的张数写进 PR 描述和 §5.2。

  5. 日志照现有的写法,去掉 psutil(image.py:23、:48-50):

    • 耗时照 crawl/fetch.py、screening/screen.py 记成 elapsed,单位秒,用 time.monotonic();
    • 内存用标准库的 resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 记进程的峰值(Linux 上单位是 KiB),去掉 psutil 依赖。抓取节点怕的是峰值超过 3.6GB,识别完那一刻的 RSS 说明不了这个;标准库做得到的,也不为它加新包(AGENTS.md「依赖从简」)。
  6. 测试经 file_text 测,挪进 files_test.py:

    • 中文那张留着,报名时间、带时刻的日期都在里面;英文那张和它测的是同一件事,删掉。空白图返回空字符串的那条留着;
    • files_test.py:83 只有 PNG 文件头的那个例子,接上识别以后就不是「没有文字」了,跟着改;
    • 字体和许可证挪进 parse/fixtures/,和别的测试数据放在一起。
  7. 补测 text_score,文档只写现状和决定:

    • 11-06 说的 text_score 还没试。PR 里有几处「整行没有可用结果」,可能就是被 0.5 的门槛丢掉的。在第 4 条这一档上把 text_score 降到比如 0.3 重跑:丢掉的行回来了、又没多出错字,就用它;不然留 0.5。降门槛不多占内存。

    • 总体设计 §5.2 的「文字识别」只留样本和判对的标准、表格、选了哪档和为什么、哪些图认不准;9 个来源的链接和逐条的错误放在 PR 描述里就行,「还要补测」这类待办不写进设计。可以写成:

      **文字识别**:`data/sources.csv` 的 9 个来源各取一张通知图片(截图、海报、长图、密集的日程表),加一份渲染成图片的 4 页 PDF,在限成 4 核、3600 MiB、断网的 Linux 容器里用 RapidOCR 3.10.0 依次识别。只看日期、时刻、网址和邮箱:同一行里和原文一字不差才算对,一张里错一项就不算全对;有这些信息的共 12 张(页)。
      
      (表格照现在的,加上第 4 条这一档复测的一行)
      
      选 small,在缩到 2000 的图上找行、从原图上切行识别,一次认一行:默认把整张图缩到 2000 再识别,长截图的字太小认不准;把整张图放到 4000 一样准,但检测要多占 1GB 以上;medium 最准,但单独识别就占 3.3GB,给浏览器留不出余量。`text_score` 用 <补测的结论>。三档都会漏掉整行的网址、拆开表格里的日程,图片里认出的关键信息不能当作可靠的原文。
      
      抓取节点同时开 2 个浏览器页面(约 450MB)、一次识别一张图(约 <复测的峰值>),合计约 <合计>,3.6GB 内存够用(第 16 节)。
    • §5.1 那一行 main 已经改写过:只把「文字识别候选 RapidOCR」改成「图片的文字识别用 RapidOCR(PP-OCRv6 small,设置见 §5.2)」,「文字识别在……的任务里定」改成「扫描的 PDF 用哪个库渲染成图片,在……的任务里定」;

    • §16 抓取节点那一行的「内存峰值约 1.4GB」按复测的结果改,不写「仍需复测」;

    • §18 「文字识别用的库」那一行改成「扫描的 PDF 用哪个库渲染成图片」还没定。

说明

  • 扫描的 PDF:#98 选的 pypdf 只读文字层,不能把页面渲染成图片,要另选一个渲染的库(不用 AGPL 许可的 PyMuPDF)。这部分留在 #47 里另开 PR,选库前先在 #47 说一声;这个 PR 不关闭 #47。PR 描述里「PDF 的下载、渲染和接入仍由 #40 处理」「#40 尚未实现附件读取」这几句跟着改。
  • 样本数量:Issue 写的是 15 到 20 张,这次是 9 张。medium 是因为内存排除的,多几张也改变不了选择,这次不用补。
  • 识别错了的值:比如把 2026-08-28 认成 2025-08-28,这个错的日期在快照里照样「原样找得到」。证据只在图片里的字段送进审核页,这是网站那边的事,我们来做,这个 PR 不用管。
  • 让 DeepSeek 直接读图:抽取用的 DeepSeek V4.1 Flash 已经能读图片,能不能用它代替本地识别,另开了 #102 做实验,有兴趣可以认领;这个 PR 照上面的改,继续用 RapidOCR。
  • 改完在这里说一声,我们就复审。推了新提交后 CI 要我们批准一次才会跑;删掉 workflow 那一步以后,这个 PR 就不改 .github/ 了。

@FengshuoChen

FengshuoChen commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor Author

已按这轮意见改完并推送,请复审:

  • 合并了最新的 main,合并提交和后续修改分成两笔提交;
  • 图片附件已通过 file_text 调用 image_text,坏图片按现有流程转成 ValueError;
  • 改用 RapidOCR 安装包自带的 PP-OCRv6 small,删除模型下载配置和 workflow 下载步骤,方向分类保持默认开启;
  • 改为缩到 2000 找行、从原图切行、每批识别 1 行;4 核、3600 MiB、断网容器复测为 7/12 张(页)全对,13 个输入共 240.87 秒,峰值 726.0 MiB;
  • text_score=0.3 没找回关键信息,反而多出两行乱码,因此保留默认 0.5;
  • 日志改用 elapsed 和 resource.getrusage,去掉 psutil;测试改经 file_text,字体移到 fixtures;
  • 已更新总体设计 §5.1、§5.2、§16、§18,并改掉 PR 里关于 [任务] 读正文和附件 #40 和扫描 PDF 的旧说法;
  • crawler/Dockerfile 已成功构建,断网容器能识别现场生成图片;本地 crawler/check.sh 通过,当前 CI 的 crawler、web 两项也都通过。

本次最终 diff 不再修改 .github/。

锁文件以 main 的为底重新生成,只加上识别要用的包,已有的依赖不跟着升级。Playwright 留在 1.64.1,它自带的 Node 不要 libatomic,镜像也就不用再装。

@JunieXD JunieXD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢!上一轮的七条都改到了:合进 main 和自己的修改分成了两个提交;图片经 file_text 识别,坏图片和别的坏文件一样转成 ValueError;用安装包自带的模型,不再改 workflow;设置照第 4 条改了,容器里复测的峰值从 1151 MiB 降到 726 MiB,全对的从 4 张升到 7 张;text_score 也补测了,§5.2 写得清清楚楚。

剩下一处我们直接改了(89f1b10):uv.lock 原来是整个重新解析的,除了识别要用的 16 个新包,还把 14 个已有的依赖升了版本,比如 Playwright 1.64.1 → 1.65.1;Dockerfile 要装的 libatomic1 就是 1.65.1 自带的 Node 要的,1.64.1 不要。我们以 main 的锁文件为底重新跑了 uv lock,已有的依赖保持 main 的版本,Dockerfile 退回 main 的写法。以后依赖升级单独开 PR 就好(AGENTS.md「提交和 PR」)。改完以后本地 ./check.sh 通过,镜像不装 libatomic1 也能构建,在断网的容器里识别现场生成的图片,三行都认对了。

这就合并。扫描的 PDF 还留在 #47,选渲染的库之前在那边说一声。

@JunieXD
JunieXD merged commit bae738f into mousebaoyan:main Nov 14, 2026
2 checks passed
JunieXD added a commit that referenced this pull request Nov 14, 2026
本地的 RapidOCR 会把整行认成乱码、拆开日程表,图片里的截止时间和网址常常认不全(#102)。

- 用 #74 的 9 张通知图片比较 RapidOCR 和让 DeepSeek 整张读、切块读,切块读认对 111/113 项,写进总体设计 §5.2
- 同一张图每次读出的文字可能不同,抓取节点存下读过的文件,文件没变就用上次的文字(§7.4)
- 读图的用量和抽取一起交(§8.6);§5.1、§16 跟着改
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants