
简介这份离线文字识别依赖库基于 RapidOcr 与 Onnxruntime 实现面向需要在本地完成 OCR 的开发者解决云端识别依赖网络、隐私易泄露等问题。包内包含 991 个文件、约 192.61MB涵盖 C/Python 头文件hpp/h、ONNX 模型文件、动态库so、静态库a及 CMake 构建配置还附有 OpenCV 相关静态库可支撑 Android、Linux 等多平台编译与移植。目前已吸引 1481 人学习查看。通过学习读者能掌握 Onnxruntime 加载与推理流程理解图像预处理、模型执行及结果后处理的完整链路同时包内丰富的 cmake、ninja 等构建文件能帮助开发者快速集成 OCR 能力到自有项目压缩包内的 txt/json 文件也可作为参数与配置参考降低离线识别方案落地门槛。适合对 OCR 技术感兴趣的算法人员与嵌入式开发者。 近几年我在本地部署文字识别OCR时被 RPA 抓取、票据翻拍、车牌号提取这些真实项目反复折腾。联网接口一断就崩数据出境更是碰都不敢碰。后来我把目光锁定在 RapidOcr 和 Onnxruntime 上搭了一套完全离线的文字识别管线。效果很稳。今天把整个集成思路、依赖库选型、踩过的坑和报错复盘原原本本写出来。这套方案最核心的价值就是“离线可用”模型和推理运行时全部落在本地没有网络请求也没有数据外传。我手头有一批内部表格图片每天要自动提取关键字段敏感度又高RapidOcr 配合 onnxruntime 的动态库就成了最简单的自托管解。这套东西在 Windows 和 Linux 下都能跑单张 CPU 推理耗时在 300-600ms 区间看图片分辨率对绝大多数内部工具完全够。如果你正打算做离线 OCR又不想引入繁重的 PaddleOCR 全家桶或者调云接口这篇文章应该能帮你少走很多弯路。1. 整体设计与思路拆解1.1 为什么选择 RapidOcr 而非 PaddleOCR 或 Tesseract三套方案我都实际用过差别不在准不准而在“落地成本”。Tesseract 是老牌开源方案识别印刷体英文和数字效果不错但中文长文本、表格混排、低分辨率截图上的表现比较吃力。PaddleOCR 识别精度高模型也多但框架依赖非常重——PaddlePaddle 全家桶自带一大堆动态库部署包动辄几百 MB有些精简环境根本塞不进去。RapidOcr 的定位恰好补齐了这两者的短板模型来自 PaddleOCR 的 PP-OCR 系列但推理引擎换成了 onnxruntime依赖更干净、体积更小、跨平台性更好而且有 C 动态库也有 Python 封装能快速嵌入到现有服务里。实际用下来我对 RapidOcr 的判断是如果你想在“精度不错”和“部署轻量”之间取一个平衡点它是最理想的选择。1.2 Onnxruntime 在链路中的角色Onnxruntime 是微软开源的推理引擎它把训练好的模型比如 PaddleOCR 导出的 onnx 格式模型加载进来然后跑在 CPU、GPU 或者 NPU 上。很多人容易把“OCR”和“推理引擎”混为一谈其实这是两层东西。我打个比方RapidOcr 是餐厅的菜单和厨师onnxruntime 是后厨的灶台和炒锅。模型决定识别的“手艺”推理引擎决定“上菜速度”。RapidOcr 只是把 PaddleOCR 的模型导成了 onnx 格式然后用 onnxruntime 来做底层的算子计算两者的关系是协作而不是绑定。这意味着你可以自己控制 onnxruntime 的版本甚至针对 CPU 指令集做定制编译获得更好的性能。1.3 依赖库架构与调用流程RapidOcr 在推理时的完整数据流是这样的图片输入 - 预处理缩放/归一化 - 文本检测DBNet - 方向分类可选 - 文本识别CRNN - 后处理解码 - 文本输出每个环节在 RapidOcr 目录里都有对应模型文件。我建议你用 Onnxruntime 的 Python API 或者 C API 分别加载这三个模型而不是混在同一个 Session 里。原因有三个检测、分类、识别模型输入尺寸不同分开 Session 可以单独控制内存分配。如果某一步计算失败可以单独排查不至于整个链路崩掉。后续如果识别模型升级了可以只替换对应 onnx 文件不用动其他模块。2. 依赖库选型与关键版本约定2.1 全套依赖清单与版本建议我目前稳定跑在生产环境的一套组合是这样组件版本说明rapidocr_onnxruntime1.3.24核心库内置模型与推理封装onnxruntime1.15.1CPU 版自带 OpenMP 多线程支持opencv-python4.8.0.76图像预处理与可视化numpy1.24.3数组操作注意与 onnxruntime 兼容Pillow10.0.0可选处理大图转码别小看版本号这里每个版本都是踩过坑之后定下来的。onnxruntime 1.15.x 算是一个分水岭。1.13 之前对高分辨率图片偶尔会有算子兼容问题1.16 之后 C API 变化了一些但 Python 端还好。我测试过 1.14.1 和 1.15.1后者在纯 CPU 环境下单线程延迟低了不少推荐优先这个版本。Python 版本建议 3.9 或 3.10。3.11 以上我在某些 Linux 环境下遇到过 onnxruntime 预编译包加载异常排查成本很高除非必要不要冒险。2.2 到底要不要自己编译 onnxruntime我见过很多帖子劝人“源码编译 onnxruntime 以获得极致性能”。我的意见是绝大多数场景下不要编。理由很直白预编译包已经启用了 CPU 指令集优化AVX、AVX2直接 pip 安装即可吃满。源码编译需要安装 cmake、CUDA如果用 GPU、protobuf 等一堆工具链一搞就是半天出错的概率极高。RapidOcr 的模型是全卷积网络没有特别冷门的算子预编译包完全能撑起来。唯一的例外如果你的目标机器是 ARM 架构比如树莓派或某些国产开发板或者你确实需要裁剪 so 体积那么自己编译才有意义。否则老老实实用 pip 装最高效。2.3 动态库文件的位置与获取方式RapidOcr 的下载包解压后模型文件位于models目录下一般包含models/ det.onnx cls.onnx rec.onnx dict_chi_sim.txt dict_en.txt需要特别说清楚dict_*.txt是字典文件也就是模型输出索引到中文字符的映射表。如果没有这个文件识别出来全是乱码。很多人只下载了三个 onnx 模型就跑去加载缺失字典文件导致输出错乱这个不建议。onnxruntime 的动态库onnxruntime.dll / libonnxruntime.so在 pip 安装后有对应的文件位置。如果你是纯 Python 调用不需要手动指定如果是 C 集成需要把这个动态库路径加入系统的库搜索路径然后把头文件目录也加进来。3. 核心集成实操与参数调优3.1 快速跑通 Python 版识别先用一段最简单的代码验证整个链路是否通畅from rapidocr_onnxruntime import RapidOCR engine RapidOCR() result, elapse engine(test.png) print(result) print(elapse)result的结构是每个文本框的坐标、置信度、文本内容和得分。第一次初始化会比较慢因为要加载三个模型并分配内存后续单张识别就快很多。如果这一步正常输出说明你环境装对了。但如果在from rapidocr_onnxruntime import RapidOCR就报 DLL 加载失败通常是 onnxruntime 的动态库依赖缺失比如 VC 运行库没装或者 Python 架构和包架构不一致32 位/64 位混用。3.2 解析单张图片的完整输出结构识别结果里嵌套的信息很关键。result的原始返回结构是这样的[[box, text, score], ...]box是四个点坐标格式为[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]表示文本框的四边形定位。text是识别出的字符串。score是置信度范围 0~1。实际项目中我用得最多的是box坐标当需要把识别结果按位置排序、或者把人名和金额字段一一对应时坐标信息比单纯文本有价值得多。比如发票识别我按 y 坐标排序后再按 x 坐标从左到右归组就能准确把“商品名称”和“金额”列对齐。3.3 参数调节哪些值得动哪些别乱动RapidOCR 的初始化参数里有几个我用下来对识别效果影响最大参数默认值我的建议det_use_dnnFalse保持 FalseONNX 原生算子和 DNN 模块的速度在这里没有优势cls_use_dnnFalse同上rec_batch_num6调大可以提升批量识别吞吐但要小心显存/内存占用det_limit_side_len736对长图有帮助但图片分辨率越大耗时越高需要平衡det_limit_side_len这个参数尤其值得注意。它控制检测阶段输入图片的边长上限。如果原图是 4000 像素宽的长截图直接丢进去会被严重压缩导致小字漏检。我会根据实际场景把上限抬到 960 或者 1280但代价是 CPU 耗时显著上升。实测一张 1920x1080 的截图736 上限时约 450ms1280 上限时约 900ms翻了一倍。所以如果没有小字密集场景保持默认即可。3.4 C 调用 onnxruntime 动态库的示例如果你的主程序是 C需要通过 onnxruntime 的 C API 加载 RapidOcr 模型。简化后的核心步骤大概如下#include onnxruntime/core/session/onnxruntime_cxx_api.h Ort::Env env(ORT_LOGGING_LEVEL_WARNING, rapidocr); Ort::SessionOptions options; options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); options.SetIntraOpNumThreads(4); Ort::Session det_session(env, Lmodels/det.onnx, options);这里SetIntraOpNumThreads(4)是控制单算子内部的线程数。实测在 8 核 CPU 上4 线程比 8 线程吞吐更高因为线程上下文切换的开销超过了算子并行收益。另外ORT_ENABLE_ALL开启后会做图优化能小幅提速但首次加载时间会变长如果内存吃紧可以降到ORT_ENABLE_EXTENDED。C 路线的启动流程比 Python 长你需要自己写图像预处理缩放、归一化、letterbox、三个 Session 的数据传递、后处理解码和字典映射。我在实际项目中用 C 主要图一个部署干净没有 Python 解释器依赖但代码量会翻几倍。如果业务并发不高推荐先用 Python 快速验证算法效果再用 C 重写性能敏感部分。4. 常见问题与排错过程全记录4.1 chromadb backend init failed 报错的真相有一个报错我觉得有必要单独拿出来讲因为很多人在装 RapidOcr 时莫名其妙碰到它其实跟 RapidOcr 本身没关系。报错全文大概是chromadb backend init failed, falling back: the onnxruntime python package is not installed这个报错的逻辑是chromadb这个向量数据库在初始化时内部尝试导入一个叫onnxruntime的 Python 包来跑 embedding 模型。如果你的环境里已经装了 RapidOcr 使用的 onnxruntime理论上应该能直接导入成功但如果两个包存在版本冲突或者系统里存在一个损坏的 onnxruntime pip 包装了一半chromadb 就会把异常抓走然后打出“falling back”的提示。排查思路先看pip show onnxruntime确认版本是否正常。单独执行python -c import onnxruntime; print(onnxruntime.__version__)如果这一步也报错说明 onnxruntime 本体就没装好。如果这个 import 正常那就检查 chromadb 版本的兼容性尝试升级或降级chromadb。我当时是在一个同时跑 RAG 服务的机器上部署机器里既有 chromadb 又有 RapidOcrPython 环境比较杂。后来检查发现是onnxruntime的安装位置不对——另一个虚拟环境里的包串到当前环境导致符号冲突。我的处理方式是用虚拟环境隔离两个服务互不干扰。如果你也需要在同一环境里同时用 chromadb 和 RapidOcr建议锁定 onnxruntime1.15.1 并升级 chromadb 到最新版我实测这样能消除报错。4.2 onnxruntime 加载失败错误码 5060的处理onnxruntime 5060几乎每个深入用过的人都会撞到一次。错误码本质上是 Windows 下LoadLibrary失败常见原因是 DLL 依赖缺失尤其是 msvcp140.dll、vcruntime140.dll、vcomp140.dll 这些 VC 运行库。解决方案并不复杂去微软官网安装最新的 VC Redistributablex64 版本。确认有且只有一个 onnxruntime.dll 在搜索路径中不要同时混装 CPU 版和 GPU 版。如果是 Python 环境直接卸载重装pip uninstall onnxruntime -y pip install onnxruntime1.15.1如果这些都不行用 Dependency Walker 或 PE-bear 打开 onnxruntime.dll 查看具体缺失的依赖项。我遇到过最隐蔽的情况是系统里有旧版openmp.dll和 onnxruntime 自带的开源 OpenMP 实现冲突导致启动即崩。解决方法是把 onnxruntime 安装目录下的libomp.dll复制到 exe 同目录强制优先加载它。4.3 有时能识别有时完全空白怎么定位这种情况我在跑一批倾斜字体的截图时经常遇到。如果结果为空第一步不是调参而是先检查图片的预处理结果img cv2.imread(test.png) cv2.imwrite(debug.png, img)导入失败或者通道顺序不对都会让检测器直接失明。RapidOcr 内部用的是 BGR 还是 RGB 有一定讲究我的经验是先用 OpenCV 读图然后走默认流程最稳。如果图片本身没问题再检查det_limit_side_len比如很宽的横幅文字会被压到无法检测。还有一种常见问题图片背景复杂、文字与背景对比度低。RapidOcr 的检测模型对低对比度场景会漏检这时候最简单的做法是先用 OpenCV 做一次对比度增强或灰度化比调任何参数都有效。4.4 内存占用持续增长的问题我长期跑服务时发现内存会缓慢上涨初步怀疑是图片预处理时 OpenCV 的矩阵没有释放。RapidOcr 内部每次推理都会创建新的 OrtValue正常情况下 Python 的引用计数会自动回收缓存但如果你用executor或线程池反复调用同一引擎一定要确认有没有保存推理结果的引用。如果发现问题不好定位直接用下面这行代码加上强制回收import gc result, elapse engine(test.png) gc.collect()当然这只是治标。真正的治本方案是避免在循环内重复创建RapidOCR实例整个进程只初始化一次复用同一个引擎。我实测复用实例比反复创建新实例的内存占用低大约 40%这点在长驻服务里体现得很明显。5. 实际场景适配与性能观察5.1 身份证和票据类高分辨率图的适配票据图片通常分辨率高、字段密集而且有多行、有表格。我在处理 300dpi 扫描件时先做一次等比缩放把长边控制在 2000 像素左右然后交给 RapidOcr。太高的分辨率不会提升识别率反而让检测模型把无关边缘噪声也框进来产生大量低置信度结果。对于字段对齐我的后处理方案是results.sort(keylambda item: (item[0][0][1], item[0][0][0]))也就是先按 y 坐标排序再按 x 坐标排。这一步对表格类的字段提取非常关键因为 OCR 模型的输出顺序并不保证跟视觉阅读顺序一致。5.2 CPU 推理性能实测数据我拿一台普通办公机Intel i5-1040016GB 内存跑了一批 1280x960 的截图统计结果如下操作平均耗时文本检测220ms方向分类30ms文本识别150ms整体单张400ms 左右这个速度虽然是“毫秒级”但和 GPU 动辄几十毫秒的体验还是差很多。如果只是做定时批处理完全够用如果是线上实时 OCR建议考虑 GPU 版 onnxruntime 或者提升硬件规格。5.3 批量识别的并发与线程配置如果一次要处理几百张图片不要简单地开线程池到处调用同一个 RapidOCR 实例。onnxruntime 的 Session 本身是线程安全的但 CPU 资源有限多线程并发反而会因为资源竞争导致单张耗时暴涨。我的做法是先量好机器的核心数然后按照线程数 核心数 / 2的规则开进程池。每个进程里独立创建 RapidOCR 实例再切分图片列表分发给各进程。实测 8 核机器上开 4 个进程总体吞吐是单进程的 2.8 倍效果明显。继续往上加进程收益变小因为内存带宽成了瓶颈。6. 长期运行后的体会调完这套离线识别之后我最大的感受是“可控性”带来的安心感。云端 OCR 接口虽然快但要么有 QPS 限制要么对图片大小有要求批量任务一旦跑起来就像在悬崖边走钢丝。RapidOcr 加 onnxruntime 的组合虽然需要自己处理一些细节但一切都在掌控内模型想换就换线程数想调就调。有几点再啰嗦一下模型文件一旦下载就不要再从临时目录读取放到固定路径方便后续版本升级时直接覆盖。如果识别质量严重下滑优先检查 dict 文件和模型是否来自同一个版本混用不同版本容易出诡异问题。不要盲目追新版本稳定性远比“看起来更强”重要。1.15.1 这个版本我在生产环境跑了快一年没有任何问题。后续如果你想继续深入可以把这个引擎封装成 HTTP 服务或者用 pybind11 包一层给 C# 调用。方向很多但底座已经稳了后面的事情都是水到渠成。本文还有配套的精品资源点击获取