麒麟V10 aarch64部署RapidOCR:内网环境完整踩坑与解决方案
2026/9/9 7:19:23 网站建设 项目流程

先说结论:RapidOCR 在麒麟 V10(aarch64)上完全能跑起来,但绝对不是“pip install 一下就完事”的项目。我在内网环境下整整折腾了两天半,踩过平台不匹配、缺失系统动态库、模型自动下载失败、中文路径乱码这些坑,最终把识别服务稳定跑起来了。这篇文章把整个过程和解决方案完整还原,给后面要在国产化 ARM 环境上做 OCR 的朋友做个参考。

这里说句掏心窝的话:aarch64 环境最大的问题不是“装不上”,而是很多 Python 包在 PyPI 上的 aarch64 wheel 并不全。尤其 onnxruntime 这种带原生代码的库,装错一个版本就是连环坑。而麒麟 V10 又经常是内网环境,没法临时去网上找依赖……所以这篇文章的核心思路是:先弄懂每个组件在 aarch64 上的支持情况,再决定怎么装。

如果你正打算在麒麟 V10 / aarch64 上部署 RapidOCR,或者已经装了一半卡在某个报错上,这篇文章应该能帮你省下大半天的排查时间。

1. 为什么最终选了 RapidOCR:一次内网环境的 OCR 选型过程

1.1 需求本身是什么

我这次的场景比较简单:一台麒麟 V10 服务器,aarch64 架构,CPU 是国产飞腾系列,内存 16G,无 GPU,系统盘、数据盘都是常规配置。要做的事情是,把一批扫描 PDF 和图片里的中文文字提取出来,供后续系统检索和录入使用。因为是内网环境,不能连接外网下载模型或依赖,软件包来源受限,这给部署增加了不少难度。

1.2 主流 OCR 方案的对比

部署前我先把市面主流的 OCR 方案在脑内过了一圈:

  • PaddleOCR:识别效果确实好,但 PaddlePaddle 框架本身比较大,在 aarch64 上要么自己编译 paddlepaddle,要么依赖官方提供的 whl。而且 PaddleOCR 完整安装后依赖很重,对内存和磁盘要求高,我这个 16G 内存的机器跑起来有点勉强,部署体积也大。
  • Tesseract:安装简单,但对中文识别的精度一般,尤其遇到复杂排版、倾斜文本、低清扫描件时,效果明显不如基于深度学习模型的方案。训练自己的模型又需要额外工作量。
  • 商用 OCR:识别效果好,但需要联网调用或授权,内网环境下合规和成本都是问题。

最后选了 RapidOCR。这个项目最吸引我的点是:它把 PaddleOCR 的模型转换成了 ONNX 格式,推理时不需要装 PaddlePaddle,只需要 onnxruntime 这一个带原生代码的推理引擎。这就把“安装 Paddle 全家桶”的问题缩小成了“搞定 onnxruntime 一个包”的问题。更重要的是,RapidOCR 对运行环境要求低,纯 CPU 就能跑,非常适合内网服务器这种没有 GPU 的场景。

1.3 RapidOCR 的组件和部署形态

RapidOCR 并不是一个单体程序,而是一套组件的组合。核心是三个模型文件:检测模型(det)负责框出文本区域,方向分类模型(cls)负责把旋转的文本方向纠正,识别模型(rec)负责把文本区域转换成字符。三个模型都是 .onnx 格式,由 Python 包 rapidocr_onnxruntime 统一加载调用。

封装形式上,RapidOCR 提供了 Python API 和命令行工具,实际使用中以 Python API 居多。对于我这个项目,最终要把它封装成一个 HTTP 服务,供业务系统调用。这个部署形态决定了我在后续操作中要格外注意:模型加载次数、并发处理方式、内存占用等问题。因为一旦做成服务,就不是“跑一条命令行”那么简单了。

2. 动手前先摸清家底:麒麟 V10 (aarch64) 环境检查

2.1 系统版本与 CPU 架构确认

拿到机器第一件事,不是急着装包,而是先把系统信息摸清楚。我用下面几条命令确认了基本盘:

cat /etc/os-release uname -m cat /proc/cpuinfo | grep -E "model name|processor" | head -n 20

uname -m输出是aarch64,确认这是 64 位 ARM 架构,和常见的 x86_64 完全不同。/etc/os-release显示麒麟 V10 的某个 SP 版本。/proc/cpuinfo显示处理器是飞腾系列,支持的指令集是 armv8 这一档。

