
直接写别整开场白废话。易语言社区里关于OCR的帖子我翻来翻去基本都是同一个套路贴一个在线API的调用代码然后告诉你注册个key就行。真到了内网机器、Win7老电脑、完全没网的环境这招当场就废了。所以我把“易语言基于飞桨的本地OCR识别”这条路线完整趟了一遍从环境搭建、离线部署、易语言侧传图到踩坑记录都整理在下面照着抄就行。1. 先搞清楚需求边界本地离线OCR到底要满足什么条件1.1 一个标题拆出四个硬性指标“易语言基于飞浆的本地ocr文字识别 win7 win10 无网离线 都可用图片/截图/字节集…”这个标题信息量非常大可以拆成四个硬指标来理解。第一调用方是易语言程序这意味着最终交付物要能挂在易语言工程里或者以易语言能调用的方式暴露接口。不能用Python去写整个应用再打包成exe替代因为需求方明确要求易语言程序做业务逻辑。第二飞浆OCR框架即飞桨PaddleOCR需要本地推理。OCR引擎跑在这台机器上不经过任何云服务器这和“无网离线”是对应的。第三系统要求是Win7和Win10都要兼容。这个约束远比看起来严重——Win7不能安装较新版本的PythonPaddlePaddle新版对Win7的支持也早就停了。很多人在Win10上调试得好好的一到Win7机器就报错原因就在这里。第四输入来源包括图片文件、屏幕截图、字节集。图片文件好理解截图是指易语言里用“快照()”之类命令抓取窗口或屏幕得到的位图数据字节集则是程序运行中直接拿到的一段图像数据流。这三类输入最后必须在易语言侧统一成一个格式再送给OCR服务端。这四个指标叠加在一起决定了方案不能做任何妥协。我当时的需求里还加了一句“识别结果要能拿坐标”这就更不能靠在线API了因为数据根本不允许出机器。1.2 方案选型对比为什么最终选了HTTP本地服务把PaddleOCR接进易语言我踩过三条路对比很清楚。方案一易语言直接调用命令行运行Python脚本传图片路径脚本识别完落盘写结果易语言再读文件。优点是没有额外通信设施缺点是非常明显——每次识别都会重新启动Python解释器、重新加载模型一次消耗2到3秒纯属正常如果连续识别几十张图用户能等到怀疑人生。而且易语言的“运行()”在等待外部程序结束时如果Python进程有中文输出控制台窗口还会出现乱码或卡顿。方案二把OCR能力封装成DLL让易语言像调用普通子程序一样调用。理论最优实际却很难落地。要在C层把Python运行时、模型加载、Tensor推理全部包进一个DLL里对绝大多数易语言开发者来说工作量远超预期。更要命的是易语言的DLL调用对参数类型限制比较严格传递字节集、返回结构化数据、回调进度都容易出问题一遇到崩溃连排查都无从下手。方案三本地HTTP服务。Python侧用FastAPI或Flask起一个轻量服务监听127.0.0.1。易语言程序通过HTTP请求把图片POST过去服务端返回JSON识别结果。模型在服务启动时加载一次之后常驻内存。这个方案在工程上最合理易语言侧只需要处理HTTP请求和JSON解析Python侧代码逻辑也清晰服务挂了可以单独重启不影响主程序。本机回环地址的通信开销微乎其微一次请求的传输耗时以毫秒计完全感知不到。最终选型就是方案三。后面的所有内容都基于这个方案展开。1.3 为什么OCR引擎选飞桨PaddleOCR而不是Tesseract很多易语言兄弟的第一反应是Tesseract毕竟开源、免费、支持离线。我也试过但识别效果在中文场景下实在不理想。Tesseract对干净印刷体还行一旦遇到截图里的倾斜文字、表格线、混合字体或者带背景色的界面元素漏字错字就非常多。日常办公场景里的截图往往就是这种脏数据识别率一低整个工具的价值就没了。飞桨PaddleOCR的识别流程是“文本检测方向分类文字识别”三段式。检测模型先找到图片里哪些区域有文字方向分类器判断文字方向是否需要旋转识别模型再逐个区域解码出文本。相比直接整图识别的方案它对位置、角度、复杂背景的处理能力明显更强。而且飞桨有专门的中文模型日常截图、文档拍摄、票据识别都能稳定出结果。我还考虑过云端OCR服务识别率确实不错。但离线内网环境直接把这个选项否决了即使有授权也不愿意把内部图片传到外部服务器。把敏感数据发出去这在成熟场景里提都不用提。飞桨PaddleOCR可以在CPU上跑对硬件要求不高4GB内存的办公机器实测可用这就够用了。2. 服务端搭建离线和Win7是最难啃的两块骨头2.1 版本组合定死Python 3.8 PaddlePaddle 2.5 PaddleOCR 2.6这部分是全网最容易踩的坑先讲结论建议固定用Python 3.8 PaddlePaddle 2.5.x的CPU版 PaddleOCR 2.6.x。这个组合在Win7和Win10上都能跑离线环境也能正常工作。Win7对Python官方版本的支持到3.8就基本到头了。再新版的Python安装包在Win7上要么直接拒绝安装要么装完运行报错。PaddlePaddle这个库又跟着Python版本走新版Paddle也不支持Win7旧系统。所以必须用旧但稳定的组合。我第一次在Win10上随手装PaddleOCR最新版调试一切正常信心满满地拿到Win7测试机上部署结果Python解释器根本起不来。换成3.8后又遇到numpy版本太新导致“DLL load failed”的报错。最后把numpy控制在1.23.5或更早版本问题才消失。如果你用Win10做开发机建议也按这个老组合来配虚拟环境避免把Win10上能跑的新版本带到Win7上翻车。2.2 离线部署包的收集方法无网机器安装Python库不能用pip在线装得在联网机器上把依赖全部拉下来拷贝过去离线安装。命令不复杂关键是依赖清单别漏。pip download有一个好处它会连带把依赖一起下载到指定目录。核心命令就是pip download -r requirements.txt -d D:\offline_packages --platform win_amd64 --only-binary:all:requirements.txt内容大致如下paddlepaddle2.5.2 paddleocr2.6.1 numpy1.23.5 opencv-python4.5.4.58 Pillow shapely protobuf3.20.3 PyMuPDF这里有几个容易遗漏的地方。opencv-python的新版本依赖较新的系统运行库Win7缺dll的情况很多4.5.4.58这个版本实测最稳。shapely在Windows环境经常没有预编译的whl包离线安装时可能还得单独找对应的cp38版本文件。protobuf版本不能太新太新了会和PaddleOCR内部的部分逻辑冲突3.20.x是在这个组合里验证过的。把离线包拷到无网机器上以后用一条命令装完pip install --no-index --find-linksD:\offline_packages -r requirements.txt如果目标机器连Python还没有就把Python 3.8的安装包也一并拷过去。安装时注意选上“Add Python to PATH”以外的选项不让Python进入系统PATH也行后面用绝对路径调用更可控。2.3 服务端代码与服务启动方式服务端我建议用FastAPI代码短、自动支持异步、文档直观。核心就一个接口接收文件上传返回识别结果。import io import numpy as np from fastapi import FastAPI, UploadFile, File from PIL import Image from paddleocr import PaddleOCR app FastAPI() ocr PaddleOCR( use_angle_clsTrue, langch, show_logFalse, det_model_dirD:/OCRServer/models/det, rec_model_dirD:/OCRServer/models/rec, cls_model_dirD:/OCRServer/models/cls, ) app.post(/ocr) async def ocr_api(file: UploadFile File(...)): img Image.open(io.BytesIO(await file.read())).convert(RGB) result ocr.ocr(np.array(img), clsTrue) lines [] if result and result[0]: for box in result[0]: text, score box[1][0], box[1][1] lines.append({text: text, score: round(float(score), 4)}) return {code: 0, lines: lines}det_model_dir、rec_model_dir、cls_model_dir这三个参数非常关键。模型文件可能被PaddleOCR自动下载到当前用户的.paddleocr目录但离线机器不能联网首次启动去下载就会卡死或报错。直接把模型文件复制到项目目录然后在代码里指定绝对路径这是离线部署的必做动作。启动服务用uvicorn命令很简单D:\Python38\python.exe -m uvicorn main:app --host 127.0.0.1 --port 8000为了让服务常驻可以注册成Windows服务也可以用批处理轮询守护。批处理方案成本最低echo off :loop tasklist | find python.exe nul || start /b D:\Python38\python.exe -m uvicorn main:app --host 127.0.0.1 --port 8000 timeout /t 5 nul goto loop这个脚本每5秒检查一次python进程是否存在不存在就重新拉起。实测下来即使服务死掉最多5秒就能自愈对业务基本无感知。服务日志不要输出图片内容只记录请求时间和识别行数就够避免日志文件把磁盘塞满。2.4 端口与部署目录规划离线部署最忌讳乱七八糟的路径依赖。把整个OCR服务固定到一个纯英文目录下比如D:\OCRServer里面放三个子目录code存放服务端代码models存放检测、识别、方向分类三套模型offline_pkgs存放离线安装包。模型和代码分离的好处是升级代码时不动模型文件重新部署时也方便。端口方面本地服务固定用8000或自选高位端口。易语言侧连接地址必须写死127.0.0.1不用机器名或局域网IP避免主机名解析出问题。如果目标机器上8000端口已经被占用把uvicorn启动参数里的端口改掉同时易语言侧同步修改即可。3. 易语言侧实现图片、截图、字节集如何统一处理3.1 三种输入的本质都是字节集易语言里处理图像“字节集”这个类型是绕不开的。读文件得到字节集快照窗口得到字节集其他模块传过来的图像数据也是字节集。所以易语言侧的代码设计可以围绕一个核心原则来做把一切输入先统一成字节集再做HTTP上传。图片文件直接读入文件。窗口截图用“快照(窗口句柄)”取回来得到的就是带BMP文件头的位图字节集。其他来源的字节集只要内容本身是合法的图片编码数据也可以直接使用。个别场景下从易语言图像库得到的是经过解码的像素数组没有文件头这种情况就需要调用方额外提供宽度高度绕开常规方案。实际业务里90%以上不会走到这一步日常的读文件、截图、字节集拷贝都能直接用。3.2 Base64方案和Multipart方案怎么选易语言侧把字节集交给本地服务有两条技术路线。multipart/form-data是HTTP标准的文件上传格式很多易语言的网络模块已经封装成“表单上传”命令直接传入字节集和文件名即可。优点是贴近HTTP协议原始语义后端直接用UploadFile接收。缺点是如果引用的库封装不到位自己拼multipart边界符容易出错调试起来也麻烦。BBase64方案易语言侧先把字节集做Base64编码塞进JSON里POST到服务端服务端Base64解码后再交给PIL。优点是不用处理multipart边界JSON结构一目了然服务端解析代码也简单。缺点是Base64编码会让体积膨胀约三分之一对一张几百KB的截图而言完全可接受。我的实践结论是优先用Base64方案。易语言对Base64编码有现成命令服务端只接收一个JSON字段出错概率更低。易语言侧核心思路大致是这样.版本 2 .支持库 iext 子程序 识别图片字节集, 文本型 参数 图片字节集, 字节集 局部 请求地址, 文本型 局部 请求体, 字节集 局部 返回文本, 文本型 请求地址 http://127.0.0.1:8000/ocr_base64 请求体 编码_utf8到字节集 (“{” #引号 “image” #引号 “:” #引号 取字节集Base64编码 (图片字节集) #引号 “}”) 返回文本 网页_访问_对象 (请求地址, 1, 请求体, , , , , , , , , , , , , , , ) 返回 (返回文本)编码转换是易语言侧最容易翻车的一步。服务端返回的是UTF-8编码的JSON而易语言窗口组件默认按GBK显示。解决办法是在拿到返回字节后先转成文本时指定UTF-8解码或者拿到文本后调用编码_UTF8到GBK。很多人在这一步看到乱码就以为接口返回了错误数据其实是编码问题。识别出来的文字如果是中文这一步错不了。3.3 返回结果解析纯文本还是JSON服务端返回什么样子的数据决定了易语言侧写多少解析代码。如果只是想把识别文字展示出来让服务端直接返回“换行分隔的纯文本”最省事。易语言用“分割文本”命令按换行符切一下逐行加入列表框或编辑框即可一行解析代码都不用写。如果需要每个文本框的坐标比如根据识别结果去点击对应位置那就必须走JSON。易语言里用开源的JSON解析模块读取lines数组里每个元素的text和score字段。坐标信息可以在服务端结果里额外增加box字段把四个角的坐标一并返回。开始阶段不建议一步到位上JSON先把纯文本流程跑通再按需增加坐标支持排查问题的链路会短很多。3.4 截图识别还有两个细节用“快照()”抓窗口时如果目标窗口被遮挡或最小化快照拿到的是窗口最后渲染的画面或者空白区域。要识别屏幕上当前可见的内容用的是屏幕截图而不是窗口快照。易语言里可以取屏幕宽度高度创建画板后复制屏幕到画板再取出位图字节集。这个思路在易语言社区有很多现成模块可以直接参考。另一个细节是抓取区域的清晰度。截图的缩放比例如果不对文字会发虚。尤其是高分屏下Windows的DPI缩放如果大于100%抓下来的图像尺寸和实际显示尺寸可能不一致识别率会受点影响。必要时可以把图片放大两倍再送识别PaddleOCR对小字体的识别率会明显提升。4. 完整实操从零到一次识别成功4.1 联网机器上的准备步骤整个过程可以概括为开发机装环境、拉离线包、拷到目标机、启动服务、易语言联调。按顺序操作失败率很低。开发机安装Python 3.8创建虚拟环境用上面的requirements.txt安装依赖。运行一次PaddleOCR让模型自动下载到本地目录。联网机器首次启动会自动拉取det、rec、cls三套模型文件下载完成后从用户目录的.paddleocr文件夹里找。把模型文件复制到项目的models目录下按det/rec/cls子目录分好。用pip download收集离线安装包。把Python运行时、模型目录、服务端代码、离线包整体拷贝到目标机器。这个流程里最值得注意的点就是第2步。很多人不在开发机上先跑一次直接在项目目录里引用不存在的模型路径服务一启动就报错。先让模型完整下载一次后面挪到任何离线机器都不会再有模型缺失的问题。4.2 先验证服务端再写易语言代码我在实际项目里总结出一个联调顺序先不碰易语言在目标机器上启动服务端然后直接用命令行工具或浏览器构造一次POST请求确认接口能返回正确JSON再去写易语言代码。这样问题边界非常清晰服务端有问题查服务端易语言有问题查易语言。测试命令大致是curl.exe -X POST http://127.0.0.1:8000/ocr_base64 -H Content-Type: application/json -d {\image\:\这里填Base64字符串\}返回内容应该能看到text字段和score字段提示识别成功。这一步通过以后易语言侧的HTTP调用只要按同样的格式提交就一定能通。如果返回格式不对优先检查Base64编码有没有换行符、JSON有没有拼错这两个是最常见的低级错误。4.3 易语言侧完整调用测试易语言这边我会先用一个最简单的窗口测试放一个“选择图片”按钮一个“开始识别”按钮一个多行编辑框。点击按钮后读入图片文件调用识别子程序把返回结果显示出来。这套测试小样跑通之后再挪到正式工程里。正式工程里需要注意线程问题。如果易语言界面在主线程里直接发HTTP请求并等待返回识别耗时会阻塞界面窗口会变成“未响应”状态。解决办法是放到线程里执行识别完成后再通过标签反馈或投递消息方式更新界面。这一点在连续识别多张图片时尤其重要不然用户体验非常糟糕。4.4 关键参数调优记录PaddleOCR的几个参数值得单独说明用表格整理如下参数作用建议值use_angle_cls是否启用方向分类器True文本方向或竖排场景必备lang识别语言ch中文识别show_log是否输出推理日志False减少无意义输出det_db_thresh文本检测阈值默认0.3低对比度文档可下调rec_image_shape识别网络输入尺寸保持默认不轻易改动文本检测阈值这个参数我实际试过默认0.3在清晰截图下没问题但识别浅色文字或低对比度扫描件时可以调到0.1到0.2检测框会更容易框住模糊文字。反过来如果图片背景纹理杂乱阈值调高一点反而能抑制误检。这些参数不影响代码结构直接改PaddleOCR初始化时的参数即可。5. 常见问题与排查技巧实录5.1 高频问题对照表现象原因解决办法服务启动报缺少dll缺少VC运行库或OpenCV依赖安装VC_redist运行库降级OpenCV到4.5.4.58pip离线安装报shapely错误shapely没有匹配的whl文件单独下载shapely的cp38 win_amd64 whl再安装PaddleOCR首次启动卡住模型路径错误正在尝试联网下载指定det_model_dir、rec_model_dir、cls_model_dir到本地目录易语言返回乱码UTF-8和GBK编码未转换拿到文本后调用编码_UTF8到GBK服务端返回500错误上传的不是合法图片数据检查读取图片、截图字节集是否完整服务端测试PIL是否能打开Win7安装Python 3.8没问题但启动报错缺少VC 2015运行库安装对应的VC运行库32位程序装x86版识别速度慢每次请求重新加载模型确认PaddleOCR初始化在全局变量服务常驻不重启识别结果顺序紊乱版面复杂默认输出顺序不理想服务端按坐标框的Y轴和X轴排序后再返回5.2 部署路径的隐藏要求整个项目目录不能放在中文路径下这是PaddleOCR底层C库的一个已知短板。部分版本读取模型文件或者临时文件时如果路径包含中文会莫名报错或加载失败。我踩过一次之后统一把服务放在D:\OCRServer、C:\OCRServer这类纯英文目录问题再没出现过。目标机器如果D盘不存在就放C盘目录名保持英文即可。另外用户目录里的.paddleocr模型文件夹有中文用户名的时候也可能出问题比如“C:\Users\张三”。把模型主动复制到项目目录并指定绝对路径可以完全绕开这个坑因为最终读取的就是D:\OCRServer\models\det这类纯英文路径。5.3 性能优化的几条经验服务常驻加模型只加载一次是最根本的优化。每次请求都重新加载模型意味着多秒延迟常驻后单次识别耗时主要是推理时间。CPU模式下一张1920x1080的截图大约一到两秒如果图片尺寸较小几百毫秒就能完成。连续识别时易语言侧可以用线程池并发POST服务端也支持同时处理多个请求。实测同时提交五张图总耗时比逐张提交要短不少。要注意的是PaddleOCR在CPU上高并发时会有锁竞争并发数并不是越大越好控制在3到5个线程比较合适。合理的并发数值需要在目标机器上测试不要盲目的开几十个线程。内存管理也值得留意。服务长期运行后Python进程的内存占用会缓慢上涨部分场景下持续多日后可能涨到无法接受的程度。最省心的兜底方案是每天凌晨自动重启一次OCR服务或者设置内存占用超过阈值时自动重启。批处理脚本里加一个定时任务就能搞定。5.4 识别结果的后处理PaddleOCR识别出的文本里夹杂着空格或特殊符号很常见尤其是从界面截图来的文字可能会有多余空格、全角半角混用的情况。易语言侧做文本清洗时建议先把全角空格统一掉再用正则或分割命令剔除不必要的字符。如果识别对象是固定格式的票据或表格可以在易语言里写规则解析把关键字段提取出来存储到数据库效果比直接把整段文字给你要好得多。坐标匹配也是一个方向。服务端返回的box字段包含了每个文字区域的四角坐标易语言可以按这些坐标计算点击位置实现“识别到某段文字后自动点击对应位置”的自动化操作。这个能力在批量处理表格、自动录入系统时非常实用识别引擎只是第一步后续业务动作才是项目的核心价值。6. 给后来者的一点个人体会这套方案最让人头疼的不是OCR模型本身而是易语言、Python、离线环境、老系统之间的衔接。飞桨PaddleOCR的识别能力不需要怀疑真正的工程难点在于版本组合、模型路径、编码转换、服务保活这些细节。我最初也是被一堆报错绕得晕头转向等把版本固定下来、把部署目录固定下来之后整套流程就变得非常顺畅了。如果你之后要复用这套方案我强烈建议先把开发机和目标机的环境版本完全对齐不要一边用新一边用旧。离线包、运行库、模型文件这些打包一次之后留好存档下次部署直接复制即可。还有一个小技巧在服务端加一个“/ping”的接口返回“ok”易语言程序启动时先探测一下服务是否在线如果不在线就提示启动OCR服务而不是让用户面对“连接失败”这种冷冰冰的报错。这个小细节能让整套工具的可用性提升不少。