local-ocr —— 离线本地 OCR 技能(Skill)

August 21, 2026 · View on GitHub

在 Windows 上离线识别图片/PDF 中的文字,输出结构化文本。零云端成本、离线可用,专为"模型无视觉输入、须以 OCR 文本喂模型"的 Agent 环境设计。

  • 引擎自动选择:Windows 原生 OCR(微软官方引擎,中文精度最高)→ RapidOCR → Tesseract
  • 支持图片:PNG / JPG / JPEG / BMP / WebP / TIFF / GIF;PDF 逐页转图片识别
  • 结构化 JSON 输出、预处理增强、引擎对比自测

目录结构

local-ocr/
├── SKILL.md              # 技能指令(agent 加载后按其执行)
├── scripts/
│   ├── ocr_tool.py       # OCR 实现(本仓库快照,约 430 行,无第三方框架依赖)
│   └── ocr.bat           # 便捷包装(%~dp0 定位同目录脚本,可整体移动)
├── README.md
└── LICENSE               # MIT

依赖

  • Python 3.11(系统 python;勿用 onnxruntime 兼容差的 3.14 venv)
  • pip install winocr pillow(winocr 为首选引擎;pillow 用于预处理/自测/PDF 渲染辅助)
  • Tesseract(可选兜底:scoop install tesseractProgram Files\Tesseract-OCR,需 chi_sim + eng 语言包)
  • PDF 渲染(三选一):pypdfium2(首选)→ PyMuPDF(fitz)→ pypdf(仅文本层)
  • Windows 中文 OCR 语言包(zh-Hans-CN,Win11 通常自带;Win10 需在"设置→语言"中手动添加)

用法

# 图片或 PDF,输出结构化 JSON(推荐)
python scripts/ocr_tool.py "<文件路径>" --json

# 或直接用批处理包装
scripts\ocr.bat "<文件路径>" --json

# 自测:生成中文测试图并对比各引擎
python scripts/ocr_tool.py --selftest

# 指定引擎 / 低清图预处理
python scripts/ocr_tool.py "<图片>" --json --engine tess --preprocess

参数

参数说明
--json结构化 JSON 输出(推荐)
--engine auto|win|rapid|tess引擎选择(默认 auto)
--preprocess图片预处理(放大 2x/灰度/对比度/降噪),低清图用
--selftest生成含中文测试图并对比各引擎

AI 阅读配置(模型如何消费 OCR 输出)

本技能的核心场景:把图片/PDF 转成文本喂给不具备视觉输入的模型(如 DeepSeek-V4-Flash 等文本模型),或作为视觉通道的备用/交叉验证。

五步阅读流程

  1. 定位文件:dsh 附件在 ~/.dsh/attachments/v1/objects/<xx>/<sha256>,无扩展名先复制为 .png/.jpg 再识别
  2. 执行识别python scripts/ocr_tool.py "<文件路径>" --json
  3. 解析 JSON:读 lines/pages 字段(勿依赖控制台文本,防 GBK 显示乱码)
  4. 静默纠正:形近字(L/I、O/0、全角点、漏字)结合上下文纠正
  5. 汇报:完整文字 + 低置信行备注

置信度语义(勿混用)

score 来源语义使用建议
win恒 100.0(API 不提供置信度)不能作为质量依据
tess真实平均置信度<80 需复核
rapid检测模型分数与 tess 不可直接比较

引擎选择、预处理、输出结构等细节见下方「工作原理」各节。


工作原理(详细)

1. 总体流程

CLI 入口 (argparse)
  └─ ocr_file(path, engine, preprocess)        # 统一入口
       ├─ 扩展名校验: .pdf → PDF 管线; 图片扩展名 → 图片管线; 其他 → 报错
       ├─ 图片: ocr_image() → 引擎链 → [(text, score), ...]
       └─ PDF:   pdf_to_images() 渲染每页 → 逐页 ocr_image() → pages
       └─ 组装 {ok, type, engine, lines|pages, text}

全程离线:不调用任何云端 API,无网络依赖(仅首次 pip install 需要网络)。