这里为什么要强调“先看架构”?因为 aarch64 环境下,很多 pip 包默认下载到的 wheel 可能是为 x86_64 编译的,硬装会直接报not a supported wheel on this platform。系统源、Python 版本、动态库情况也都要以 aarch64 为准去评估。

2.2 Python 和 pip 环境的坑

麒麟 V10 系统自带的 Python 版本通常比较老,不同 SP 版本带的 Python 3 版本不完全一样。我建议在部署 RapidOCR 前,先确认当前的 Python 和 pip 版本:

python3 --version pip3 --version

如果系统自带 Python3 版本太旧(比如 3.6),尽量用系统包管理装一个新一点的 Python3,或者找内网源里现成的更高版本。RapidOCR 官方对 Python 版本有要求,太老的 Python 版本会导致某些依赖的 wheel 不存在或者语法不兼容。

还有个容易忽略的点:pip 默认源。在内网环境里,如果不配置内网 pip 源,pip install 会一直卡在连接超时上。配置方法很简单:

mkdir -p ~/.pip cat > ~/.pip/pip.conf <<EOF [global] index-url = http://内网pip源地址/simple trusted-host = 内网pip源地址 EOF

如果没有内网 pip 源,那就只能在有外网的机器上把依赖包下载成 whl 文件,再用pip install --no-index --find-links=/路径 whl文件离线安装。这一步在后面模型下载那部分还会遇到。

2.3 内网环境下如何准备依赖

内网部署最忌讳“边装边找包”。我给自己的规矩是:先在测试环境把依赖梳理清楚,再打包搬运到目标机。这一步可以在外网或者有网的机器上做:

pip download rapidocr_onnxruntime -d /tmp/rapidocr_pkgs --platform manylinux2014_aarch64 --only-binary=:all:

--platform manylinux2014_aarch64--only-binary=:all:能确保下载的是 aarch64 的预编译 wheel,而不是源码包。如果你是 x86_64 机器上现跑的 Python 环境,直接pip download可能下载到 x86_64 的包,到目标机后装不上,所以这个平台参数一定要加。

下载完成后,把整个目录拷贝到内网机器上,再用pip install --no-index --find-links=/tmp/rapidocr_pkgs rapidocr_onnxruntime安装。注意--no-index一定要加,否则 pip 仍会试图访问外网源。如果内网机器还需要通过固定路由访问某些服务,别忘了提前确认路由表,避免依赖包下好了却传不进目标机这种尴尬事。

3. 安装阶段最大的坑:onnxruntime 的 aarch64 支持

3.1 直接 pip 安装会得到什么

我在测试机上有网环境下直接执行了:

pip install rapidocr_onnxruntime

结果装完之后,导入时没报错,用户还挺高兴。但一到执行识别,onnxruntime 初始化 InferenceSession 的时候直接抛了异常,具体的错误信息大概类似Failed to load library或者Error at inference。这种问题往往不是 RapidOCR 本身的问题,而是 onnxruntime 安装的版本和系统动态库不匹配。

后来把 onnxruntime 卸载重装,指定版本才解决。所以我的建议是:在 aarch64 上,不要让 pip 自动解析 onnxruntime 版本,必须显式指定一个你验证过的版本。

3.2 定位问题:是平台标签不匹配

如果手动下载 wheel 文件,最容易翻车的就是平台标签不匹配。比如在内网机器上执行:

pip install onnxruntime-1.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

pip 会直接提示:

ERROR: onnxruntime-1.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl is not a supported wheel on this platform

这种情况在 aarch64 上非常典型。下载 whl 前先看文件名,比如onnxruntime-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl,看到aarch64字样才对。还有一种情况是 Python 版本和 whl 的 cp 标签不匹配。例如你的 Python 是 3.8,却下了 cp39 的包,pip 同样会拒绝安装。下载前先python3 -V确认版本,然后下载对应 cp 标签的 whl。

3.3 正确安装方式和版本选择

在多次尝试后,我确定下来一套稳定的版本组合,这里直接给结论:

  • Python 3.8 或 3.9
  • onnxruntime==1.15.1(aarch64 manylinux 版本,注意不同版本对 glibc 版本有要求)
  • opencv-python-headless(不要装 opencv-python,服务器没有 GUI 时 opencv-python 会缺 libGL.so.1)
  • numpy 版本不要太高,1.24 左右比较稳,避免和 onnxruntime 的 ABI 冲突

安装命令:

pip install numpy==1.24.4 pip install onnxruntime==1.15.1 pip install opencv-python-headless pip install rapidocr_onnxruntime

提示:上述版本组合是我在飞腾平台上验证过的。如果你用的是鲲鹏 CPU 或者更新的 SP 版本,onnxruntime 版本可以适当上调,但改动后必须重新压测识别效果,不要直接上生产。

