资讯详情

PaddleOCR打包exe离线部署实战:PyInstaller避坑与体积裁剪

📅 2026/10/10 18:13:01 | 华诺云谱 👁 阅读
PaddleOCR打包exe离线部署实战:PyInstaller避坑与体积裁剪
简介这是一份面向无Python环境用户的PaddleOCR离线文字识别工具打包资源适合需要将OCR能力部署到Windows端、仅凭图片路径即可获取识别结果的开发者与运维人员。压缩包共2000个文件约279.22MB以319个py源码、319个pyc字节码、232个pyd扩展模块和139个dll动态库为核心构成完整的Python运行时与PaddleOCR依赖另有57个txt说明、25个png与11个gif示例图、10个ini配置及大量时区数据文件保证离线环境下的稳定运行。已有3786人学习下载。资源内含可直接运行的exe工具与配套脚本读者能获得从模型调用、图片路径输入到结果写入txt的完整链路并参考错误处理、批量识别与界面优化思路快速搭建属于自己的离线OCR工具。1. 把 PaddleOCR 塞进一个 exe离线场景下最省心的交付方式上个月帮一个做仓储盘点的朋友处理过一件事他们仓库里十几台 Windows 工控机网络是内网隔离的装不了 Python更别提 pip 在线拉依赖。需求很朴素——拍一张货架标签照片识别出上面的物料编号。这种场景下把 PaddleOCR 打包成一个双击就能跑的 exe几乎是唯一能让现场人员接受的交付形态。PaddleOCR 本身是百度开源的 OCR 工具库中文识别效果在开源方案里属于第一梯队支持检测、方向分类、识别三段式流水线也能直接调PaddleOCR类一把梭。但它的依赖链不轻PaddlePaddle 推理库、模型文件、OpenCV、numpy、shapely、pyclipper 一大串直接pyinstaller打包十有八九会翻车。这篇笔记就围绕「PaddleOCR 打包 exe 离线工具」这件事把选型、打包脚本、模型外置、路径处理、体积裁剪和常见报错一条条拆开讲目标是你照着能复现出一个在无网 Windows 上稳定运行的识别工具。适合两类人一类是要给客户或现场交付离线 OCR 能力的工程师另一类是自己想把 PaddleOCR 做成桌面小工具、又不想被环境问题反复折磨的开发者。2. 打包前的技术选型PyInstaller、模型外置与推理引擎2.1 为什么默认推荐 PyInstaller 而不是 NuitkaPython 转 exe 的常见方案有 PyInstaller、cx_Freeze、Nuitka、py2exe 几种。PaddleOCR 这种带大量 C 扩展和动态库的项目我一般会先上 PyInstaller原因是它对二进制依赖的收集逻辑最成熟--collect-all和 hook 机制能省掉大量手工拷贝 dll 的活。Nuitka 编译成真正机器码启动快、体积小但它对 Paddle 这种含自定义算子的库兼容性坑更多编译一次动辄十几分钟调试成本高。cx_Freeze 配置写起来更啰嗦社区针对 Paddle 的现成 hook 少。所以除非你对启动速度有硬指标否则 PyInstaller 是性价比最高的起点。需要提醒的是PyInstaller 打包出来的不是「编译后的程序」而是把 Python 解释器和依赖一起塞进一个自解压结构运行时会在临时目录展开这一点直接决定了后面模型路径和资源定位的写法。2.2 模型外置还是打进 exePaddleOCR 默认会去下载轻量模型检测约几 MB、识别约十几 MB、方向分类约 1MB 多首次运行联网拉取。离线场景绝对不能依赖这个行为必须把模型文件提前准备好。两种做法一是把模型目录一起打进 exe二是模型放在 exe 同级目录外置。我强烈建议外置。原因有三第一打包进去会让 exe 体积膨胀且每次换模型都要重新打包第二PyInstaller 单文件模式下资源在临时目录模型路径容易写错第三外置模型方便现场替换成更高精度的服务器版模型。目录结构建议长这样ocr_tool/ ├── ocr_tool.exe ├── models/ │ ├── det/ # 检测模型 inference.pdmodel inference.pdiparams │ ├── rec/ # 识别模型 │ └── cls/ # 方向分类模型 └── config.ini # 可选放阈值等参数模型文件从 PaddleOCR 官方模型库下载后解压每个目录里应有inference.pdmodel、inference.pdiparams、inference.pdiparams.info三个文件。识别模型还要带ppocr_keys_v1.txt字典文件这个字典漏了会直接报 key 数量不匹配。2.3 推理引擎与依赖版本锁定PaddleOCR 底层跑的是 PaddlePaddle 推理库CPU 版就够用除非现场有 NVIDIA 显卡且愿意装 CUDA。离线交付我基本只用 CPU 版paddlepaddle不装paddlepaddle-gpu因为 GPU 版对驱动和 CUDA 版本极其敏感工控机上大概率没有。版本上要锁死PaddleOCR 和 PaddlePaddle 的版本兼容关系比较微妙建议用一组经过验证的组合比如paddleocr2.7.x配paddlepaddle2.5.x具体以你本地能跑通的为准锁进requirements.txt。另外opencv-python建议换成opencv-python-headless省掉 GUI 相关的 dll打包体积和报错都会少一截。# 建议在干净虚拟环境里装依赖避免把无关包打进去 python -m venv venv_ocr venv_ocr\Scripts\activate pip install paddlepaddle2.5.2 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install paddleocr2.7.3 pip install opencv-python-headless pyinstaller这里用清华源是为了装包快离线机器上不需要。装完后先别急着打包务必在虚拟环境里跑通一次识别确认模型能加载、图片能出结果再进入打包环节。很多打包失败其实是环境本身就没跑通被误判成打包问题。3. 打包脚本实操spec 文件、隐藏导入与资源收集3.1 先写一个最小可用的识别入口打包前需要一个明确的入口脚本不要直接拿 PaddleOCR 的示例改。入口要处理三件事定位模型目录、初始化 OCR 对象、接收命令行参数或简单 GUI。下面是一个命令行版入口够用且好调试# main.py import os import sys import argparse from paddleocr import PaddleOCR def get_base_dir(): 兼容 PyInstaller 打包后的路径定位 if getattr(sys, frozen, False): # 打包后 exe 所在目录 return os.path.dirname(sys.executable) # 源码运行时的当前文件目录 return os.path.dirname(os.path.abspath(__file__)) def build_ocr(): base get_base_dir() model_root os.path.join(base, models) ocr PaddleOCR( use_angle_clsTrue, langch, det_model_diros.path.join(model_root, det), rec_model_diros.path.join(model_root, rec), cls_model_diros.path.join(model_root, cls), rec_char_dict_pathos.path.join(model_root, rec, ppocr_keys_v1.txt), use_gpuFalse, show_logFalse, ) return ocr def main(): parser argparse.ArgumentParser() parser.add_argument(--img, requiredTrue, help待识别图片路径) args parser.parse_args() ocr build_ocr() result ocr.ocr(args.img, clsTrue) for line in result[0] or []: box, (text, score) line print(f{text}\t{score:.4f}) if __name__ __main__: main()逻辑说明get_base_dir是关键sys.frozen是 PyInstaller 运行时注入的标志打包后必须用sys.executable的目录来定位外置模型否则会跑到临时解压目录里找不到文件。det_model_dir等参数显式指定模型路径避免 PaddleOCR 触发自动下载。use_gpuFalse强制 CPU。show_logFalse关掉冗余日志现场看着干净。参数上use_angle_clsTrue会多跑一个方向分类模型对拍歪的标签有用代价是慢一点如果图片方向固定可以关掉省时间。3.2 用 spec 文件精确控制打包过程直接敲pyinstaller -F main.py大概率失败因为 PaddleOCR 有大量动态导入和隐式依赖PyInstaller 静态分析扫不到。正确做法是先生成 spec 再改pyinstaller --name ocr_tool --onedir main.py注意这里先用--onedir目录模式而不是--onefile。单文件模式启动时要把所有东西解压到临时目录Paddle 的 dll 加载在这种场景下更容易出问题而且启动慢。目录模式交付时把整个文件夹压缩给现场一样方便。生成ocr_tool.spec后重点改Analysis的hiddenimports和datas# ocr_tool.spec 关键片段 from PyInstaller.utils.hooks import collect_all, collect_submodules paddle_datas, paddle_bins, paddle_hidden collect_all(paddle) ocr_datas, ocr_bins, ocr_hidden collect_all(paddleocr) shapely_datas, shapely_bins, shapely_hidden collect_all(shapely) a Analysis( [main.py], pathex[], binariespaddle_bins ocr_bins shapely_bins, dataspaddle_datas ocr_datas shapely_datas, hiddenimportspaddle_hidden ocr_hidden shapely_hidden [ pyclipper, skimage, imgaug, scipy, lmdb, ], hookspath[], runtime_hooks[], excludes[matplotlib, tkinter, PyQt5], noarchiveFalse, )逻辑说明collect_all会把包的二进制、数据文件和子模块一次性收全这是解决 Paddle 打包缺 dll 最省事的办法。hiddenimports里补的几个是 PaddleOCR 运行时动态导入、静态分析抓不到的模块pyclipper和shapely用于检测框后处理lmdb在部分版本里会被引用。excludes排除 matplotlib、tkinter 这些用不到的大块头能砍掉几十 MB。参数上noarchiveFalse保持默认即可改成 True 会牺牲启动速度换体积不划算。3.3 打包与首次验证改完 spec 执行pyinstaller ocr_tool.spec --noconfirm产物在dist/ocr_tool/下。把models/目录拷到ocr_tool.exe同级然后拿一张测试图跑cd dist/ocr_tool ocr_tool.exe --img test_label.jpg能打印出文字和置信度就算通了。第一次跑建议在没有 Python 环境的机器上验证或者至少在一个干净的用户账户下跑因为开发机上往往有全局的 Python 和 dll会掩盖缺失依赖的问题。这一步是血泪经验我见过太多次「我电脑上好好的客户那边一打开就闪退」。4. 避坑与排查打包后最常见的五类翻车4.1 现象双击 exe 一闪而过命令行看报错是ModuleNotFoundError原因PyInstaller 静态分析漏掉了动态导入的模块PaddleOCR 内部大量用importlib和字符串导入扫不到。解决在 spec 的hiddenimports里补上缺失模块名或者干脆用collect_submodules(paddleocr)全量收。定位方法是在命令行里运行 exe看 traceback 最后一行缺哪个模块逐个补。别一次补一堆容易引入新问题。4.2 现象报FileNotFoundError找不到ppocr_keys_v1.txt或模型文件原因模型和字典没跟着走或者路径用了相对当前工作目录的写法。PyInstaller 打包后工作目录可能不是 exe 所在目录。解决统一用get_base_dir()那种基于sys.executable的绝对路径定位模型外置到 exe 同级。字典文件确认在rec目录下且文件名一致识别模型和字典必须配套用错字典会输出乱码。4.3 现象启动时报DLL load failed或找不到 msvcp140.dll原因Paddle 依赖的 VC 运行库在目标机器上缺失或者打包时漏了某个 dll。解决一是让现场装一遍 Microsoft Visual C Redistributable二是用collect_all(paddle)确保 dll 被收进去三是用 Dependency Walker 或dumpbin /dependents查 exe 依赖了哪些 dll逐个核对。工控机系统版本老的话这个坑概率很高。4.4 现象识别结果为空但程序不报错原因图片路径传错、图片格式 OpenCV 读不了比如某些 CMYK 的 jpg、或者检测阈值把框过滤掉了。解决先在源码环境用同一张图跑确认模型本身没问题再检查打包后传入的路径是不是被 shell 转义了。如果是低对比度标签调det_db_box_thresh和det_db_thresh参数适当降低阈值。参数在PaddleOCR()初始化时传比如det_db_box_thresh0.3。4.5 现象exe 体积超过 1GB或者启动要十几秒原因把整个 paddle 包连同测试数据、无用子模块都打进去了或者用了单文件模式。解决用excludes排除 matplotlib、tkinter、PyQt、IPython 等确认用的是--onedircollect_all虽然省事但会收进一些用不到的东西体积敏感时可以改成collect_dynamic_libs加手工指定 datas。启动慢主要是单文件解压导致换目录模式立竿见影。5. 进阶技巧体积裁剪、批量识别与一个自检习惯5.1 把体积从 1GB 压到 400MB 左右collect_all(paddle)会把 Paddle 的静态库、头文件、测试资源都收进来实际运行只需要动态库和少量数据。体积敏感时可以改成只收动态库from PyInstaller.utils.hooks import collect_dynamic_libs, collect_data_files paddle_bins collect_dynamic_libs(paddle) paddle_datas collect_data_files(paddle, include_py_filesFalse)collect_dynamic_libs只抓.dll/.socollect_data_files抓非 Python 的数据文件include_py_filesFalse避免把源码再塞一遍。配合excludes排除paddle.distributed、paddle.jit、paddle.fluid如果版本还用不到这些大模块能砍掉不少。裁剪后一定要重新跑一遍识别验证别为了体积把功能砍没了。5.2 批量识别与结果落盘现场往往不是识别一张而是一个文件夹的图片。入口脚本可以扩展成批量模式把结果写成 CSV方便后续导入系统import csv import glob def batch_ocr(ocr, img_dir, out_csv): files glob.glob(os.path.join(img_dir, *.jpg)) \ glob.glob(os.path.join(img_dir, *.png)) with open(out_csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([file, text, score]) for fp in files: res ocr.ocr(fp, clsTrue) for line in (res[0] or []): _, (text, score) line writer.writerow([os.path.basename(fp), text, f{score:.4f}])encodingutf-8-sig是为了 Excel 打开 CSV 不乱码这个细节现场很受用。批量模式下建议把show_log关掉否则日志刷屏。如果图片量大可以考虑把 OCR 对象初始化一次复用别每张图都重新PaddleOCR()初始化模型加载很耗时。5.3 一个我强制自己走的自检流程打包这类工具我现在的习惯是每次改完 spec 或模型先在开发机跑通然后把 dist 目录整个拷到一台没装过 Python 的干净 Windows 上断网双击运行拿三张不同类型的图清晰标签、倾斜标签、低对比度标签各测一遍再跑一次批量模式。这个流程走完交付翻车概率能降到很低。有一次偷懒跳过了干净机器验证结果客户现场因为缺一个 VC 运行库全员卡住从那以后我每次打包完都强制走一遍这个自检再急也不省。希望这套流程能帮到你少走点我踩过的弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