工控机还是 Win7?我把 PP-OCRv6 做成了 14MB 纯 CPU DLL,.NET 3.5 也能直接调用

📅 2026/7/12 2:19:50
工控机还是 Win7?我把 PP-OCRv6 做成了 14MB 纯 CPU DLL,.NET 3.5 也能直接调用
目录一个很典型的工业现场先看结论这次发布包含什么为什么没有直接选择其他方案一张图看懂调用链为了支持 Win7我们具体做了什么1. 从源码编译 OpenCV 52. 明确指定 Windows 7 系统版本3. 静态集成 OpenCV 和 C 运行时4. 使用兼容 .NET Framework 3.5 的 C# 调用层5. 避免 GPU 和新驱动依赖6. 做成可以整体复制的绿色工程绿色包里有什么发布版怎么用实际运行效果怎样正确测试速度为什么提供 C ABIcurl调用HTTP接口浏览器JavaScript调用C# 完整调用代码为中文路径和长期运行做的处理这次还做了哪些稳定性处理常见问题1. 为什么程序只能使用x642. cpu_threads0是什么意思3. 模型放在中文目录会不会失败4. 初始化失败先检查什么5. 8080端口被占用怎么办6. 能不能多线程调用7. 支持哪些图片8. 为什么钢印、反光文字效果不好适用边界上线前检查清单适合哪些项目三种使用入口下载很多人以为 Win7 已经离开主流就等于离开了生产现场。但在工控机、产线终端、检测设备和老旧业务系统里情况完全不同设备运行稳定、改造成本高、上位机软件仍停留在 .NET Framework 3.5既不能随意升级系统也不适合安装一长串运行环境。为了解决这类真实需求我做了lw.OpenCVDNN.PPOCR1.0.0.1基于 OpenCV 5 DNN 和 PP-OCRv6 的 x64 纯 CPU OCR 组件并把 OpenCV 与 C 运行时静态集成进 DLL。部署时不需要 CUDA、不需要 Python也不需要再携带一组 OpenCV 动态库。一个很典型的工业现场假设产线上有一台已经稳定运行多年的工控机Windows 7 SP1 x64、4核CPU、没有独立显卡上位机程序还是 .NET Framework 3.5。现在需要增加标签、包装或铭牌 OCR。如果直接引入 Python现场需要处理解释器、第三方包和环境变量如果选择 GPU 方案又会碰到显卡型号、驱动和 CUDA 版本如果升级操作系统和 .NET原有采集卡、PLC 通讯组件或专用驱动也可能受到影响。这类设备真正需要的不是最复杂的技术栈而是保留现有 Windows 和上位机程序。不安装 Python不配置 CUDA。解压即可运行离线也能部署。C# 3.5 可以直接调用其他语言也有接口。出错时能看到日志、模型路径和明确的初始化信息。lw.OpenCVDNN.PPOCR就是围绕这些约束做的一套工程化实现。先看结论核心引擎OpenCV 5 DNN OCR模型PP-OCRv5 / PP-OCRv6 tiny / PP-OCRv6 small 运行设备CPU 原生组件x64 C DLL约14MB C#版本.NET Framework 3.5 部署方式DLL直调 / HTTP服务 / Windows服务 模型路径支持中文路径 额外运行时无需独立OpenCV DLL无需VC运行库无需Python和CUDA这次发布包含什么lw.OpenCVDNN.PPOCR.dll约 14MB 的原生 OCR DLL提供稳定 C ABI。lw.OpenCVDNN.PPOCR.HttpServer.exe开箱即用的 HTTP OCR 服务。.NET Framework 3.5WinForms 测试程序可选择图片、初始化模型、查看耗时和文本框。PP-OCRv5、PP-OCRv6 tiny/small 模型与字典。浏览器测试页面上传图片后显示识别文字、坐标和 OCR 耗时并绘制检测框。C/C 头文件、导入库、C# P/Invoke 参考封装和 SDK 接入说明。整个推理链只考虑 CPU。少一层后端选择就少一批驱动、版本和部署问题对工控现场反而更实用。为什么没有直接选择其他方案不同方案都有自己的价值。这里的选择标准不是“谁在所有环境里最强”而是谁更适合老旧、离线、依赖受限的工控设备。方案典型部署依赖Win7与老.NET接入GPU要求本项目中的定位Python PaddleOCRPython、Paddle及第三方包通常需要进程或HTTP中转可选研发验证方便现场部署较重OpenCvSharp OpenCV托管组件与原生OpenCV库需要匹配可用版本不需要C#开发方便但组件数量更多ONNX RuntimeONNX Runtime动态库取决于运行库版本和封装可选通用性好需额外维护运行时TensorRTCUDA、cuDNN、TensorRT及驱动不适合无NVIDIA GPU设备必需NVIDIA设备上追求极致速度本项目DLL、模型和字典提供.NET 3.5封装及C ABI不需要面向纯CPU、绿色部署和旧系统这并不意味着 OpenCV DNN 在所有硬件上都最快。它的优势是依赖少、接口稳定、模型通用、部署路径清晰特别适合维护周期长的工业软件。一张图看懂调用链JPG/PNG/BMP图片 │ ├── C#、C等程序传入图片字节 └── HTTP客户端传入Base64 │ ▼ lw.OpenCVDNN.PPOCR.dll │ OpenCV 5 DNN CPU推理 │ 文本检测 → 透视裁剪 → 方向分类可选→ 文字识别 │ ▼ UTF-8 JSON文字、置信度、四点坐标、OCR耗时为了支持 Win7我们具体做了什么“支持 Win7”不是把工程里的目标系统名称改一下就结束了。OpenCV 5、C 运行库、系统 API 和 .NET 调用层只要有一处引入高版本依赖程序就可能在目标机器上直接报错或闪退。这次主要做了以下兼容处理。1. 从源码编译 OpenCV 5项目没有直接使用面向新系统发布的通用 OpenCV 二进制包而是保存了完整 OpenCV 5.0.0 源码、构建配置、头文件和编译后的静态库。OpenCV 使用 Visual Studio 2019 的 MSVC v142 工具集编译。v142 能满足 OpenCV 5 对 C17 的要求同时比 VS2022 的 v143 更适合作为 Win7 兼容目标。整个源码和静态库都放在项目目录中复制到另一台编译机后仍然可以复现。2. 明确指定 Windows 7 系统版本C DLL 和 HTTP Server 都设置了WINVER0x0601 _WIN32_WINNT0x0601 PE Subsystem Version6.016.01对应 Windows 7。这样可以在编译和链接阶段约束目标系统减少无意调用高版本 Windows API 的风险。3. 静态集成 OpenCV 和 C 运行时OpenCV 5 相关模块使用静态库链接MSVC 运行时使用/MT静态链接。最终lw.OpenCVDNN.PPOCR.dll不需要额外携带opencv_world500.dll也不要求客户机器预先安装 Visual C Redistributable。实际检查 DLL 导入表后系统依赖只剩KERNEL32.dll ole32.dll这两项都是 Windows 系统组件。对工业现场来说依赖越少安装失败和版本冲突的概率就越低。4. 使用兼容 .NET Framework 3.5 的 C# 调用层测试程序不是只面向新版 .NET而是建立了完整的 .NET Framework 3.5 WinForms x64 项目。C# 通过 P/Invoke 调用稳定的 C ABI不依赖 OpenCvSharp也不要求安装 NuGet 运行组件。模型路径和错误消息使用 UTF-16OCR 结果使用 UTF-8 JSON。这样既能处理中文安装目录也能让老版本 C# 正确接收中文识别结果。5. 避免 GPU 和新驱动依赖Win7 工控机的显卡型号、驱动版本和 CUDA 环境通常并不统一因此这一版只保留 OpenCV DNN CPU 推理。代码中已经移除 GPU 参数和相关分支不需要 CUDA、cuDNN、DirectML 或 OpenVINO 运行时。6. 做成可以整体复制的绿色工程源码、解决方案、OpenCV 静态库、第三方组件、模型、测试图片和发布脚本全部放在同一目录。程序根据 EXE 所在目录寻找inference和www不依赖启动时的当前工作目录放在中文路径下也能正常初始化。需要说明的是不同 Win7 工控机的补丁、CPU 指令集和系统组件可能不同。项目已经按 Win7 SP1 x64 目标构建并控制依赖正式部署前仍建议在客户的实际设备或同配置镜像中完成一次验收测试。绿色包里有什么lw.OpenCVDNN.PPOCR.Win7-x64/ lw.OpenCVDNN.PPOCR.dll OCR原生动态库 lw.OpenCVDNN.PPOCR.HttpServer.exe HTTP服务 lw.OpenCVDNN.PPOCR.Win7.Test.exe .NET 3.5测试程序 inference/ PP-OCR模型和字典 www/ 浏览器测试页面 deploy/ Windows服务安装/卸载脚本 sdk/ native/ DLL、LIB和ppocr_api.h csharp/ NativeOcr.cs参考封装 SDK_INTEGRATION.md 多语言接入说明 test-images/ 测试图片 start_console.bat 启动HTTP服务 start_winforms_test.bat 启动窗体测试程序普通用户只需要关心两个启动脚本C#开发者可以直接查看sdk/csharpC、C、Delphi、Python ctypes、Java JNA 等语言可以依据sdk/native/ppocr_api.h对接。发布版怎么用解压绿色包后目录中已经带有服务程序、DLL、模型、网页和测试图片。双击start_console.bat默认启动http://127.0.0.1:8080/浏览器打开这个地址选择图片并点击识别就能看到 OCR 结果和文本框。接口地址为POST /api/ocr Content-Type: application/json {imageBase64:图片的Base64内容}需要作为 Windows 服务运行时以管理员身份执行deploy/install_service.bat卸载执行deploy/uninstall_service.bat。需要测试原生 DLL 时双击start_winforms_test.bat。界面中的“CPU线程0”表示使用 OpenCV 默认线程策略在多实例或高并发机器上可以分别测试 1、2、4实际吞吐往往比直接填满逻辑核心更有参考价值。实际运行效果HTTP Server 启动后会在命令行中输出产品版本、作者信息、监听地址、模型路径和主要推理参数。DLL 初始化时也会固定输出版本与作者信息方便确认加载的是正式发布组件。使用发布包中的3.jpg和 PP-OCRv6 tiny 模型进行最终回归一次测试结果如下接口返回code0 OCR耗时165.328 ms 检测文本框16个 第一行识别结果纯臻营养护发素这里的耗时是开发机单次回归结果只用于证明完整发布链路可以正常运行。实际速度会受到 CPU 型号、图片尺寸、文本数量、模型版本、CPU 线程数和同时请求数量影响应以目标工控机连续多次测试后的平均值为准。浏览器页面可以直接上传 JPG、PNG 等图片识别完成后显示文字、置信度、坐标和 OCR 耗时并在原图上绘制检测框。.NET Framework 3.5 WinForms 测试程序支持 PP-OCRv5、PP-OCRv6 tiny/small 模型切换也可以调整检测阈值、识别批次、预测器数量和 CPU 线程数。初始化成功或失败、模型参数和多次识别耗时都会显示在界面中。怎样正确测试速度OCR性能不能只报一次“最快耗时”。首次推理通常包含缓存和内存准备图片尺寸、文本框数量、CPU线程数也会明显影响结果。建议按下面的方法记录固定CPU电源模式、模型、图片和检测参数。初始化模型后先预热5次不计入统计。连续识别100次记录平均值、P50、P95和最大值。同时记录初始化时间、图片尺寸、文本框数量和进程内存。分别测试cpu_threads0/1/2/4不要只看单次最低值。HTTP场景还要分别测试单请求延迟和多客户端总吞吐。本次最终发布链路回归数据可以作为一条基线环境模型图片文本框单次OCR耗时结果当前开发机PP-OCRv6 tiny3.jpg16165.328ms成功Win7目标工控机待你们填写发在评论区待你们填写发在评论区待你们填写发在评论区待你们填写发在评论区待你们填写发在评论区为什么提供 C ABIC 类接口容易受到编译器、运行库和 ABI 变化影响。发布版只暴露一组extern C __cdecl函数模型路径使用 UTF-16识别结果统一返回 UTF-8 JSON。因此除了 C#C、C、Python ctypes、Delphi、Java JNA 等语言也可以直接对接。SDK 位于发布包的sdk目录其中包含 DLL、LIB、ppocr_api.h、C# 封装和完整说明。接口返回格式保持简单{ code: 0, msg: ok, elapsed_ms: 165.328, results: [ { text: 纯臻营养护发素, score: 1.0, x1: 22, y1: 32, x2: 307, y2: 32, x3: 307, y3: 75, x4: 22, y4: 75 } ] }前端可以直接利用四点坐标绘制文本框业务程序也可以按照置信度过滤结果。curl调用HTTP接口curl -X POST http://127.0.0.1:8080/api/ocr \ -H Content-Type: application/json \ -d {\imageBase64\:\这里填写图片Base64\}浏览器JavaScript调用const response await fetch(http://127.0.0.1:8080/api/ocr, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ imageBase64 }) }); const result await response.json(); console.log(result.elapsed_ms, result.results);C# 完整调用代码下面是一份可直接加入 .NET Framework 3.5 x64 控制台项目的完整示例。将 DLL 放在 EXE 同目录并确认inference中存在对应模型。using System; using System.IO; using System.Runtime.InteropServices; using System.Text; classProgram { [StructLayout(LayoutKind.Sequential, CharSet CharSet.Unicode)] privatestruct PpocrConfig { publicint struct_size; [MarshalAs(UnmanagedType.LPWStr)] publicstring det_model_path; [MarshalAs(UnmanagedType.LPWStr)] publicstring rec_model_path; [MarshalAs(UnmanagedType.LPWStr)] publicstring rec_dict_path; [MarshalAs(UnmanagedType.LPWStr)] publicstring cls_model_path; publicint limit_side_len; publicdouble det_db_thresh; publicdouble det_db_box_thresh; publicdouble det_db_unclip_ratio; publicint use_dilation; publicint use_angle_cls; publicdouble cls_thresh; publicint cls_batch_num; publicint rec_batch_num; publicint rec_img_h; publicint rec_img_w; publicint rec_predictor_num; publicint cpu_threads; } privateconststring DllName lw.OpenCVDNN.PPOCR.dll; [DllImport(DllName, CallingConvention CallingConvention.Cdecl)] private static extern void ppocr_config_init(ref PpocrConfig config); [DllImport(DllName, CallingConvention CallingConvention.Cdecl, CharSet CharSet.Unicode)] private static extern int ppocr_create_ex_w(ref PpocrConfig config, out IntPtr engine, StringBuilder error, int errorCapacity); [DllImport(DllName, CallingConvention CallingConvention.Cdecl, CharSet CharSet.Unicode)] private static extern int ppocr_ocr_encoded(IntPtr engine, IntPtr imageBytes, int imageSize, out IntPtr utf8Json, out int jsonSize, StringBuilder error, int errorCapacity); [DllImport(DllName, CallingConvention CallingConvention.Cdecl)] private static extern void ppocr_free(IntPtr memory); [DllImport(DllName, CallingConvention CallingConvention.Cdecl)] private static extern void ppocr_destroy(IntPtr engine); static void Main(string[] args) { string baseDir AppDomain.CurrentDomain.BaseDirectory; string modelDir Path.Combine(baseDir, inference); string imagePath args.Length 0 ? Path.GetFullPath(args[0]) : Path.Combine(baseDir, test-images, 3.jpg); PpocrConfig config new PpocrConfig(); ppocr_config_init(ref config); config.det_model_path Path.Combine(modelDir, PP-OCRv6_tiny_det.onnx); config.rec_model_path Path.Combine(modelDir, PP-OCRv6_tiny_rec.onnx); config.rec_dict_path Path.Combine(modelDir, PP-OCRv6_tiny_rec_dict.txt); config.rec_predictor_num 1; config.cpu_threads 0; IntPtr engine IntPtr.Zero; StringBuilder error new StringBuilder(512); int code ppocr_create_ex_w(ref config, out engine, error, error.Capacity); if (code ! 0) thrownew InvalidOperationException(初始化失败( code ): error); try { byte[] image File.ReadAllBytes(imagePath); GCHandle pinned GCHandle.Alloc(image, GCHandleType.Pinned); IntPtr jsonPtr IntPtr.Zero; try { int jsonSize; error.Length 0; code ppocr_ocr_encoded(engine, pinned.AddrOfPinnedObject(), image.Length, out jsonPtr, out jsonSize, error, error.Capacity); if (code ! 0) thrownew InvalidOperationException(识别失败( code ): error); byte[] jsonBytes newbyte[jsonSize]; Marshal.Copy(jsonPtr, jsonBytes, 0, jsonSize); Console.WriteLine(Encoding.UTF8.GetString(jsonBytes)); } finally { if (jsonPtr ! IntPtr.Zero) ppocr_free(jsonPtr); pinned.Free(); } } finally { if (engine ! IntPtr.Zero) ppocr_destroy(engine); } } }这段代码中特别重要的是两点项目必须编译为 x64DLL 返回的 JSON 内存必须调用ppocr_free不能交给 C# 自己释放。为中文路径和长期运行做的处理工业现场的软件经常安装在中文目录。原生 API 使用 UTF-16 模型路径HTTP 服务也会基于 EXE 目录解析模型和网页不依赖当前工作目录。作者信息由 DLL 初始化函数固定输出。控制台直接使用 Unicode输出重定向时使用 UTF-8HTTP 服务启动时也会设置 UTF-8 控制台代码页解决中文作者信息乱码。在资源管理方面模型、预测器、Windows 文件句柄和 OCR 返回内存都使用明确的 RAII 或成对释放策略。预测器即使在 OpenCV 推理抛异常时也会自动归还池中避免长期服务出现请求越跑越少、最后全部等待的问题。这次还做了哪些稳定性处理这次不是只把模型“跑起来”还对长期服务中容易出问题的资源路径做了专项整理OCR主对象改为智能指针模型加载中途抛异常也能自动释放已经创建的对象。Windows模型文件句柄使用RAII封装读取或内存分配失败时不会遗留句柄。识别预测器使用自动租约OpenCV推理异常时仍会归还预测器池。修复多路径轮廓展开可能使用错误长度而越界的问题。过滤无效文字裁剪避免空图进入识别网络。DLL返回的UTF-8 JSON统一通过ppocr_free释放。C#封装将识别与销毁串行化避免原生句柄正在使用时被释放。HTTP日志按天和大小滚动单文件最大1MB最多保留7天。更准确的表述是项目已经对主要资源所有权和异常路径进行了专项审计。任何需要长期无人值守运行的工业项目仍建议在目标设备上执行数小时或数天的循环压力测试并观察句柄数、线程数和内存是否稳定。常见问题1. 为什么程序只能使用x64当前OpenCV静态库、原生DLL、HTTP服务和WinForms测试程序统一按x64编译。调用方也必须选择x64不能使用x86老项目使用Any CPU时建议取消“首选32位”并明确改为x64。2.cpu_threads0是什么意思0表示保留OpenCV默认线程策略正整数会调用cv::setNumThreads。线程越多不一定越快多请求并发时还可能产生CPU过度竞争应在目标机器实测1、2、4和0。3. 模型放在中文目录会不会失败模型路径通过UTF-16传入DLL程序也以EXE目录解析相对路径已经验证中文路径场景。HTTP控制台使用UTF-8DLL作者信息使用Unicode输出。4. 初始化失败先检查什么先确认进程为x64再检查DLL是否与EXE同目录、模型和字典文件是否存在、识别模型与字典是否配套然后查看界面初始化信息或logs目录。5. 8080端口被占用怎么办可以指定其他端口lw.OpenCVDNN.PPOCR.HttpServer.exe --port 180806. 能不能多线程调用同一个OCR句柄的调用会在DLL中串行保护。需要并行吞吐时可以由上层建立多个独立实例但每个实例都会加载模型并占用内存必须根据CPU核心数和内存容量测试。7. 支持哪些图片编码图片接口由OpenCV解码常见的JPG、PNG、BMP可以直接传入。HTTP接口使用Base64上传默认限制请求大小超大图片建议先缩放或裁剪ROI。8. 为什么钢印、反光文字效果不好OCR模型更擅长对比度清晰的印刷文字。钢印主要依赖微弱明暗和边缘变化建议从侧向光源、固定相机、ROI裁剪、CLAHE、高通和Scharr梯度增强入手必要时采集现场数据微调模型。适用边界这套方案适合依赖受限的CPU OCR但并不打算覆盖所有场景。适合固定工位的标签、包装、铭牌、票据和印刷文字。Windows x64、离线部署和老.NET上位机。中低并发、本地调用或局域网HTTP服务。需要额外评估高反光、低对比钢印、严重透视、弯曲文字和手写体。每秒大量并发请求或超高分辨率整图。强制要求x86进程的历史软件。没有完成Win7 SP1补丁和硬件指令集核验的特殊设备。主动说明边界不是削弱产品反而能帮助项目在正确的环境中稳定落地。上线前检查清单[ ] 目标系统为Windows 7 SP1 x64或更高版本。[ ] 调用程序明确编译为x64。[ ] DLL、模型和字典已经复制完整。[ ] 识别模型与对应字典配套。[ ] 使用客户真实图片完成准确率验收。[ ] 对初始化、首次推理和预热后速度分别计时。[ ] 连续循环至少1000次观察内存、句柄和线程数。[ ] HTTP部署已确认端口、防火墙和局域网访问范围。[ ] Windows服务使用有权限读取模型和写入日志的账户。[ ] 已保存可回退的旧版本程序和配置。适合哪些项目Win7/Win10/Win11 工控机上的标签、铭牌、包装和单据识别。仍使用 .NET Framework 3.5/4.x 的 C# 上位机软件。不方便安装 Python、CUDA 或复杂运行环境的离线设备。希望通过 HTTP 快速接入 OCR又需要保留原生 DLL 二次集成能力的项目。三种使用入口如果只想快速体验解压绿色包双击start_console.bat浏览器打开http://127.0.0.1:8080/。如果是C#开发者把sdk/csharp/NativeOcr.cs加入.NET Framework 3.5/4.x的x64项目并参考本文完整代码。如果使用其他语言从sdk/native/ppocr_api.h开始按照C ABI加载DLL并严格遵守句柄和返回内存的释放规则。OCR 真正有价值的地方不是演示时能读出一张图而是能在受限环境中稳定部署、持续运行并让旧系统也有清晰的升级路径。lw.OpenCVDNN.PPOCR做的正是这件事。下载通过网盘分享的文件lw.OpenCVDNN.PPOCR.Win7-x64 链接: https://pan.baidu.com/s/1fyhPDD4eI7y-2fwlAhioqA 提取码: qzfh