这里解释一下为什么 opencv 一定要用 headless 版本:麒麟 V10 服务器版通常没有安装桌面环境和 X11 相关库,opencv-python 自带的 cv2 在导入时会去加载 libGL.so.1,找不到就报 ImportError。headless 版本则去掉了 GUI 相关依赖,只保留图像处理和编解码能力,正好满足 OCR 场景。

3.4 还需要装哪些系统依赖(libgomp 等)

onnxruntime 在 aarch64 上还依赖 OpenMP 运行时库 libgomp。如果系统里没有这个库,导入 onnxruntime 时会报:

ImportError: libgomp.so.1: cannot open shared object file: No such file or directory

这个错误很典型。解决方法是:

yum install -y libgomp

如果 yum 源里搜不到,可以试yum search libgomp或者从安装光盘、内网 yum 仓库里找对应的 aarch64 rpm 包。这个库很小,但漏掉它时排查起来特别隐蔽,因为它不是 Python 包,很多人会以为只是编译问题。

4. 模型文件内网下发:RapidOCR 自动下载模型的“隐形依赖”

4.1 首次运行会触发模型下载

RapidOCR 首次运行时如果检测不到模型文件,会自动从远程仓库下载三个 .onnx 模型文件。外网环境可能无感,但在内网环境下,这一步会一直卡住或者超时。

具体表现是执行engine = RapidOCR()之后,日志显示正在下载模型,然后长时间无响应。如果网络完全不通,最终会报连接失败。这种“运行时报错”比安装报错更难定位,因为你可能以为代码没问题,实际上是模型文件缺失。

4.2 模型文件的获取与手动放置

解决方法是提前在有外网的机器上下载好模型文件。RapidOCR 的 GitHub Releases 页面提供了包含三个模型文件的压缩包,下载后解压会得到类似下面的结构:

models/ ch_PP-OCRv3_det_infer.onnx ch_PP-OCRv3_rec_infer.onnx ch_ppocr_mobile_v2.0_cls_infer.onnx

把这几个文件拷贝到内网机器上。放置位置有两种选择:

  • 放到 Python 环境中 RapidOCR 包自带的 models 目录下(替换或补齐同名文件)。这种方式的优点是简单,代码不用改;缺点是升级包时要留意模型是否被覆盖。
  • 放到自定义目录,创建 RapidOCR 实例时通过参数指定模型路径。这种方式更灵活,适合多环境部署。

RapidOCR 初始化时如果指定了模型路径,就不会再触发自动下载。推荐用第二种方式,因为部署到多台机器时,模型和代码可以分开管理。

4.3 模型版本和代码版本必须匹配

这是一个很容易被忽视的坑。RapidOCR 的 Python 包版本和模型文件版本是有对应关系的,如果代码升级了但模型还是旧版,或者反过来,可能会出现推理输出异常、shape 不匹配等莫名其妙的问题。

我遇到过一次:把 RapidOCR 从 1.2.x 升到 1.3.x 后,检测模型还是老的,结果输出结果里的坐标框明显偏移。最后重新下载配套模型文件才恢复正常。所以换版本时,一定把 Python 包和模型文件当作一个整体来更新,不要只改一半。

5. 运行时报错的完整排查链路:从 import 到第一次推理

5.1 ImportError: libgomp.so.1: cannot open shared object file

这个坑前面提过,但排查过程值得展开说一下。

我当时的报错链路是这样的:

python3 -c "import onnxruntime"

输出:

ImportError: libgomp.so.1: cannot open shared object file: No such file or directory

第一反应是重新装 onnxruntime,但没用。用ldd查看 onnxruntime 的 so 文件依赖:

ldd /usr/local/lib/python3.8/site-packages/onnxruntime/capi/libonnxruntime.so | grep "not found"

发现只有 libgomp.so.1 找不到。这时候才明白不是 Python 包的问题,而是系统的动态库缺失。用yum install -y libgomp装好后再执行ldd,所有依赖项都正常了。

这个排查思路适用于任何“Python 包导入时报找不到 so 文件”的情况:先用 ldd 定位缺哪个库,再有针对性地装系统包,不要盲目重装 Python 包。

5.2 UnicodeDecodeError / 中文路径问题

模型能加载了,接着测识别。第一张测试图片路径是/data/测试图片/发票.jpg,执行识别时报了 UnicodeDecodeError 或者图片读取失败。

原因在于 OpenCV 的cv2.imread在部分 Linux 环境下对中文路径支持不好,会返回 None,导致后续处理崩溃。RapidOCR 内部如果直接用 cv2 读图,会遇到这个问题。

