handwriting-ocr

v2026.09.24

手写笔记 OCR 流水线:将纸本扫描图与平板 PDF 导出的手写笔记转成可检索的 Markdown。 触发条件:用户提供一批图片(PNG/JPG)或 PDF,需要 OCR 转文字、做润色/纠错、加摘要索引; 或者需要评估这个流水线本身在人工标注对照集上的 CER/WER/检索召回打分。 命令: - /ocr-notes <目录> - 批量 OCR + LLM 润色 + 生成 MD - /ocr-notes refine <raw_ocr.json> - 仅做润色(OCR 已跑过) - /ocr-notes eval <测试集目录> - 评估打分(需人工标注对照集) - /ocr-notes install - 安装/检查 Python 依赖 - /手写笔记 <目录> - 中文命令入口,等价于 /ocr-notes 能力:PaddleOCR 本地推理(不依赖外部 API)、PDF 自动拆页、LLM 纠错与理顺、 自动生成摘要与关键词索引、CER/WER/关键词召回打分、Markdown 可检索产物。

GitHub
安装命令
npx skhub add cycleuser/handwriting-ocr
Markdown
SKILL.md

handwriting-ocr 技能

把纸本/平板的手写笔记,经过「PaddleOCR → LLM 润色 → Markdown 索引」三步, 变成可在文件系统中 grep 或在任何 Markdown 阅读器里全文检索的笔记库。

Quick Commands

命令作用
/ocr-notes install检查并安装 Python 依赖(首次运行必跑)
/ocr-notes <目录>完整流水线:批量 OCR → 润色 → 生成 MD
/ocr-notes refine <raw_ocr.json>只做润色(OCR 已经跑过)
/ocr-notes eval <测试集目录>在人工标注对照集上打分
/手写笔记 <目录>中文命令,等价于 /ocr-notes

适用场景

  • 笔记本扫描件(手机/扫描仪导出的 PNG/JPG)
  • iPad/安卓平板导出手写笔记 PDF
  • 历史手稿扫描归档
  • 需要在电脑上全文检索 / 摘要整理

不适用:印刷体书籍(请直接用 pdftotext / 文档类技能)、 纯图示/公式笔记(需要专用 OCR 模型)。


依赖与首次安装

仅依赖本地 Python 包,不依赖外部 API:

paddlepaddle
paddleocr
pymupdf
jieba(可选,改善 WER 精度)

在终端里:

pip install -r ~/.config/opencode/skills/handwriting-ocr/requirements.txt

Python 3.13 注意:请固定 paddlepaddle==3.2.2(3.3.x 在 Windows 有 oneDNN 转换 bug)。 GPU 加速(推荐,约 200 倍提速):有 NVIDIA 显卡时改用 GPU 版 paddle:

pip uninstall paddlepaddle -y
pip install paddlepaddle-gpu==3.2.2 -i https://www.paddlepaddle.org.cn/packages/stable/cu126/

cu126 对应 CUDA 12.6 驱动(可用 nvidia-smi 查看右上角 CUDA Version); 其他驱动版本替换为 cu118 / cu123 等。装好后 batch_ocr.py 默认 --device auto 会自动使用 GPU,也可显式 --device gpu / --device cpu。

首次跑 PaddleOCR 会自动下载检测/识别模型(约 100MB),保持网络畅通。

技能自带 /ocr-notes install 命令帮用户跑安装并验证。


完整工作流

Step 0 — 准备目录

让用户准备好一个目录,例如 ~/notes/2025-q1/, 里面放 PNG/JPG/PDF:

~/notes/2025-q1/
├── 01.png
├── 02.jpg
├── meeting-0312.pdf      # 多页
└── ...

Step 1 — 跑 PaddleOCR(脚本)

调用:

python ~/.config/opencode/skills/handwriting-ocr/scripts/batch_ocr.py \
    ~/notes/2025-q1/ \
    --output ~/notes/2025-q1/raw_ocr.json \
    --lang ch

产出 raw_ocr.json,结构:

