ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

麒麟V10离线环境部署PaddleOCR全流程指南

麒麟V10离线环境部署PaddleOCR全流程指南 前阵子接手了一台银河麒麟V10服务器任务是在这台完全没有外网的生产机器上把 PaddleOCR 跑起来做批量图片文字识别。机器在隔离网络里不能pip install不能在线下载模型更不可能临时抱佛脚去拉镜像。折腾了差不多一周把 PaddleOCR 离线绿色部署整个流程摸透了中间踩了好几个坑包括文字识别乱码、字体缺失、依赖冲突、安全中心拦截。这篇文章把完整过程和思考逻辑写出来给要在麒麟系统上离线部署 OCR 的同行做个参考。这里说的“绿色部署”意思是不修改系统自带的 Python 环境、不往系统目录里塞东西、整个应用目录可以拷贝迁移部署包删掉之后系统完全不留残留。在办公网络中这种可迁移、可清理、可重复交付的特性特别重要也是我这次选择目录化部署而不是走系统级安装的原因。我要先说明一个现实麒麟系统的坑大多不在 PaddleOCR 本身而在于离线环境下的依赖补齐、路径认知和系统差异。把这三层想清楚部署就成了一半。1. 部署前夜我在麒麟上做OCR撞上的第一个现实问题1.1 拿到手的“离线环境”长什么样我拿到的机器是银河麒麟 V10 桌面版处理器是 x86_64 架构的商用台式机系统自带 Python 3.8。网络环境就是内网隔离物理上连不到外网所有软件包必须在其他机器上下载好用优盘或者移动硬盘拷进去。很多人对麒麟系统有误解觉得它是套壳Windows或者是什么特别的系统。实际上它就是一个基于 Linux 的发行版只是包管理器、目录组织和安全策略跟常见的 CentOS、Ubuntu 有一些差异。所以你在 CentOS 上部署 PaddleOCR 的思路在麒麟上基本同样适用。真正需要害怕的是三个变量机器架构是不是 x86_64、系统自带 Python 版本是多少、目标机器上有没有 GPU 或者专用加速卡。1.2 三个前提必须提前确认先别急着找安装包拿到机器先跑三条命令确认环境。uname -m # CPU架构 python3 --version # Python版本 lspci | grep -i -E vga|3d|nvidia # 有没有独立GPU这三条命令的输出决定了你整个部署方案能不能走通。x86_64 架构最省心PaddlePaddle 官方提供了 manylinux 的 x86 版 wheel 包直接下载就能用。如果是 aarch64 架构也就是飞腾、鲲鹏这一类命令行的查看方式是一样的但第三方依赖的坑会明显变多很多对有硬件加速的场景官方 wheel 的支持就不那么完善了后面部署要更注意版本选择。第二个看 Python。麒麟 V10 自带的 Python 版本一般比较固定有的是 3.7有的是 3.8。PaddleOCR 2.7 要求 Python 3.8 以上所以只要系统是 3.8 就问题不大。但我在另外一台干净系统上遇到过一个情况虽然 python3 是 3.8但系统只装了基础的 python3没装 python3-venv 和 python3-dev。这意味着你没办法用虚拟环境也没法编译任何带 C 扩展的第三方包。这种基础环境“缺牙”的问题在离线条件下非常致命。所以动手之前把 python3 -m venv test_env 这种命令先跑一下确认虚拟环境模块可用。第三个是 GPU。办公网络里利旧的机器绝大多数都没有独立 GPU纯 CPU 推理是常态。要是目标机器上有华为昇腾或者寒武纪 MLU 这类加速卡那其实已经脱离了“通用部署”的范畴你需要的是专门的推理引擎和模型转换工具链。这篇内容主要围绕纯 CPU 的通用场景来讲这也是遇到最多、需求最集中的场景。这三个前提确认完之后心里的部署思路就清晰了x86_64 麒麟 Python 3.8 CPU 推理对应一套完全离线的目录化部署方案。2. 离线包设计很多人只打包Python库结果在单位里三天没跑起来2.1 Python依赖只是第一层绝大多数人在准备离线部署时第一反应是在联网机器上 pip install 一遍然后 pip freeze 导出 requirements.txt再 pip download 把 wheel 包下载下来。这个思路没错但只完成了最基础的第一层。PaddleOCR 的完整依赖树远超你想象。基础的 paddlepaddle、numpy、opencv-python、Pillow 这些大块头自然不必说还有 shapely、pyclipper、scikit-image、imgaug、python-docx、pytz、tqdm、lmdb 这一堆间接依赖。有个 package 会依赖另一个 package那个 package 又依赖第三个层层嵌套。在 Linux 上很多包不是纯 Python 实现而是有编译好的二进制 wheel对系统 glibc 版本和 Python ABI 版本有严格匹配。我见过有人在联网机器上用 Python 3.10 的虚拟环境下载了全套依赖然后拷到只有 Python 3.7 的麒麟机器上想直接装结果一堆 cp310 标识的 wheel 在 cp37 环境下根本装不进去。这就是为什么版本锁定和架构核对这么重要后面会详细展开。2.2 系统动态库和字体依赖是第二层这一层是新手最容易忽略的。PaddlePaddle 和 OpenCV 在 import 阶段需要加载一些系统动态库比如 libGL.so.1、libgomp.so.1、libSM.so.6。麒麟系统如果装的是精简版桌面这些库不一定会预装。遇到的报错长这样ImportError: libGL.so.1: cannot open shared object file: No such file or directory在联网机器上一条apt install libgl1 libglib2.0-0就解决了。但在离线环境里你必须在其他机器上找齐 deb 包或者直接拷贝对应 .so 文件然后放到应用目录的 libs 子目录里再用 LD_LIBRARY_PATH 指过去。字体依赖也要提前考虑。PaddleOCR 的可视化模块在把识别结果画到图片上时需要一套支持中文的字体文件默认找的是 simfang.ttf。麒麟系统基本不会预装这种中文字体所以字体文件必须作为部署包的一部分带上。这个问题在后面的乱码排查里会详细说但准备阶段就要意识到字体不是可有可无的装饰而是 OCR 工具链可用性的组成部分。2.3 模型文件和运行环境是第三层最后一层是模型。PaddleOCR 本身只是推理框架真正干活的是检测、方向分类、识别这三个模型文件。离线部署必须提前下载好推理模型放进固定目录并在代码里明确指定模型路径。常规的 PaddleOCR 中文识别用到三个模型文本检测模型负责找出图片里哪些区域有文字通常用 ch_PP-OCRv4_det方向分类模型判断文本框是否需要旋转矫正用 ch_ppocr_mobile_v2.0_cls文字识别模型把检测出来的文本区域识别成字用 ch_PP-OCRv4_rec这三个模型加起来有两百多 MB。要是一台机器上部署多个业务系统模型文件可以共享但如果转发到外网隔离环境必须保证版本对应。用 v4 的检测模型配 v3 的识别模型单独看都能跑但整体识别效果没有经过官方适配验证出问题很难排查。建议直接用官方同版本的整套模型。模型下载通常是在联网机器上从 PaddleOCR 官方模型库下载然后再传到内网。下载完务必校验文件哈希内网传输过程中文件损坏是真实发生过的一个损坏的模型文件会让程序在推理阶段报出非常隐晦的错误。3. 在联网机器上准备离线介质的完整流程3.1 版本锁定先固定PaddleOCR与PaddlePaddle组合这一步是整个部署的基石我的建议是版本宁保守不激进。在我写这篇内容的时间点附近一个经过大量生产环境验证的组合是paddlepaddle 2.5.2CPU版本paddleocr 2.7.3Python 3.8为什么不用 PaddleOCR 3.x因为 3.x 版本的接口改动比较大识别结果返回的数据结构变了很多存量脚本需要改。在离线生产环境里没有搜索引擎帮你查新接口怎么用所以稳妥第一。2.7.3 的调用方式我闭着眼睛都能写出问题也好排查。在联网机器上先创建一个干净的 Python 3.8 环境然后用清华源或官方源安装固定版本的依赖。注意全部用 pip 安装二进制 wheel不要从源码编译。python3 -m venv build_env source build_env/bin/activate pip install paddlepaddle2.5.2 paddleocr2.7.3安装完成后先不要关环境直接跑一段 Python 代码验证 PaddleOCR 能不能正常初始化。这步如果不验证后面下载的依赖包里可能就会带一个残缺的依赖树。3.2 pip下载依赖并核对架构标签在 build_env 里跑下面这条命令把当前环境中所有已安装包连同依赖一起下载到本地目录pip download -d paddle_offline_pkgs -r (pip freeze)然后重点核对下载目录里的文件名。所有带 C 扩展的包文件命名里都会有 cp38-cp38-manylinux 或类似的标签。cp38 表示 Python 3.8 ABImanylinux 表示系统兼容性。如果你看到 cp39、cp310 字样说明你当前环境版本不对要回头检查。另外PaddlePaddle 官方包有时候不会出现在 PyPI 默认索引里需要在安装时通过额外索引地址指定。离线下载时也一样直接从官方索引把 cpu 版的 wheel 文件下载下来手动放到 pkgs 目录。下载完之后在联网机器上模拟离线安装验证一次mkdir test_install pip install --target test_install --no-index --find-links./paddle_offline_pkgs -r requirements.txt如果没有报依赖错误说明依赖集是完备的。requirements.txt 也从 pip freeze 生成确保版本的完全锁定。3.3 模型库、字体和说明文件一起入库依赖包验证通过之后把三件事一次性做完第一下载模型文件。按前面说的三个模型从官方模型库下载后解压放到部署包的 models 目录下保持目录名和代码里 det_model_dir、rec_model_dir、cls_model_dir 一一对应。第二准备字体文件。找到 simfang.ttf放到部署包的 fonts 目录。如果业务侧要求额外支持某种字体比如 Windows 办公环境常见的宋体、黑体也一并拷进去。第三写一份 README。这步很多人嫌麻烦但实际现场交付时特别有用。我在 README 里记录部署包清单、每个文件的 SHA256 值、Python 版本要求、部署路径建议、启动方式、常见问题。这样做的好处是当这个部署包被转手三四个人的时候不会有人打电话问你“这个目录下少了点什么”。最后打包tar -czf paddleocr_offline_x86_py38.tar.gz paddle_offline_pkgs requirements.txt models fonts到这里离线介质就准备好了。整个部署包大约 800MB 到 1GB用优盘或移动硬盘拷进内网。4. 麒麟系统内的“绿色”落地用--target目录代替venv4.1 为什么不直接用venv网上大量教程推荐用 virtualenv因为它能创建独立 Python 环境。但 virtualenv 有一个硬伤创建的虚拟环境在生成时会把 Python 解释器路径写进 bin/activate 和 pyvenv.cfg 里。你在联网机器上创建 /home/user/build_env挪到麒麟机器上变成 /opt/ocr_env直接激活是不能用的因为路径对不上。虽然网上有 virtualenv --relocatable 这种参数号称可以迁移但实测并不可靠会有一堆隐性问题。更干净的做法是这根本不用虚拟环境激活这个概念而是用 pip 的 --target 参数把三方依赖全部装到一个自定义目录然后用 PYTHONPATH 指过去。python3 -m pip install --target /opt/paddleocr_green/deps --no-index --find-links./paddle_offline_pkgs -r requirements.txt--target 的好处是依赖目录和业务代码、模型文件放在同一个根目录下整个目录结构完全自包含。想部署就整体拷到新机器想卸载就 rm -rf 整个目录系统零残留。和“绿色”这个词完全对得上。前提是目标机器的 Python 主版本和依赖包编译时的 Python 版本一致。只要都是 Python 3.8后面基本没有 ABI 兼容问题。4.2 部署目录结构与启动脚本我在麒麟机器上的最终目录结构长这样/opt/paddleocr_green/ ├── deps/ # 所有第三方Python包 ├── models/ │ ├── ch_PP-OCRv4_det_infer/ │ ├── ch_ppocr_mobile_v2.0_cls_infer/ │ └── ch_PP-OCRv4_rec_infer/ ├── fonts/ │ └── simfang.ttf ├── libs/ # 可选的系统动态库 │ ├── libGL.so.1 │ └── libgomp.so.1 ├── run_ocr.py ├── start.sh └── README.txt关键在 start.sh。这个脚本负责把环境变量切到应用目录让 Python 找到依赖库和动态链接库。#!/usr/bin/env bash BASE_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) export PYTHONPATH$BASE_DIR/deps:$PYTHONPATH export LD_LIBRARY_PATH$BASE_DIR/libs:$LD_LIBRARY_PATH export FONTS_DIR$BASE_DIR/fonts export MODELS_DIR$BASE_DIR/models python3 $BASE_DIR/run_ocr.py $run_ocr.py 的核心加载逻辑如下import os import sys BASE_DIR os.path.dirname(os.path.abspath(__file__)) os.environ.setdefault(MODELS_DIR, os.path.join(BASE_DIR, models)) os.environ.setdefault(FONTS_DIR, os.path.join(BASE_DIR, fonts)) from paddleocr import PaddleOCR MODELS_DIR os.environ[MODELS_DIR] ocr PaddleOCR( use_angle_clsTrue, langch, det_model_diros.path.join(MODELS_DIR, ch_PP-OCRv4_det_infer), rec_model_diros.path.join(MODELS_DIR, ch_PP-OCRv4_rec_infer), cls_model_diros.path.join(MODELS_DIR, ch_ppocr_mobile_v2.0_cls_infer), use_gpuFalse, show_logFalse, ) def recognize(image_path: str): result ocr.ocr(image_path, clsTrue) lines [] if result and result[0]: for line in result[0]: box, text_info line text, confidence text_info[0], text_info[1] lines.append({text: text, confidence: confidence, box: box}) return lines if __name__ __main__: if len(sys.argv) 2: print(Usage: python3 run_ocr.py image_path) sys.exit(1) for line in recognize(sys.argv[1]): print(f{line[confidence]:.4f}\t{line[text]})注意代码里给 PaddleOCR 显式传了模型目录这样程序永远不会因为当前工作目录变化而找不到模型。显式传全部三个路径比让框架自己猜更可控。4.3 首次运行验证清单在麒麟机器上跑完部署不要直接上业务图片按下面的清单快速验收先跑python3 -c import sys; print(sys.version)确认 Python 版本再跑PYTHONPATH/opt/paddleocr_green/deps python3 -c import paddle; print(paddle.__version__)验证 paddlepaddle 能被正确加载用ldd deps/paddle/libs/libpaddle.so | grep not found检查还有没有缺失的系统库找一张包含中文文字的图片跑./start.sh test.png看标准输出是否正常如果 import 阶段没问题但推理阶段报段错误多半是系统库版本对不上。这时候优先检查 libgomp.so.1 的版本在干净系统上这个是老出问题的对象。这一步全部通过绿色部署就算落地成功。剩下的就是应用对接和性能微调。5. 乱码问题的两次实战排查5.1 可视化画框乱码不是模型问题是字体路径丢了我第一次在麒麟系统上跑出一张带标注框的识别结果图时图片里检测框的位置全对但框上面的中文标签全部变成一个个方块和乱码。第一反应是模型坏了于是重下模型、重装依赖来回折腾两小时问题依旧。后来才意识到这些方块字根本不是识别结果而是可视化绘图模块需要用指定中文字体把文字画到图片上。系统里没这个字体绘图库就先用占位符替代画出来自然就是方块。PaddleOCR 源码里绘制文本的函数默认字体路径指向的是 paddleocr 包安装目录下的 doc/fonts/simfang.ttf。这个字体文件正常情况下会随 pip 包一起分发但某些裁剪版或者精简安装中会被丢弃。我在--target部署方式下遇到过一次就是因为依赖包被二次处理字体文件没完整保留。解决办法两步第一把 simfang.ttf 放到部署包的 fonts 目录第二在代码里显式给可视化函数传 font_path。如果是用 PaddleOCR 自带的 ocr.ocr 接口做可视化可以通过如下方式指定字体from paddleocr import draw_ocr font_path os.path.join(os.environ[FONTS_DIR], simfang.ttf) image draw_ocr( image, boxes, txts, scores, font_pathfont_path, )draw_ocr的 font_path 参数如果不传就从默认位置找找不到就静默回落成系统默认字体结果就是乱码。这个问题在离线环境中特别容易出现因为大多数办公网络里的机器没有额外安装繁体中文字体。5.2 识别结果本身乱码模型、字典与文件编码三处根因如果说画框乱码只是视觉瑕疵识别结果本身变成乱码就更致命了。有一次我把一张扫描的合同图片丢进程序识别出来的文本里所有中文都变成问号或者残缺的方块。排查链路是这样的。第一步先确认模型文件完整。拿部署前记录的文件哈希和当前文件哈希对比确定模型没有在拷贝过程中损坏。这一步起码能排除一半的“灵异事件”。第二步检查字典文件。PaddleOCR 的识别模型会配套 ppocr_keys_v1.txt 字典文件这个文件定义了模型输出 token 到实际汉字字符的映射。如果字典路径在部署时没有正确设置或者加载了不匹配的字典输出的“汉字”就会是毫无规律的字符。第三步排查终端编码。很多时候不是程序输出的字符串真的乱码而是 Linux 终端默认的 locale 没有设成 UTF-8导致控制台把已经正确解码的中文显示成乱码。这个误判很容易迷惑人。解决办法是在脚本里显式设置输出编码或者把结果写入文件后用编辑器打开检查。我当时最隐蔽的一个问题出在图片输入编码上。PaddleOCR 内部用 OpenCV 读取图片而 OpenCV 的 imread 对中文路径的支持很弱。当图片路径包含中文或者图片本身是灰度 PNG 但有异常的颜色通道信息时读进来的图像数据可能本身就是错的。识别自然就废了。稳妥做法是在代码里用 PIL 读图之后转成 RGB 再转给 PaddleOCR绕开 OpenCV 路径问题。from PIL import Image import numpy as np image Image.open(image_path).convert(RGB) image np.array(image) result ocr.ocr(image, clsTrue)把图片读取这一步改成 PIL 之后识别乱码问题就消失了。6. 性能实测与安全细节离线部署能跑多快怎么不被安全中心拦6.1 纯CPU推理的实测数据很多人关心麒麟系统上 CPU 推理到底多快。我实测的样本是 A4 扫描件300dpi 分辨率页面里包含标题、正文、表格混排单页文字量约 500 字。测试机器是 i5-850016GB 内存麒麟 V10 系统。完整流程检测 方向分类 识别。单页耗时约 1.2 到 1.8 秒。这个速度在纯 CPU 环境下属于正常水平做批量离线识别完全够用。但机器低端一些的话比如几年前的双核处理器单页可能飙到 3 秒以上。耗时瓶颈主要在文本检测。识别模型本身相对轻量检测模型要对整图做特征提取计算量最大。如果你的图片只是包含单行文字的截图可以考虑用 det_db_thresh 提高检测阈值减少多余的候选框数量能快不少。6.2 三项可以立刻做的性能调优第一设置 CPU 线程数。PaddlePaddle 在 CPU 推理时默认会占用所有核心但并不是线程越多越快。实测下来4 线程在这台 i5 机器上比默认全核更快因为减少了线程调度开销。在 Python 代码最前面加上import paddle paddle.set_num_threads(4)第二重复利用 PaddleOCR 实例。很多人图省事每次识别一张图就重新创建一个 PaddleOCR 对象。这样做非常慢因为每次创建都要重新加载两百多 MB 的模型。正确做法是在程序启动时创建一次之后全程复用。第三模型预热。模型加载后第一次推理会额外做很多初始化工作你拿第一张正式图片计时数据会虚高。正确做法是启动后先喂一张空白小图让框架完成所有初始化再开始计时正式任务。warmup np.zeros((64, 64, 3), dtypenp.uint8) ocr.ocr(warmup, clsTrue)这三项调整做完之后同机实测单页耗时从约 1.6 秒降到约 1.1 秒体感明显。6.3 安全中心、权限和分发介质注意事项麒麟 V10 自带安全中心里面有实时防护和文件系统扫描功能。部署包解压后如果安全中心对目录做全量扫描第一次启动 OCR 程序会明显卡顿因为它们要在进程启动时扫描可执行文件。有人误以为是程序卡死其实是在等待扫描完成。解决办法是把部署目录加入安全中心的信任列表或者排除目录。这个操作在图形界面的安全中心里可以配置命令行环境则可能需要修改应用白名单配置。具体的路径和命令名在不同版本上略有差异但总体上只要确认目录被程序扫描时排除了启动速度会恢复正常。权限方面我建议部署目录放在 /opt 下并用 root 先创建然后把所有权交给普通运维账号。长期用 root 跑 OCR 服务不是好习惯一旦程序被传入恶意图片触发漏洞root 权限的破坏面太大。最后说下分发介质。一个大容量 USB 优盘在麒麟系统里插上后提示打不开很多时候是因为优盘是 GPT 分区表 NTFS 格式部分精简版系统缺驱动。我建议分发介质用 exFAT 格式兼容性最好。如果是要在 Linux 服务器之间传递大量数据直接把优盘格式化成 ext4 更省事。插上之后如果没自动挂载执行sudo mount /dev/sdb1 /mnt/usb挂载再拷贝。整个流程走完你会发现在麒麟系统上离线部署 PaddleOCR 并没有想象中那么难。把 Python 依赖、系统库、模型文件和字体文件这四块东西一次备齐用 --target 方式做成自包含目录剩下的就是常规的 Python 程序调试。这套方法不只适用于 OCR你在麒麟系统上离线部署其他 Python 服务比如 Flask 应用、数据处理脚本思路完全一致。最后分享一个小技巧部署包里的 README一定要把每个文件的哈希值写清楚。现场交付时一份有哈希值的清单能省去大量远程排查时间。我当时把模型文件、依赖包、字体文件的 SHA256 全部记录成表后来在另一台机器上部署时运维同事自己就能核对文件完整性整个过程顺畅很多。
返回列表