2026/10/21 3:36:43

本地离线OCR实施方案:易语言对接深度学习框架全流程

本地离线OCR实施方案:易语言对接深度学习框架全流程 做了这么多年桌面工具发现“识别图片里的字”这个需求十个客户里至少有八个会提。前阵子给某公司维护一套单据录入工具对方提了几个看着普通、实际上很折腾的要求必须离线识别Win7和Win10都得能跑输入来源要支持图片文件、屏幕截图、内存里的字节集程序本体得用易语言写。我把那套开源深度学习框架的OCR工具集跑通以后又用易语言封装了一层本地HTTP调用整条链路现在跑得还算稳。这篇就把选型、环境、对接、填坑的完整过程记下来给同样想搞本地离线OCR的开发者一点参考。1. 先看清需求本地OCR到底要解决什么1.1 项目需求拆解这个项目表面是“做个OCR”实际拆开以后有四条硬性需求完全离线识别过程不联网数据不出本机兼容Win7 SP1和Win10不能只能跑在最新系统上识别来源包括图片文件、屏幕截图、内存字节集最终要跟易语言写的客户端程序平滑集成这里最容易被忽略的是“字节集”。易语言读文件、抓屏幕、接收网络数据最后拿到的都是字节集。如果OCR引擎只认磁盘路径那截图还得先存成临时文件来回写硬盘又慢又容易留垃圾。所以我一开始就把“直接吃字节流”定成了设计目标后面所有方案都围绕这个来选。1.2 为什么不能靠云端OCR很多人第一反应是调云端识别接口注册个账号拿个Key就能用。但真正放到项目里云端方案有一堆问题网络依赖。客户现场经常是内网或者弱网断网的时候整个功能直接废掉数据安全。单据、合同、身份证这类图片往外传客户那头合规审查过不去成本不可控。批量识别时按次计费一个月几千张图费用积累起来很可观维护麻烦。账号到期、额度用完、接口升级、回调地址变动全是售后坑本地OCR把模型放在自己机器上识别过程完全不联网速度和稳定性都可以自己控制。一次性部署完后面再识别多少张都不要再花钱。对桌面工具开发者来说这是最省心的路线。1.3 选型为什么选深度学习框架而不是老牌OCR老牌的OCR引擎我也试过比如Tesseract它的确能用但中文识别效果不太稳定。单据、截图这类图片经常有背景干扰、低分辨率、文字倾斜的情况老牌引擎处理起来识别率会掉得比较厉害。最后选的是某开源深度学习框架的OCR工具集主要看中几点中文模型是预训练好的开箱即用效果比通用引擎好一个档次支持CPU推理不需要独立显卡普通办公电脑就能跑模型可以完全离线部署下载一次之后终身不用联网识别管线里带了文本检测和方向分类能处理倾斜和旋转的文字这里有一个重要提醒新版本对Win7兼容性收窄得很厉害实际部署需要锁定某个旧版本分支。别一味追新尤其是要给老电脑装环境的时候。2. OCR引擎的工作原理与部署关键2.1 三步走的识别流程这个OCR工具集的识别逻辑分三步理解它有助于后面排查问题。第一步是文本检测。它用分割类的检测网络找出图片里所有可能包含文字的区域输出文本框坐标。这一步解决“字在哪里”的问题。第二步是方向分类判断文本框里的文字是不是倒着或横着的必要时旋转摆正。第三步是文字识别用卷积加循环网络的组合提取特征通过CTC解码输出字符序列。这一步解决“字是什么”的问题。为什么单独说这个因为很多人识别结果差以为是模型不行其实是第一步检测漏了或者第二步方向判断错了。比如一张手机拍的合同照片文字有点歪斜方向分类器阈值设置不当出来的结果就会缺字乱序。明白了管线结构才知道该调哪里。2.2 模型和字典文件离线部署时OCR引擎需要三类模型文件检测模型负责定位文字区域识别模型负责把文字区域转成字符方向分类模型可选项处理旋转场景另外必须有一个字典文件里面是所有可识别字符的清单中文字符加常见标点基本都覆盖。识别时模型输出的是字符在字典里的编号最终映射回文字。模型文件需要在有网的机器上下载好然后拷贝到目标机器放在一个固定目录里。目标机器不能联网所以所有依赖、模型、运行库都得提前准备好。我的做法是建立一个干净的package目录按“依赖库、模型文件、启动脚本、易语言客户端”四层放好拷贝过去就能跑。2.3 CPU推理和版本坑OCR推理完全可以只靠CPU不需要GPU。普通双核CPU识别一张几百KB的截图大概几百毫秒到一两秒对桌面工具来说可以接受。加速方面要看CPU是否支持。要注意的是Win7系统上特定加速指令集支持不完整某些框架版本会直接崩。稳妥做法是选带通用加速库的CPU版本并且提前在Win7真机上做验证。版本坑还体现在Python运行环境上。Win7最高只能装Python 3.8而部分新版框架包要求Python 3.9以上。这一点直接卡死。装环境的时候还要补上VC运行库和Universal CRT更新否则运行时会报缺少DLL。3. 易语言对接架构设计3.1 三种对接方案对比OCR引擎本身是用Python写的易语言要调用它常见有三种思路方案实现方式优点缺点A 命令行调用易语言执行命令行调用Python脚本识别实现简单不需要额外接口每次识别都要重新加载模型慢结果拿取麻烦B 本地HTTP服务Python起一个HTTP服务常驻内存易语言POST图片字节流拿JSON结果模型只加载一次速度快进程隔离崩溃不影响易语言主程序需要处理端口和进程守护C 封装DLL把OCR封装成动态库易语言直接调用导出函数调用最直接性能最好工程量大要处理内存模型和跨语言结构体调试困难我最后选了方案B。原因很实际开发量小易语言只要负责HTTP请求和JSON解析调试方便浏览器就能直接测OCR接口引擎进程崩溃了易语言程序不会跟着崩重启服务就行最重要的是模型常驻内存不用每次识别都重新加载。3.2 服务端接口设计服务端接口设计得极简两个接口就够POST /ocr 接收JSON其中一个字段是图片的Base64内容返回识别文本GET /ping 健康检查易语言启动时先探测一下服务在不在JSON格式定好之后易语言和服务端就可以并行开发。服务端用Python标准库自带HTTP服务器就能实现不需要额外安装Web框架。端口我选了17860这种不常用的避免跟本机其他服务冲突。之所以用Base64文本传输而不是二进制流是因为易语言那边组JSON包最方便的就是文本拼接。Base64会让体积增加约三分之一但本地回环传输这个开销可以忽略。3.3 易语言客户端调用实现易语言这边核心就三件事图片转Base64、Post数据、解析返回的JSON。.版本 2 .支持库 spec .程序集 窗口程序集_启动窗口 .子程序 识别图片数据 .参数 图片字节集, 字节集 .局部变量 base64文本, 文本型 .局部变量 提交数据, 文本型 .局部变量 返回文本, 文本型 图片字节集转Base64模块函数可根据自己环境调整 base64文本 编码_BASE64编码 (图片字节集) 提交数据 “image_base64” base64文本 返回文本 网页_访问_对象 (“http://127.0.0.1:17860/ocr”, 1, 提交数据, , , , , , , , , , , , , , , ) 返回文本是JSON里面带了识别结果 编辑框_识别结果.内容 取文本中间 (返回文本, “text:” #引号, #引号)如果你用的模块里没有现成的JSON解析可以直接用取文本中间命令提取“text”字段的值。注意返回结果里中文是UTF-8编码取出来后如果显示乱码转成GBK再显示。3.4 进程守护与自启动本地服务必须常驻但客户机不可能每次开机都手动启动Python脚本。我写了一个简单的批处理守护脚本放在启动目录里echo off :loop tasklist /fi imagename eq pythonw.exe | find pythonw.exe nul if errorlevel 1 ( start OCRService /min pythonw C:\OCR\ocr_server.py ) timeout /t 10 nul goto loop这个脚本每10秒检查一次OCR服务进程发现没跑就自动拉起来。用pythonw而不是python是为了不弹黑色控制台窗口。易语言主程序启动时也可以先请求一下/ping接口如果连不上就执行一下启动命令双保险更稳。4. 从零搭一套完整离线OCR4.1 准备运行环境Win7机器上最折腾的就是运行环境。我整理了一个核对清单按顺序执行基本不会漏安装VC 2015-2019 x64运行库很多组件运行时都要它安装Universal CRT更新补丁Win7不装这个会报“api-ms-win-crt-runtime-l1-1-0.dll缺失”安装Python 3.8并勾选Add to PATH在有网机器上下载好Python依赖包离线拷贝到目标机器用pip安装特别说明一下离线安装依赖包。在有网机器上执行pip download把依赖whl文件全部拉下来连同安装命令一起放到U盘。到目标机器上执行pip install --no-index --find-links本地目录这样就不会去联网找包。Win10相对省事但也建议把VC运行库装上很多机器默认不带全。4.2 模型文件准备模型文件结构如下C:\OCR\ models\ det_model\ rec_model\ cls_model\ dict.txt ocr_server.py start.bat模型下载好之后要注意路径不能有中文和空格。我之前把模型放在“C:\Program Files\项目目录”下面结果某些依赖库路径解析出错服务起不来。改成纯英文路径后一切正常。模型文件是给OCR引擎用的引擎初始化时通过参数指定模型路径。这里用的路径是绝对路径避免服务工作目录变化后找不到模型。4.3 OCR服务端完整代码服务端代码用标准库实现逻辑很清晰# ocr_server.py # 本地OCR识别服务使用http.server实现无第三方Web框架依赖 import json import base64 from http.server import HTTPServer, BaseHTTPRequestHandler # 这里的ocr_engine替换成实际使用的OCR框架入口 from ocr_engine import OCREngine # 初始化引擎加载模型保持全局单例 engine OCREngine(langch, use_gpuFalse, use_angle_clsTrue) class OCRHandler(BaseHTTPRequestHandler): def do_GET(self): # 健康检查GET /ping 返回服务状态 if self.path /ping: resp json.dumps({code: 0, msg: ok}).encode(utf-8) self.send_response(200) self.send_header(Content-Type, application/json; charsetutf-8) self.end_headers() self.wfile.write(resp) else: self.send_response(404) self.end_headers() def do_POST(self): if self.path /ocr: length int(self.headers.get(Content-Length, 0)) body json.loads(self.rfile.read(length).decode(utf-8)) img_bytes base64.b64decode(body[image_base64]) result_text engine.recognize(img_bytes) resp json.dumps({code: 0, text: result_text}, ensure_asciiFalse).encode(utf-8) self.send_response(200) self.send_header(Content-Type, application/json; charsetutf-8) self.end_headers() self.wfile.write(resp) else: self.send_response(404) self.end_headers() def log_message(self, fmt, *args): pass if __name__ __main__: HTTPServer((127.0.0.1, 34567), OCRHandler).serve_forever()模型初始化放在脚本启动阶段服务启动后第一次请求就不用再等模型加载了。OCR引擎内部建议设置只使用CPU线程限制线程数防止CPU被打满。4.4 易语言三种输入方式集成易语言端我封装了一个统一的“识别图片数据”子程序参数就是字节集图片文件、截图、内存字节集都转成字节集后调用它。文件输入最简单识别图片数据 (读入文件 (编辑框_图片路径.内容))屏幕截图用易语言自带的快照命令抓取指定窗口或屏幕区域.子程序 截取屏幕区域并识别 .参数 左边, 整数型 .参数 顶边, 整数型 .参数 宽度, 整数型 .参数 高度, 整数型 .局部变量 位图数据, 字节集 位图数据 快照 (取窗口句柄 (), 宽度, 高度, 左边, 顶边) 识别图片数据 (位图数据)这样截图之后不需要存临时文件直接进OCR流程整个识别过程在内存里完成。内存字节集更简单比如从网络接口返回的图片数据或别人写好的DLL给你的图片数据直接传给识别图片数据子程序就行。要注意大图片的处理超过2000像素宽的图可以先用易语言的缩放命令压到合适尺寸再识别。OCR引擎对分辨率太高的图并不会更准反而会明显变慢还可能内存暴涨。4.5 兼容性核对表部署到客户机器前建议按这个表逐项检查检查项通过标准备注系统版本Win7 SP1 / Win10均能启动服务Win7必须打SP1运行库无DLL缺失弹窗VC2015-2019和UCRTPython版本python --version显示3.8.x不要装3.9以上模型路径纯英文且无空格C盘根目录最稳服务端口未被其他程序占用netstat查17860端口内存占用服务常驻不超过600MB太大说明图片没压缩防火墙本地回环不受阻部分安全软件需放行5. 实际使用中的问题排查5.1 启动慢、首张图卡住服务第一次启动要加载好几个模型文件慢是正常的但要注意首张图不应该再卡。如果发现第一次请求要等好几秒十有八九是模型没有在启动时完成初始化。解决办法是脚本里调用一次空识别或者直接初始化时预热模型。我踩过的坑是把OCR初始化放进了每次请求的处理函数里结果每识别一张图都要重新加载模型速度完全没法看。改成全局单例初始化后后续识别都是毫秒级响应。5.2 内存持续上涨模型常驻内存后基础占用大概300到500MB如果服务持续跑几天内存涨到1GB以上多半是真机大图片识别造成的。应对办法是服务端把输入图先压缩到合适宽度比如最长边不超过960像素。另外不要在OCR引擎里开太多推理线程线程数设为CPU核心数的一半左右比较合理。易语言客户端如果编译成32位自身内存上限大约2GBOCR服务独立进程后不存在这个问题。但如果把引擎封成DLL进程内调用32位程序就会很危险。这也是我当时坚持独立服务进程的另一个原因。5.3 识别不准怎么办识别不准先看是哪种不准文字位置找错说明检测模型出了问题常见场景是背景太花需要先做图像增强或转灰度文字方向错乱需要打开方向分类器文字识别错字多半是图片分辨率太低把图片放大两倍再识别表格里的字挤在一起可以先把图片二值化去掉表格线再传图像预处理尽量不要放在易语言端做Python端直接处理更方便。我就在服务端加了一个简单的预处理选项超过阈值时自动转灰度加二值化。5.4 换行、空格、标点丢失OCR输出默认是按检测框给文本块很多工具商会把检测到的一整块文字拼成一行换行信息就丢了。单据类需求经常要保留字段顺序我的做法是让服务端按检测框的坐标排序返回每条文本带上坐标信息。易语言端通过坐标判断哪段是标题、哪段是正文比单纯拼文本可靠得多。标点丢失多半是字典文件不全可以补一个包含常见中文标点符号的字典或者识别后做一次规则替换。5.5 问题速查表现象常见原因解决办法启动报缺少DLLVC运行库或UCRT未安装补装运行库服务起来但请求超时模型路径错误或模型未加载成功检查models目录和绝对路径识别结果乱码UTF-8编码没转GBK易语言端转码显示服务常驻内存过高大图没压缩服务端限制图片最长边Win7上跑不起来Python版本太新改用Python 3.8端口被占用其他服务使用同一端口更换端口并更新易语言调用地址服务进程消失引擎异常退出守护脚本自动拉起最后聊几句题外话。这个项目做完之后我最大的体会是本地OCR的难点根本不在算法而在环境兼容和工程对接。模型识别得准不准那是框架已经解决的事情真正磨人的是Win7老机器的各种运行库、模型路径带中文、服务进程没人看管。如果你也在用易语言做类似功能建议先把我这条链路完整跑通一遍再根据自己场景优化。输入源那头先把文件路径方式做出来截图和字节集都是同一套字节流传参自然就通了。整条链路跑顺之后客户那边再提什么离线识别需求你都能十分钟内给出方案。