{
  "source_dir": "...",
  "lang": "ch",
  "items": [
    {
      "id": "01",
      "source_file": "01.png",
      "page": 1,
      "image_path": "page_images/01.png",
      "raw_text": "原始 OCR 文本(按行用 \\n 拼接)",
      "raw_text_lines": ["第一行", "第二行", ...],
      "confidence": 0.876,
      "char_count": 234,
      "bbox_count": 18,
      "error": null
    },
    ...
  ]
}

同时把图片(含 PDF 拆出的页)复制/渲染到同目录的 page_images/ 下, 方便后续校对。

如果脚本抛出「PaddleOCR 未安装」或「PyMuPDF 未安装」,立即转去 /ocr-notes install。

Step 2 — 拆 raw_ocr.json 为按源文件的 .raw.txt(脚本)

cd ~/notes/2025-q1
python ~/.config/opencode/skills/handwriting-ocr/scripts/dump_raw.py \
    raw_ocr.json --out-dir . --format txt,md

产出:

  • <源文件名stem>.raw.txt — 纯文本(多页用 \n\n---\n\n 分隔)
  • <源文件名stem>.raw.md — Markdown 版(含分页标记与置信度)

evaluate.py 直接读 .raw.txt;LLM 润色时读 .raw.md 更直观。

Step 3 — LLM 润色与理顺(本会话内完成,无外部 API)

这一步骤由 opencode 当前会话的大模型直接执行,不需要调用第三方 LLM。 请按下面的「润色协议」逐个处理 <stem>.raw.md。

润色协议

对每个源文件(如 01.png、meeting-0312.pdf):

  1. 用 read 工具读取 <stem>.raw.md 与对应 raw_ocr.json 的条目
  2. 纠错:依据上下文修正 OCR 错字(如同音字、形近字、断行错误)
  3. 理顺:合并断行、补全标点、按语义分段,必要时加标题/列表/代码块
  4. 保真:只修可由上下文推出的内容,不要凭空补写;不确定处用 [?] 标记
  5. 生成产物:
    • <stem>.refined.txt — 仅正文纯文本(用于 evaluate.py 二次打分)
    • <stem>.md — 完整 Markdown,结构如下:
# <自动推断的标题或用源文件名>

> **来源**: `01.png`
> **OCR 引擎**: PaddleOCR(lang=ch, use_angle_cls=True)
> **OCR 平均置信度**: 0.876
> **生成时间**: 2026-08-17

## 摘要

<3-5 句话概括本页/本份笔记的核心内容,便于检索时一眼判断是否相关>

## 关键词索引

- 关键词1
- 关键词2
- ...

## 正文

<润色后的正文,按页面用 `### 第 N 页` 分隔>

<!--
以下为原始 OCR 文本,供校对(不计入检索):
<details><summary>查看原始 OCR</summary>