2. 引擎选择与降级链(auto 语义)

各引擎可用性惰性探测并缓存(进程内只探测一次):

探测函数判定方式失败后果
_winocr_ready()import winocr + 枚举系统 OCR 语言win 不可用
_rapidocr_ready()import rapidocrrapid 不可用
_find_tesseract()依次试 PATH → scoop/shims/tesseract.exeProgram Files/Tesseract-OCRtess 不可用

auto 模式的执行链ocr_image):

  1. win(可用时):识别 → 有结果立即返回
  2. rapid(可用时):win 无结果才轮到它
  3. tess(兜底):前两者无结果或不可用时

关键语义:"有结果" = 返回非空行列表。win 可用但输出为空(如空白图、异常图),会静默继续走 rapid/tess——这是自动降级,不报错。

3. Windows 原生 OCR(winocr)★首选

  • 封装系统内置 Windows.Media.Ocr(WinRT/UWP API),调用 winocr.recognize_pil_sync(img, lang)
  • 语言选择_winocr_pick_lang() 从系统可用语言中优先挑含 zh/hans/cn 的标签;一个都没有时退回第一个可用语言(可能不是中文!)
  • 置信度语义:Windows OCR API 不提供逐行置信度,脚本对每行标记 score=100.0——100 分不代表"完美",只代表"该行已返回"
  • 中文精度最高、零下载、系统内置
  • 已知行为:中文之间会自动插空格(探 索),由 _clean_win_text() 清理(见第 8 节)

4. Tesseract(兜底)

  • 定位方式见上表;命令行调用:tesseract <图> <out> -l chi_sim+eng --psm 3 tsv(psm 3 = 自动分页)
  • 解析 TSV 输出(text/conf/left/top 列):
    • top // 8 分桶(纵向 8px 内视为同一行)
    • 桶内按 left 排序拼接 → 得到行文本
    • 行置信度 = 桶内各词 conf 的平均值(真实置信度,可作质量参考)
  • 低清图敏感:engine==tess强制走预处理管线(见第 7 节)
  • 中文精度明显低于 Windows 原生 OCR,仅作回退

5. RapidOCR(可选增强)

  • rapidocr.RapidOCR() 惰性单例(进程内只初始化一次),基于 onnxruntime + PaddleOCR 检测/识别模型
  • 适合复杂版式/表格(检测框更细);未安装时自动跳过
  • 依赖 onnxruntime:Python 3.14 无兼容 wheel,这是"必须用 3.11"的根本原因

6. PDF 处理

三级策略(pdf_to_images,dpi=200):

  1. pypdfium2:逐页渲染为位图 → OCR(扫描件也能识别,首选)
  2. PyMuPDF (fitz):pypdfium2 缺失时兜底渲染
  3. pypdf:仅在前两者都缺失时,尝试直接抽取文本层(对扫描件/图片型 PDF 无效,返回空)

注意:即使渲染成功,扫描件若无文字层、图片又模糊,OCR 结果可能为空页——结果里空文本页会被过滤(if t)。