解决办法是提前将图片读取为 numpy 数组再传给 RapidOCR:

import cv2 import numpy as np from rapidocr_onnxruntime import RapidOCR engine = RapidOCR() def read_image(path): img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR) return img img = read_image("/data/测试图片/发票.jpg") result = engine(img)

这样完整绕过了中文路径问题。这个技巧在业务系统对接时特别常用,因为业务文件路径往往带着中文目录名。

5.3 输入图片和预处理细节

RapidOCR 对输入图片有一些基本要求:图片太小或者大片空白区域时,检测框定位可能失败;纯白底图片直接返回空结果。这在测试时容易误判为“部署失败”,其实是图片本身没有可识别的文字。

另外,如果图片是 RGBA 四通道格式,部分版本会提示不支持或识别异常,最好统一转成 BGR 或 RGB:

if img.shape[2] == 4: img = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)

这类预处理逻辑建议封装在统一接口里,而不是每次调用时临时处理。业务方传过来的图五花八门,有截图、有手机拍照、有扫描件,统一走同一个预处理入口,遇到问题也好排查。

6. 性能实测与调优:让 CPU 推理从“吃力”到“够用”

6.1 线程数设置

aarch64 处理器核心数通常不少,但 onnxruntime 默认的线程调度未必能充分利用。在初始化 RapidOCR 时可以设置 intra_op_num_threads,控制推理时使用的线程数。

以我使用的版本为例:

engine = RapidOCR(intra_op_num_threads=4)

如果用的版本支持这个参数,直接传即可;不支持的话,也可以在初始化 onnxruntime 的 session options 里设置。线程数不是越大越好,实测 4 到 8 个线程时性能提升最明显,再往上会受内存带宽影响,收益递减。

6.2 量化模型替换

RapidOCR 官方提供了量化后的模型文件,文件名通常带_quant后缀,比如ch_PP-OCRv3_det_infer_quant.onnx。量化模型体积更小、推理速度更快,识别精度略有下降但大多数场景下可接受。

我的做法是:先用原始模型跑通流程,确认识别效果满足需求后,再替换成量化模型压测性能。如果精度下降在可接受范围,就切换到量化版,毕竟内网服务器的 CPU 资源也要考虑给其他业务留一部分。

6.3 实测耗时数据

下面是我在飞腾 CPU(8 核心)上的一组粗略测试数据,供参考。测试图片是一张 A4 大小、包含约 200 个汉字的扫描截图,单张识别。

模型类型线程数单张耗时
原始模型4约 2.1 秒
原始模型8约 1.6 秒
量化模型4约 1.2 秒
量化模型8约 0.9 秒

这个数据不是 CPU 满负载时测的,实际业务高峰可能更慢。但对比来说,量化模型 + 8 线程的组合在性能和准确率之间比较平衡,能满足大多数后台 OCR 场景。

6.4 批量场景的优化策略

如果业务是批量处理几百张图片,不要每次新建一个 RapidOCR 实例,那样会反复加载模型、白白浪费内存和 IO。正确做法是全局只初始化一个实例,循环调用:

engine = RapidOCR(intra_op_num_threads=4) for img in img_list: result = engine(img) # 处理结果

实测下来,复用实例比每次新建实例在批处理场景下能快近一倍,因为没有反复加载模型文件的开销。这一步优化代码改动很小,收益却很明显。

7. 服务化部署:封装成 HTTP 接口的经验

7.1 FastAPI 封装

识别能力调通之后,最终要提供给业务系统调用。RapidOCR 本身是 Python API,最直接的方式是用 FastAPI 包一层 HTTP 接口。示例代码如下:

import base64 import cv2 import numpy as np from fastapi import FastAPI from pydantic import BaseModel from rapidocr_onnxruntime import RapidOCR app = FastAPI() engine = RapidOCR(intra_op_num_threads=4) class OCRRequest(BaseModel): image_base64: str @app.post("/ocr") def ocr(req: OCRRequest): img_bytes = base64.b64decode(req.image_base64) img_array = np.frombuffer(img_bytes, dtype=np.uint8) img = cv2.imdecode(img_array, cv2.IMREAD_COLOR) result = engine(img) texts = [item[1] for item in result[0]] if result[0] else [] return {"texts": texts}

这样调用方只需要把图片转成 base64 传过来,服务端解析、识别、返回结构化文本,整个链路简单清晰。

7.2 uvicorn 和 systemd 开机自启

服务封装好后,用 uvicorn 启动:

uvicorn ocr_service:app --host 0.0.0.0 --

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询