\`\`\`
<原 raw_text>
\`\`\`

</details>
-->

分块策略

如果某个源文件页数很多(如 30+ 页的 PDF),单次会话上下文可能装不下:

  • 按页分块:每次只读 1 个 raw_text_lines,润色并追加到当前 *.md
  • 用 bash + 工具保持写入原子:先写 <stem>.md.tmp,最后 mv 覆盖
  • 在每次 refine 之后输出当前进度,避免用户长时间等待无反馈

关键约束(refine 时必须遵守)

不要做要做
凭想象补写原文不存在的内容用 [?] 标注不确定字符
删去 OCR 已经能看清的细节保留可识别的数字、符号、列表结构
把所有笔记润成「标准书面语」保留原文的语气、口语、笔记习惯用法
一次性把全部内容塞进 LLM 上下文按源文件/按页分次润色

Step 4 — 生成目录索引(可选)

跑完 Step 3 后,额外生成一个 INDEX.md:

# 笔记索引 — <目录名>

| 文件 | 标题 | 摘要 | 关键词 |
|------|------|------|--------|
| [01.md](01.md) | ... | ... | ... |
| [meeting-0312.md](meeting-0312.md) | ... | ... | ... |

grep 或 IDE 全文搜索时,从 INDEX.md 入手。


评估打分(/ocr-notes eval <测试集目录>)

准备对照集

目录约定:

test_set/
├── 01.png             # 原图
├── 01.gt.md           # 人工逐字转录(必填)
├── 01.gt.json         # 可选,结构化 GT
└── 01.raw.txt         # Step 2 产出
└── 01.refined.txt     # Step 3 产出

01.gt.json 示例(关键词评估需要它):

{
  "text": "原始笔记的人工转录文本",
  "keywords": ["检索关键词1", "检索关键词2"]
}

跑评估

python ~/.config/opencode/skills/handwriting-ocr/scripts/evaluate.py \
    test_set/ \
    --report score_report.md \
    --json metrics.json

产出:

  • score_report.md — 0-100 综合分、CER/WER、关键词召回、逐条详情
  • metrics.json — 详细数值,便于二次分析

综合分公式

score = 0.7 × (1 - CER) × 100 + 0.3 × 关键词召回 × 100

无关键词标注时退化为 (1 - CER) × 100。

CER关键词召回综合分
0.100.9090
0.200.8080
0.050.9595

打分目标

场景及格线良好优秀
印刷体859296
工整手写758592
潦草手写607585

如果润色后分数低于原始 OCR 分数,说明润色反而引入了错误, 此时应在 refine 协议里加重「不要改写」约束。


输出文件清单

文件说明
<dir>/raw_ocr.jsonOCR 原始输出(含置信度、行、坐标)
<dir>/page_images/单页图片(扫描原图 + PDF 拆页)
<dir>/<stem>.raw.txt按源文件聚合的纯文本
<dir>/<stem>.raw.md按源文件聚合的 Markdown(带分页)
<dir>/<stem>.refined.txt润色后纯文本(evaluate 用)
<dir>/<stem>.md最终可检索 Markdown(含摘要 + 关键词 + 正文)
<dir>/INDEX.md全目录索引(可选)
<test_dir>/score_report.md评估报告
<test_dir>/metrics.json评估明细

常见踩坑

坑 1: paddlepaddle 在 Windows 上 pip 装不上

现象:pip install paddlepaddle 报编码错误 / 找不到版本。

解决:

python -m pip install --upgrade pip setuptools wheel
pip install paddlepaddle -i https://pypi.tuna.tsinghua.edu.cn/simple
# 或 GPU 版:
# pip install paddlepaddle-gpu==2.6.1.post118 -i https://pypi.tuna.tsinghua.edu.cn/simple

坑 2: PDF 拆页失败

现象:PyMuPDF not installed 或 cannot open PDF。

解决:先 pip install pymupdf;若 PDF 加密,先用其他工具解密。

坑 3: PaddleOCR 首次运行太慢

现象:跑一张图要 5+ 分钟。

原因:首次下载检测/识别/方向分类三个模型。

解决:耐心等待;模型缓存到 ~/.paddleocr/ 后后续秒开。

坑 4: 中文 OCR 串行断行

现象:「今天开会 / 议讨论了 / 项目进度」其实应为「今天会议讨论了项目进度」。

解决:LLM 润色时按语义合并;不要在 OCR 阶段硬合并。

坑 5: 评估分数比润色前还低

现象:原始 CER=0.20,refined 后 CER=0.25。

原因:LLM 错误补写了原文不存在的内容,或过度「书面化」。

解决:调整润色 prompt,加重「保守/不补写」约束;考虑针对不同样本关闭润色。

坑 6: 上下文塞不下

现象:20 页 PDF 的 raw_text 超过会话上下文。

解决:按页分块;或先 refine 前 5 页 → 让用户确认效果 → 再批量续跑剩余页。


脚本位置

脚本功能
scripts/batch_ocr.py批量 OCR(PNG/JPG/PDF → raw_ocr.json)
scripts/dump_raw.py把 raw_ocr.json 按源文件拆成 .raw.txt / .raw.md
scripts/evaluate.py与人工标注对照集打分(CER/WER/关键词召回)

依赖见同目录 requirements.txt。

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

GPL-3.0

源路径

skills/handwriting-ocr

默认分支

main

最新提交

d57a35e

Tree SHA

02d9442