浏览器里跑 OCR:PaddleOCR + ONNX WASM 是怎么做到不上传图片的

· 约 5 分钟 🔍 OCR 文字识别

OCR(Optical Character Recognition)传统是云端能力——图片传上去,服务端跑大模型,结果传回来。本工具反其道而行:模型权重 + 推理引擎全部塞进浏览器,原图永不离开你的电脑。这条路怎么走通的?

一句话架构

图片字节 → DBNet 检测网络 → 文本框坐标列表
         → CRNN + CTC 识别网络(逐框)→ 字符序列
         → 联动 UI 显示

两个神经网络都是用 PaddleOCR PP-OCRv4 mobile 训练,导出成 ONNX 格式,由 ONNX Runtime Web 在浏览器 WebAssembly 后端推理。整套流水线总共 27 MB 静态资源,首次下载后浏览器缓存。

第一段:检测(DBNet)

任务:输入一张完整截图,输出”哪里有文字”——一组矩形(或多边形)坐标。

PP-OCRv4 检测用的是 DBNet(Differentiable Binarization Network),架构是:

MobileNetV3 backbone → FPN(特征金字塔)→ 二值化分支

输出一张和原图同尺寸的概率图,每个像素值 0-1 表示”这里属于某个文字区域的中心”。后处理用 OpenCV 的轮廓检测把概率图变成多边形坐标。

关键参数

  • 输入分辨率 640 × N(短边 640,长边按比例)
  • det_db_thresh 0.3:像素被算作文本中心的阈值
  • det_db_box_thresh 0.6:整个框被保留的阈值
  • det_db_unclip_ratio 1.5:把多边形向外扩张的倍数(避免裁掉笔画末端)

工具页加 ?debug=1 能直接看到这张概率图的热力图,对调试漏检很有帮助。

第二段:识别(CRNN + CTC)

任务:输入一个裁剪好的文本框小图(高度 32 px),输出字符序列。

PP-OCRv4 识别用的是经典的 CRNN(Convolutional Recurrent Neural Network):

MobileNetV3 → 双向 LSTM → 全连接层 → CTC 解码