7. 图像预处理管线(preprocess_image

--preprocess 或 tess 引擎时执行,顺序:

  1. 放大 2x(LANCZOS 高质量重采样)——小字变清晰
  2. 转 RGB → 灰度(去除色彩干扰)
  3. 自动对比度拉伸(autocontrast)
  4. 对比度增强 1.5x
  5. 高斯模糊 0.5(轻微降噪,去椒盐/扫描噪点)

另外:win/auto 引擎即使不预处理,也会做 1.5x LANCZOS 放大(保留色彩),因为 Windows OCR 对尺寸敏感。

8. CJK 空格清理(_clean_win_text

两条正则:

  • 删除 CJK 字符之间的空格:探 索 未 至 之 境探索未至之境
  • 删除 CJK 与中文标点之间的空格(,。!?;:等)

英文单词之间的空格保留(如 P/N: 12345 不乱拼)——这是设计上"只清 CJK"的原因。

9. 输出结构

// 图片
{ "ok": true, "type": "image", "engine": "auto",
  "lines": [ { "text": "供应商: 延锋 ASQE", "score": 100.0 } ],
  "text": "供应商: 延锋 ASQE\n零件编号 P/N: 1234567890" }

// PDF
{ "ok": true, "type": "pdf", "engine": "auto",
  "pages": [ { "page": 1, "text": "第 1 页文字" } ] }

// 失败(统一形态)
{ "ok": false, "error": "不支持的扩展名 .xyz" }

stdout 强制 UTF-8 输出(sys.stdout.reconfigure),避免控制台编码问题。


可能出现的错误与排查

A. 环境 / 依赖类

现象原因处理
ModuleNotFoundError: winocr首选引擎未装pip install winocr;装前会自动降级 tess,不影响使用
中文识别乱码/全是拼音系统缺 zh-Hans OCR 语言包,winocr 退回第一个可用语言Win10:设置→语言→添加"中文(简体)"→安装 OCR 组件;Win11 通常自带
ModuleNotFoundError: onnxruntime 或 rapidocr 导入失败rapidocr 依赖安装不全,或 Python 3.14 无 wheel用系统 Python 3.11;pip install rapidocr onnxruntime
引擎列表为空(--selftest 输出"无可用引擎")winocr 未装 且 tesseract 未装/未在 PATH至少装 winocr 或 tesseract 之一
Tesseract 报语言错误缺 chi_sim 语言包scoop install tesseract-languages 或手动放 tessdata

B. 输入文件类

现象原因处理
不支持的扩展名扩展名不在白名单(含 dsh 附件无扩展名 的情况)复制为 .png/.jpg 临时文件再识别
文件不存在: ...路径错/相对路径基准不对用绝对路径,先 Test-Path 确认
OCR 结果全空图片损坏、纯色图、或内容极小换图;--preprocess 放大后重试
PDF 每页都空扫描件且渲染库缺失,或图片极模糊装 pypdfium2;确认 PDF 非加密

C. 引擎行为类

现象原因处理
明明 win 可用,结果却是 tess 风格(中文被拆碎)auto 链中 win 返回空(图异常)静默降级--engine win 强制;或 --jsonengine 字段确认实际引擎
同一张图两次结果不同引擎不同(auto 降级)固定 --engine
tess 输出把两行并成一行TSV 行合并按 top//8 分桶,行距 <8px 的会合并预处理放大后重试(行距拉开)
大 PDF 内存暴涨/变慢全部页面一次性渲染(dpi=200)拆分 PDF 分批识别

D. 输出 / 编码类

现象原因处理
PowerShell 控制台中文乱码PS 5.1 默认 GBK 解码 UTF-8 输出内容本身正确;用 --json 或重定向到 UTF-8 文件读取
JSON 里中文变 \uXXXX正常转义(ensure_ascii=False 已关,但终端显示可能转义)程序解析时自动还原,不影响

E. 精度类(务必理解评分语义)

  • win 的 score 恒为 100.0:不是置信度!Windows OCR 不提供;不能靠分数判断质量
  • tess 的 score 是真实平均置信度(TSV conf 均值),<80 的行要留意
  • rapid 的 score 来自检测模型,语义与 tess 不同
  • 形近字误差(高置信度也可能错):L/I、O/0、全角"."vs 半角"."、漏字("示例"→"小例")→ 结合上下文静默纠正,只给最终结果
  • 数字/零件号等关键字段:识别后必须对照上下文校验(如 8 vs B、1 vs l)
  • 低清/斜体/艺术字:预处理可救一部分;旋转超过 15° 建议先人工摆正

验证记录

  • Windows 原生 OCR 对中文测试图 5 行全部 100% 识别(自测通过:探索未至之境 / 中文识别精度测试 / 供应商: 延锋 ASQE / 零件编号 P/N: 1234567890)
  • 真实业务截图:供应商/零件号/不良率/电话 5 行识别,手机号 13800138000 100% 正确
  • Agent 端到端调用退出码 0;dsh 附件(无扩展名)复制为 .png 后识别成功

许可

MIT