简介PaddleOCR离线exe文字识别工具包面向需要在无Python环境下快速完成图片文字提取的开发者和办公人员。该工具包将百度开源的PaddleOCR模型及其运行依赖打包为可直接执行的Windows程序用户只需输入图片路径即可获得识别结果并保存为txt文件省去环境配置与命令行调用的繁琐步骤。压缩包内共2000个文件体积约279MB核心由Python源码与编译扩展py、pyc、pyd、动态链接库dll、配置与资源文件ini、txt、png、qm等组成其中pyd与dll保障离线运行时的调用效率txt和ini提供参数与说明支持。目前已有3789人学习下载适合批量文档数字化、截图文字提取、老旧扫描件转写等场景也可作为自动化流程中的OCR子模块调用。内含的离线模型与依赖库经过整合开箱即用也为希望了解PaddleOCR打包流程的开发者提供了可参考的目录结构和部署思路同时保留完整目录结构便于按需替换模型或微调参数。1. 没有 Python 环境也想跑 OCR把 PaddleOCR 打包成离线 exe 的实战记录接手一个模拟项目X的图像处理 Demo 时甲方给的机器是一台只有 Windows 的办公电脑没有 Python、没有外网还要求把 PaddleOCR 的文字识别能力做成一个双击就能用的工具给对方业务人员。PaddleOCR 本身是一个开源 OCR 工具链依赖几十个 Python 包和模型文件直接拿去跑一跑在开发机上没难度可要交付一个能离线双击运行的版本就把问题变成了「打包怎么不丢依赖、模型怎么跟着走、路径怎么不写死」。这篇文章我把拆完这个打包过程的核心步骤和坑位整理出来读者如果是做桌面工具交付的开发者或者想把 OCR 能力本地化的从业者应该能少走不少弯路。2. 离线推理前的准备工作模型选型与依赖关系要先把关2.1 PaddleOCR 推理的本质算法、模型与配置三个缺一不可PaddleOCR 不像普通 pip 库那样装完就能直接对任意图片做识别它在推理时依赖三块东西底层是检测、方向分类、识别三段式 pipeline中间是训练好的推理模型文件前端是描述模型路径和识别参数的配置文件。打包 exe 之前必须先把这三者的关系理清否则后面所有报错都会非常玄学。常见做法是使用 PaddleOCR 的predict接口它会自动加载指定目录下的模型结构文件和参数文件。检测模型负责定位文字行的边界框方向分类器负责把旋转的图片转正识别模型负责把文字行转换成字符串。三段之间数据流是串行关系任何一个模型文件缺失、损坏或者路径写错都会让整个识别过程直接翻车。配置方面我用的是PaddleOCR(det_model_dirmodels/det, rec_model_dirmodels/rec, cls_model_dirmodels/cls)风格的显式指定方式。这个方案比用paddleocr自带的默认模型下载机制更适合离线打包因为默认机制会去远端拉模型一旦环境没有外网就完全不可用。模型文件本身可以通过项目配套的脚本统一放到指定目录推荐目录结构如下./inference/ ├── det/ # 文本检测模型 ├── rec/ # 文本识别模型 └── cls/ # 方向分类器模型选择这个结构是为了让后续 PyInstaller 打包时能通过通配符一次性把模型文件全部收入包内也方便在代码中用相对路径定位。注意模型目录里不只是.pdmodel和.pdiparams文件还会有一个inference.yml或类似命名的配置文件这部分也必须保持完整。2.2 本地推理环境的设计Python 版本、依赖包与降级策略环境搭建这一步直接决定后面打包能不能省心。我选择 Python 3.8原因非常实际PaddlePaddle 的 Windows whl 包对 Python 版本支持比较全且 PyInstaller 对 3.8 的兼容性经过大量项目验证踩坑成本最低。如果你手头是 Python 3.10 甚至 3.11也不是不能用但个别依赖包可能要处理版本兼容问题效率上不如从 3.8 起步。依赖安装分两层。第一层是 PaddlePaddle 框架本体第二层是 PaddleOCR 及其附属依赖。这里有一个容易忽略的问题PaddleOCR 会默认把paddlepaddle装成 CPU 版但如果你机器上已经有 GPU 版两个版本容易互相覆盖。打包这种离线工具场景用 CPU 版足够识别速度在普通办公文档上完全可接受还免去把 CUDA 相关 DLL 塞进 exe 的痛苦。安装命令我建议分开执行方便确认每一层的版本状态pip install paddlepaddle2.5.2 -i https://mirror.simple.example/paddle pip install paddleocr2.7.0这里-i指定的是一个内网镜像源实际使用时请替换成你所在环境能访问的源。上面两个版本号是我验证过能稳定配合的一组如果你用别的版本组合需要在跑通第一步推理之后再考虑打包不要一上来就打包再排错那样问题范围会变得很大。2.3 一个最小化推理脚本打包前的功能验证基线环境搭好之后建议先写一个最小化脚本把 OCR 链路完整跑一遍。这个脚本的意义是作为打包前的功能基线后续所有打包造成的报错都和它做对比。from paddleocr import PaddleOCR ocr PaddleOCR( det_model_dirinference/det, rec_model_dirinference/rec, cls_model_dirinference/cls, use_angle_clsTrue, langch, use_gpuFalse ) result ocr.ocr(sample.jpg, clsTrue) for line in result[0]: print(line[1][0])这段代码的核心逻辑是初始化三个模型并对sample.jpg做完整识别。use_angle_clsTrue表示启用方向分类器对手机拍摄的歪斜文档很重要langch表示识别模型走中文字典use_gpuFalse强制走 CPU避免打包后的 exe 在无显卡机器上崩掉。运行成功后你会看到终端按行输出识别出的文本。如果这一步报错问题大概率在模型文件目录结构或环境依赖不要继续往后走打包流程。3. PyInstaller 打包从多目录依赖到单文件 exe 的完整流程3.1 打包前的环境瘦身策略PyInstaller 打包的核心逻辑是分析脚本的 import 关系把所有被引用的模块收集到一起。问题是 PaddleOCR 的依赖关系非常庞杂如果把整个 Python 环境都塞进去exe 体积会超过 1GB 且运行极慢。常见的做法是新建一个干净的虚拟环境只装运行 OCR 必需的库。我用venv而非conda管理虚拟环境因为 venv 更轻量打包出来的 exe 不会带一堆 conda 特有的关联动态库。创建虚拟环境后重新执行pip install paddlepaddle2.5.2、pip install paddleocr2.7.0然后运行一次最小化推理脚本确保新环境也能正常识别。这一步看似重复实际上是把环境依赖清单梳理清楚的最好办法后续打包如果漏依赖只需要在这个干净环境里逐个补。3.2 PyInstaller 命令的参数拆解打包命令我把所有必需的参数放在一个build_exe.bat脚本里方便重复执行。最终命令如下pyinstaller -F -w ^ --name OCRTool ^ --collect-all paddleocr ^ --collect-all paddle ^ --add-data inference;inference ^ --hidden-importimghdr ^ --hidden-importskimage ^ --exclude-module matplotlib ^ --exclude-module PIL.ImageQt ^ ocr_app.py逐项说明各自的作用-F表示生成单文件 exe启动时自解压到临时目录。单文件便于交付但启动速度会比目录模式慢 1-2 秒。-w表示不显示控制台窗口适合给业务人员用。但调试阶段建议先去掉-w让报错信息能直接显示在终端。--collect-all paddleocr和--collect-all paddle是打包成功的关键这两个参数会遍历对应包内的所有子模块、数据文件和动态库全部收集进 exe。缺了它们几乎必然出现ModuleNotFoundError。--add-data inference;inference把模型目录整体复制到 exe 内部。分号前是源路径分号后是解压后的相对路径。--hidden-import声明那些在代码里未显式导入但运行时需要的模块。imghdr是 PaddleOCR 内部用来判断图片格式的模块skimage在某些图片预处理分支会被动态导入。--exclude-module用于剔除不需要的库能明显减小体积。matplotlib是 PaddleOCR 官方可视化工具带的依赖对纯识别场景没有用。3.3 第一次运行 exe 的验证方式打包结束后在dist目录下会生成OCRTool.exe。第一次运行不要双击建议在命令行里执行OCRTool.exe sample.jpg并把输出重定向到文件。本例中ocr_app.py写成了从命令行参数读取图片路径、打印识别结果的模式这样便于自动化验证。如果运行失败终端会直接显示 Python traceback这是排查问题最直接的信息来源。常见的情况是启动阶段报Failed to load dynamic library或ModuleNotFoundError这两种问题基本都出在--collect-all覆盖不全或 hidden import 缺失上。补对应依赖后重新打包即可。从这一轮开始每轮打包都要记录当前命令和报错信息方便翻查是因为哪次改动引入了新问题。4. 避坑指南PaddleOCR 打包路上的五个典型翻车现场4.1 运行报ModuleNotFoundError: No module named paddle现象打包后的 exe 启动时立即报缺少paddle模块但开发环境里明明已经正确安装。原因PyInstaller 分析 import 关系时漏掉了 PaddlePaddle 的动态导入机制。Paddle 内部有大量基于pkgutil和importlib的懒加载逻辑静态分析无法识别完整依赖图。另一个原因是打包时--collect-all paddle参数写成了--collect-all paddlepaddlePyInstaller 无法识别这个包名导致整个框架未被收集。解决统一在 bat 脚本里使用--collect-all paddle不要自行改名。如果--collect-all之后仍报缺模块可以加--hidden-importpaddle.fluid和--hidden-importpaddle.nn等显式声明。但绝大多数情况下--collect-all加对名字就能解决。建议打包完成后检查 exe 体积如果小于 80MB 大概率少了框架依赖。4.2 模型文件路径被解析到临时目录导致识别失败现象exe 能启动但一跑识别就报[Errno 2] No such file or directory: models/det/inference.pdmodel。原因PyInstaller 的-F模式把所有资源解压到一个临时目录类似_MEIxxxx但代码里写的是相对路径models/det运行时当前工作目录根本不是 exe 解压目录自然找不到模型。这是打包工具类软件最经典的路径问题。解决不能用硬编码相对路径要在代码里获取 exe 解压后的真实资源根目录。常见做法是借助sys._MEIPASS属性import sys import os def resource_path(relative_path): if hasattr(sys, _MEIPASS): return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.abspath(.), relative_path) det_dir resource_path(inference/det) ocr PaddleOCR( det_model_dirdet_dir, rec_model_dirresource_path(inference/rec), cls_model_dirresource_path(inference/cls), use_angle_clsTrue )这段代码的逻辑是PyInstaller 打包的单文件 exe 启动时会设置sys._MEIPASS指向解压目录开发环境下没有这个属性就回退到当前目录。这样开发调试和打包后运行走的是同一套逻辑。注意--add-data inference;inference中的目标路径要和resource_path里拼的参数一致。4.3 识别结果正常但程序退出时报Process finished with exit code 1现象exe 识别完文字命令行业能打印结果但退出时提示错误码 1日志显示找不到ppocr_keys_v1.txt。原因这是中文字典文件问题。PaddleOCR 的识别模型需要加载字典文件来把预测向量映射为具体字符这个文件在包内没有被收集。--collect-all paddleocr通常能带进来但某些版本中字典文件被归类为 data 文件而非 package data需要单独指定。解决用--add-data显式把字典文件加进去。先在当前环境里定位字典所在路径python -c import paddleocr; print(paddleocr.__path__)然后找到ppocr/utils/ppocr_keys_v1.txt在打包命令中追加--add-data 你的python路径/site-packages/paddleocr/ppocr/utils/ppocr_keys_v1.txt;paddleocr/ppocr/utils注意分号前是绝对路径或相对路径分号后是 exe 解压目录中的相对位置。也可以用--collect-data paddleocr把整个包的数据文件全部收集更保险但体积会稍大一些。4.4 打包后的 exe 启动极慢且首次识别耗时成倍增加现象双击 exe 后要等 5-8 秒才出现界面首次识别一张图又额外等 3 秒左右而开发环境里总共不超过 2 秒。原因单文件模式每次启动都要把几百个 DLL 和模型文件从 exe 解压到临时目录这个 IO 操作在机械硬盘上非常慢。另外 CPU 版 Paddle 在启动时会初始化算子库也会占用一定时间。这个现象不是故障而是单文件打包的固有代价。解决如果启动速度是重点关注项可以放弃-F单文件模式改用-D目录模式打包生成的 exe 在文件夹内直接运行省去解压步骤。交付时把整个 dist 目录打成 zip 提供给使用方依然是一次性解压使用。对于内部工具场景目录模式明显更舒适对于需要给外部客户发单个文件的场景再牺牲速度换单文件。我一般会两个模式都生成先给客户目录模式客户坚持要单文件再给-F版。4.5 打包出的 exe 在另一台 Windows 机器上提示缺少VCRUNTIME140.dll现象exe 在自己机器上运行正常拷到其他机器后启动报找不到VCRUNTIME140.dll或者直接弹窗提示0xc000007b。原因PaddlePaddle 的编译产物依赖 Visual C 运行库本机因为装了各种开发工具链所以不缺但干净办公机器上不会有这些运行库。这不是 PyInstaller 打包遗漏而是目标机器的系统运行库缺失。解决在打包阶段把 VC 运行库一并收集进来。常见做法是在 PyInstaller 命令中添加--add-binary C:/Windows/System32/vcruntime140.dll;. --add-binary C:/Windows/System32/vcruntime140_1.dll;. --add-binary C:/Windows/System32/msvcp140.dll;.把这三个 DLL 塞进包内之后通常能覆盖大多数干净机器的运行需求。如果目标机器是精简版 Windows 系统更稳妥的方式是把这三份 DLL 与 exe 放在同一目录下一起交付。注意如果客户机器是 32 位系统则要调整 DLL 路径为SysWOW64目录下的对应文件。5. 进阶验证与交付优化让 exe 真正达到可用状态打包完成只是第一步exe 作为交付物还需要经过系统化的验证才能给最终用户。我习惯建立一组测试图片集包含标准印刷体截图、手机拍照的歪斜文档、带复杂背景的表格图片和空白的纯色图片分别覆盖识别正常、方向矫正、背景干扰、边缘输入四种场景。每次打包后都执行一遍完整测试集记录每张图的识别耗时和结果字符串与开发环境基线的输出做 diff。这个验证流程看起来笨但能清楚发现打包过程是否改变了预处理逻辑。调试阶段建议保留控制台输出正式交付前再切到-w模式。切换后所有的报错信息会被吞掉用户只能看到「程序无响应」等错误结果定位问题的成本会立刻变高。所以我在ocr_app.py里加了一个全局日志开关逻辑把识别结果、耗时和异常信息同时写入logs/ocr.log文件exe 运行时即使没有控制台也能留底。下面是精简后的日志思路import logging logging.basicConfig( filenamelogs/ocr.log, levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s ) try: result ocr.ocr(image_path, clsTrue) logging.info(fRecognized {image_path}: {result}) except Exception as e: logging.error(fFailed on {image_path}: {e})这样设计日志的意义在于交付后如果用户报告某个图片识别失败可以先拉日志看具体异常而不是靠猜测。日志目录不存在时需要用os.makedirs(logs, exist_okTrue)提前创建否则第一次写入会报错。体积方面的优化也可以再做一轮。--exclude-module参数剔除matplotlib后已减少约 30MB继续剔除scipy也能省一部分体积但这需要确认 PaddleOCR 的某个预处理分支不依赖scipy才安全。实际测试中剔除scipy后表格识别场景仍正常但如果你需要处理复杂版面分析功能建议保留。回到交付这件事本身我现在每次做完打包验证都会强制走一遍「干净机器测试」环节找一台没有 Python、没有编译工具、没有各种运行库的虚拟机把生成的 exe 放进去跑完整测试集。只有当 exe 在那种环境里所有测试图片都通过我才会把它发给使用者。这个习惯救过我很多次不少问题都是在「我以为没问题」的机器上才暴露出来的。整个 PaddleOCR 离线打包的完整流程希望帮到你。本文还有配套的精品资源点击获取