CTC(Connectionist Temporal Classification)是关键——它解决了”输入特征序列长度不等于输出字符长度”的问题。具体做法:

  1. 把图片宽度切成 N 个 token(每个 token 对应几个像素列)
  2. 每个 token 独立预测字符概率(含一个特殊”空白”符号 -
  3. 解码规则:相邻重复字符合并(hheelloohello),删除 -

这样模型不需要标注”第 5 个字符在哪一列”,只要给它一张图和正确的字符串,它自己能学出来。

字符表 6622 字 + 1 个 blank,对应 6623 维 softmax 输出。

为什么用两阶段而不是端到端

学术界一直在做端到端 OCR(输入完整图,直接输出全部文本),但工业实践里两阶段更稳

维度两阶段端到端
训练数据检测 / 识别可独立扩充必须配对完整标注
错误隔离漏检 vs 错字可分别归因黑盒,难调试
长文本框大小可任意受输入分辨率限制
UI 联动天然有 box 坐标需额外注意力图
模型体积mobile 版 15 MB端到端通常 50 MB+

代价是检测错了识别就跟着错——但失败模式可解释,运维更容易。

为什么不上 WebGPU

ONNX Runtime Web 支持三个后端:

后端速度兼容性
WASM (SIMD)1× 基线Chrome / Firefox / Safari 全部
WebGL2-3×主流,但精度有损
WebGPU3-5×Chrome / Firefox 已稳定,iOS Safari 仍 flag 内

截至 2026 年初,iOS Safari WebGPU 还在功能开关后面。如果切到 WebGPU 后端,iPhone 用户会全员降级到 WASM——还多一段 fallback 逻辑。为了兼容性放弃了 3-5× 加速,等 iOS 全量推开再说。

27 MB 都装了什么

文件大小作用
det_model.onnx4.5 MBDBNet 检测网络权重
rec_model.onnx10.3 MBCRNN 识别网络权重
dict.txt26 KB6622 字符表
ort-wasm-simd.wasm12 MBONNX Runtime SIMD 版

加起来约 27 MB。首次下载后浏览器 HTTP 缓存——下次访问只校验 ETag,不重新传输。25 MB 这个大小放本地软件不算什么,但放浏览器初次访问比较显眼,好在只一次。

iOS Safari 的特殊兜底

WebAssembly 在 iOS Safari 里有一个长期 bug:模块化 Worker(type: "module")在某些情况下会丢失主线程消息状态。早期版本工具页在 iOS 上识别会卡死或返回空结果。

修复路径(见 commit 1648cba):

  1. 降级到经典 Workernew Worker(url) 不带 type: "module"
  2. recognize 自动重试:单帧识别失败重试一次,避开偶发的状态丢失
  3. 诊断面板?debug=1 显示推理步骤耗时,定位卡在哪一段

iOS 16 以下偶发,iOS 17+ 几乎不见。Android Chrome、桌面端从未触发过。

准确率天花板

通用印刷体可达 92-95%,但下面几类永远过不去

  • 手写体行草:< 50%,模型训练集没有这种数据
  • 艺术字 / Logo:不稳定,字体扭曲超出训练分布
  • 小于 16 px 的字:检测网络分辨率限制,根本看不见
  • 字典外字符:罕见汉字、化学符号、emoji 会被替换成形近字

要更高需要垂直模型——发票字段、身份证号、车牌——百度/阿里云的付费 OCR 在这些场景能做到 99%+,但他们的模型几个 GB,浏览器扛不动。本工具定位是通用、隐私、零成本,不是医疗/金融级精度。

怎么验证真的不上传

打开 OCR 工具,按 F12 → Network 面板:

  1. 勾选 “Disable cache”
  2. 刷新页面,看到 5 个对 /libs/paddle-ocr/* 的同源请求(首次 27 MB)
  3. 拖入一张图片识别
  4. 整个识别过程应该 0 个新出站请求

如果你看到任何对外 IP 或域名的请求,那是有问题。我们的承诺是:图片字节、识别结果永远只在浏览器内存里,刷新页面立即释放。

用得对、用得省

  • 大批量同质图片(如几百张同款发票)→ 还是去用百度云 OCR + 字段抽取,本地通用模型不划算
  • 临时几张敏感截图 → 这工具是最优解,不上传 + 离线
  • 需要导出 JSON/坐标信息 → 当前版本不支持,仅复制文本
  • PDF 文件(文字层)→ 用 PDF 提取文字,比 OCR 快百倍且无错
  • PDF 文件(扫描件)→ 先 PDF 转图片 再丢这里

OCR 不是魔法。理解检测 + 识别两段流水线,知道字典 6622 字的边界、知道 16 px 是检测下限,遇到识别失败就能立刻判断是哪一环出了问题。

❓ 常见问题

为什么不直接调云端 OCR API,非要在浏览器里跑?

三个原因。隐私——合同、票据、身份证按合规要求不能上传第三方;本地推理永远不出网。成本——百度/腾讯/阿里云 OCR 大约每千次 1.5-10 元,一个免费工具调不起。离线可用——首次缓存 27 MB 模型后,断网状态下仍能识别,地铁/飞机/园区内网都能用。代价是准确率低于云端垂直模型(发票/身份证字段抽取场景),通用印刷体仍可达 90%+。

PP-OCRv4 mobile 模型为什么只有 15 MB?

PaddleOCR 把检测和识别拆成两个轻量网络。检测用的是 DBNet(Differentiable Binarization),主干 MobileNetV3 + FPN,4.5 MB;识别用的是 CRNN + CTC(卷积 + 双向 LSTM),主干同样是 MobileNetV3,10.3 MB。和 PaddleOCR server 版(约 200 MB)比,mobile 版做了知识蒸馏 + 通道剪枝,精度损失约 2-3 个百分点换来 13× 体积压缩——这才能塞进浏览器。

ONNX Runtime Web 跟 PaddlePaddle 是什么关系?

模型在飞桨里训练完,导出成 ONNX 格式(开放神经网络交换标准)就和原生框架解耦了。浏览器端的运行时是微软的 ONNX Runtime Web,把 ONNX 算子映射到 WebAssembly / WebGL / WebGPU 三种后端。本工具用 WASM 后端——兼容性最好(Safari 也能跑),代价是没用到 GPU 算力。模型文件不再依赖 PaddlePaddle 的运行时,理论上还能跑在 Python ORT、Java ORT、C++ ORT 上。

为什么不用 WebGPU 加速?

Safari 还没全量支持。截至 2026 年 Safari 17.4 起 macOS 端默认开启 WebGPU,但 iOS Safari 仍在 feature flag 后面。如果上 WebGPU 后端,iPhone 用户全员降级到 CPU 还会触发额外的 fallback 路径——更复杂。当前 WASM 后端在 SIMD 开启下单线程跑 PP-OCRv4-mobile 大约每文本框 50-200 ms,500 字以内 2-5 秒能完事,是可接受的。WebGPU 提升约 3-5× 速度,等 iOS 跟上再切。

检测和识别为什么要分两个网络?端到端不行吗?

端到端 OCR 学界一直在做(如 ABINet、TrOCR),但工业场景两阶段更稳检测网络只回答"哪里有字"——输出文本框坐标,对字体、内容完全不挑;识别网络只回答"这个框里写了啥"——输入是裁剪好的小图,专门优化字符特征。拆开训练数据可以分别扩充(检测用 100 万张布局图,识别用 1000 万段裁剪图),任意一段坏了不会污染另一段。端到端模型在长文本、多语言、复杂布局上鲁棒性较差,且不易做 box-level 的 UI 联动。

模型支持哪些字符?

6622 字——基本覆盖 GB2312 一二级常用汉字 + 阿拉伯数字 + 大小写英文 + 常用标点。字典外字符包括:罕见汉字(如人名生僻字)、繁体特殊字形、化学元素符号(β、γ、Å)、emoji、希腊字母大部分。模型遇到字典外字会输出形近字(最相似字符)或乱码,不会留空。需要更全字符集请用云端 OCR 或 PP-OCRv5(约 200 MB,浏览器扛不动)。

为什么我的文本框检测不到?

DBNet 检测网络默认输入分辨率约 640 × N,字符高度低于 16 px 时会被采样丢掉。三个修复路径——(1) 放大原图:截图工具或图片处理软件先 2× 放大;(2) 裁切关注区域:长截图先裁出文本部分,避免文字被压扁;(3) 检查对比度:白底黑字最佳,半透明水印、淡灰背景上的浅色字检测器看不见,先调高对比度再扔进来。工具页加 ?debug=1 会显示检测框热力图,可直接看到为什么漏检。

🔍 打开 OCR 文字识别 图片转文字·中英文混排·PaddleOCR PP-OCRv4·拖拽/粘贴/选文件·框选定位·一键复制·浏览器本地不上传

📖 同一工具的其他教程