尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

C#上位机如何集成PaddleOCRSharp实现工业数字识别

C#上位机如何集成PaddleOCRSharp实现工业数字识别 简介面向C#深度学习初学者的数字识别示例基于PaddleOCRSharp封装库实现帮助解决OCR技术入门时环境配置复杂、调用门槛偏高的问题。项目可直接运行并识别数字源码注释详细适合自学深度学习与文本识别也适合需要快速集成OCR能力的桌面应用开发者。PaddleOCRSharp本身是对PaddleOCR C代码的.NET封装支持文本识别、文本检测与表格识别调用仅需几行代码且提供不同.NET框架版本NuGet包即装即用可完全离线部署无需联网即可获得高精度识别结果。资源包约103MB共87个文件包括27个DLL动态库、9个C#源码文件、5个模型参数文件、可执行文件以及JSON、TXT、Config等配置说明既有可直接运行的程序也有完整工程源码目录结构清晰便于按需查阅已有3331人学习下载。通过该示例不仅能掌握PaddleOCRSharp的集成方式、模型加载流程与数字识别实现思路还能获得一套可在X64环境下离线运行的数字识别工具为后续扩展中英文OCR应用打下基础。 很多做C#上位机的朋友应该都有这种体会工业现场需要识别的内容越来越多包装上的生产日期、仪表盘上的读数、设备屏幕上显示的数字参数靠人工盯不仅效率低还容易出错。想引入深度学习来做识别但一想到那套Python环境、模型训练、CUDA配置就先打了退堂鼓总觉得那是专门做算法的人才碰的东西。我一开始也这么认为直到用PaddleOCRSharp做了个数字识别demo才发现C#做深度学习OCR识别其实没那么玄乎。这个库把百度开源的PaddleOCR模型封装成了C#可以直接调用的类库不需要自己去搭Python推理服务直接在WinForm或者WPF项目里引用就能完成文字检测和数字提取。我最近用它做了一个识别仪表读数的案例整体跑下来效果不错把从环境搭建、核心代码到参数调优、踩坑记录全部整理出来给有同样需求的人做个参考。1. 项目背景为什么在C#里做数字识别要选PaddleOCRSharp1.1 从实际场景出发理清识别需求先说说我遇到的场景。现场有一批老式的数显仪表分布在不同的工位上需要定时记录读数判断设备是否正常运行。人工抄录效率低是一个问题更大的问题是数据没法实时汇总出了问题也追溯不到具体时间点。于是就想做一个自动识别方案摄像头或者工业相机拍照程序里识别出画面中的数字直接写入数据库。这类需求在实际项目中非常典型。除了仪表读数还有产品批次号的识别、快递单号录入、医疗检测设备数据采集等本质上都是从图像里把数字提取出来。这种需求有几个共同特点第一数字区域相对固定背景可能比较复杂第二对识别速度有一定要求最好能实时或者准实时第三一般不需要训练自己的模型用通用的OCR能力就能覆盖大部分场景。我之前考虑过几条技术路线。第一种是用OpenCV做模板匹配或者轮廓检测只适合背景极干净、数字字体完全固定的情况现场光照一变就失效。第二种是用Halcon的OCR分类器识别工业字体确实不错但Halcon是商业软件授权费用不低而且对C#的集成方式比较老套。第三种就是现在采用的方案PaddleOCRSharp。1.2 PaddleOCRSharp能做什么、解决什么问题PaddleOCRSharp的底层是百度开源的PaddleOCR模型这个模型在中文、英文、数字混合场景下的识别能力是经过大量实际项目验证的。它支持文字检测和文字识别两个阶段检测阶段定位图像中的文本区域识别阶段把每个文本区域转换为字符串。选择PaddleOCRSharp做C#侧的集成方案有几个非常现实的优势部署简单。直接用NuGet包引入项目不需要自己搭Python环境也不需要部署独立的OCR服务。离线可用。模型文件放在本地推理在本地完成数据不需要上传到云端这对很多工业场景来说非常重要——产线上的数据往往涉及生产参数不允许出内网。支持多种图片输入方式。可以传图片路径、字节数组、Bitmap对象方便和工业相机SDK集成。模型可以替换。如果你想用更高精度的模型或者定制数字专用模型只需要替换模型文件夹下的文件即可。说实话在没有PaddleOCRSharp这类封装库之前C#开发者想在本地跑PaddleOCR通常的办法是用Python写一个HTTP服务C#通过Web API调用。这么做不是不行但多了一个服务进程部署时就得额外维护出问题时排查链路也长。PaddleOCRSharp把这一层抹平了直接在进程内调用对C#桌面应用开发者非常友好。2. 环境准备与工程初始化2.1 运行时要求与依赖项清单先看看你的环境能不能跑起来。PaddleOCRSharp目前对.NET的支持版本是.NET Framework 4.6.1和.NET Core 3.1我用的是.NET 6.0WinForm项目。官方推荐Windows 10/11 x64系统CPU推理时内存占用大概在500MB到1GB之间如果你的图片分辨率高、并发量大内存会更高一些。依赖项方面PaddleOCRSharp会通过NuGet自动引入一些原生DLL包括推理库、OpenCV库等。这些库在首次运行时会解压到本地目录如果杀毒软件拦截了解压过程运行时会报找不到DLL的错误。这个在后面排查部分会详细说。如果你的机器有NVIDIA独立显卡并且想用GPU加速需要额外安装CUDA和cuDNN。但这里有个务实的建议除非你的识别量非常大否则其实CPU推理已经够用。我用一个中等配置的工控机i5-850016GB内存做测试单张1920x1080的图片识别耗时大约在500毫秒左右车间巡检这种频率完全能承受。2.2 创建WinForm项目并引用PaddleOCRSharp在Visual Studio里新建一个WinForm项目选.NET 6.0。项目创建好之后右键项目名称选择管理NuGet程序包搜索PaddleOCRSharp直接把最新稳定版安装进去。安装完成后项目里会出现几个关键目录models/存放OCR模型文件包含det_infer文本检测模型、rec_infer文字识别模型、cls_infer文本方向分类模型和keys.txt识别字符集字典。inference/存放PaddleOCRSharp运行时的原生DLL包括paddle推理库和opencv相关库。这里有个很容易踩的坑这些目录里的文件必须保证完整而且路径不能被程序写死。如果你把项目复制到别的机器上要确保models文件夹和inference文件夹跟着一起走。建议在项目的bin输出目录里检查一下确认发布之后这些依赖会自动拷贝到程序运行目录。如果不放心可以手动把这两个文件夹复制到bin\Debug\net6.0-windows目录下跑一个空项目测试一下DLL是否加载正常。模型文件从哪来NuGet包默认会带一份通用模型覆盖中英文和数字识别对大多数场景够用。如果你需要更精准的模型可以去PaddleOCR的官方模型库下载PP-OCRv4系列模型把下载下来的文件替换到对应目录。2.3 界面布局与工程结构设计这个demo的界面我设计得很简单左侧一个PictureBox显示原始图片右侧一个DataGridView用来展示识别出来的文本、置信度和位置坐标。顶部放一个选择图片按钮和一个开始识别按钮底部是一个总的识别结果文本框。工程结构上我把OCR引擎初始化、图像预处理、结果解析分别封装成独立的类这样后续扩展成相机实时识别时不需要改动界面代码。推荐你也这样设计因为实际项目中几乎不可能只识别一张图片后面接相机、接数据库、接PLC都是大概率的事情。3. 数字识别demo完整实现3.1 核心代码初始化引擎与单张图片识别OCR引擎的初始化需要在正式识别之前完成。PaddleOCRSharp的设计思路是先用配置类创建引擎实例然后反复调用该实例的识别方法。引擎实例不要每次都重新创建初始化过程涉及模型加载耗时可能达到几秒甚至十几秒频繁重建会严重影响性能。using PaddleOCRSharp; // 配置识别参数 OCRParameter ocrParameter new OCRParameter(); ocrParameter.maxSideLen 960; // 长边缩放尺寸控制输入图像大小 ocrParameter.useGPU false; // 使用CPU推理 ocrParameter.cpuThreadNum 4; // CPU线程数 ocrParameter.detDbThresh 0.3f; // 检测阈值越低检测出的文本框越多 ocrParameter.detDbBoxThresh 0.6f; // 检测框过滤阈值 ocrParameter.detDbUnclipRatio 1.6f; // 文本框扩展比例 ocrParameter.recThresh 0.6f; // 识别置信度阈值 // 配置模型路径 OCRModelConfig ocrModelConfig new OCRModelConfig(); ocrModelConfig.det_infer models\det_infer; ocrModelConfig.cls_infer models\cls_infer; ocrModelConfig.rec_infer models\rec_infer; ocrModelConfig.keys models\keys.txt; // 创建引擎实例 PaddleOCREngine engine new PaddleOCREngine(ocrModelConfig, ocrParameter);初始化完成后调用识别接口// 方式一传入图片路径 OCRResult result engine.DetectText(imagePath); // 方式二传入Bitmap对象方便从PictureBox或相机SDK中直接获取图像 using (Bitmap bmp new Bitmap(imagePath)) { OCRResult result engine.DetectText(bmp); } // 解析识别结果 foreach (var block in result.TextBlockDetails) { Console.WriteLine($识别文本: {block.Text}); Console.WriteLine($置信度: {block.Score}); foreach (var point in block.BoxPoints) { Console.WriteLine($坐标: {point.X}, {point.Y}); } }这里补充说明一下OCRResult的结构。它里面有一个TextBlockDetails集合每个元素代表一个识别到的文本框包含文本内容、置信度和四个角的坐标点。对于数字识别场景我们通常只需要Text和Score但如果你要做位置判断比如只取某个区域内的数字坐标点就派上用场了。3.2 提高数字识别精度的参数配置识别精度是这类项目最关心的点。我在调参过程中发现PaddleOCRSharp对数字的识别能力其实很强但实际效果受图片质量和参数配置的影响很大。先说图片质量。PaddleOCR对输入图片有一个基本要求文字分辨率不能太低文字高度建议在30像素以上。如果仪表上的数字很小直接识别会失败这时候需要先对图像做放大处理。我写了一个简单的预处理方法识别前用OpenCvSharp把图片放大两倍再做灰度化和二值化using OpenCvSharp; public Bitmap PreprocessImage(Bitmap original) { Mat src OpenCvSharp.Extensions.BitmapConverter.ToMat(original); // 放大图像提升小字识别率 Mat resized new Mat(); Cv2.Resize(src, resized, new Size(src.Width * 2, src.Height * 2), 0, 0, InterpolationFlags.Linear); // 转灰度 Mat gray new Mat(); Cv2.CvtColor(resized, gray, ColorConversionCodes.BGR2GRAY); // 二值化增强数字与背景的对比度 Mat binary new Mat(); Cv2.Threshold(gray, binary, 0, 255, ThresholdTypes.Otsu); return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(binary); }这个预处理步骤看起来简单但对识别率的提升非常明显。特别是工业现场拍摄的图片经常存在光照不均、背景纹理干扰的情况二值化之后数字区域变得干净利落PaddleOCR的检测模块能更准确地框出文本区域。再说参数配置。maxSideLen这个参数决定输入图像的长边缩放尺寸默认值960。如果图片本身分辨率不高这个值可以适当调大我测试时改成1280对小数字的识别效果有改善。但要注意这个值越大推理耗时越久内存占用也越高。检测阈值参数也值得细调。detDbThresh默认0.3如果调低到0.2检测模块会更加敏感能把一些边缘模糊的文本框也找出来但副作用是可能会误检出一些无效区域增加识别阶段的计算量。detDbBoxThresh是过滤检测框的阈值如果发现识别出来的文本框碎裂、一个数字被拆成两个框可以适当调低这个值。这里有一个我实测下来的经验如果只针对仪表读数这类场景推荐设置recThresh为0.5而不是默认的0.6。理由很简单有些仪表数字本身存在磨损或者笔画残缺置信度达不到0.6但人工判断完全能认出来。调低阈值后这些数字也能正常显示只是你要在结果输出时标注置信度方便后续人工复核。3.3 批量识别与结果处理实际使用时不太可能只识别一张图。我扩展了一个批量识别功能读取文件夹下所有图片依次识别把结果汇总到Excel中。这里需要注意的是批量识别时引擎实例只创建一次在循环中反复调用DetectText方法。private void BatchRecognize(string folderPath) { var files Directory.GetFiles(folderPath, *.jpg); foreach (var file in files) { using (Bitmap bmp new Bitmap(file)) { OCRResult result engine.DetectText(bmp); // 只提取数字相关的识别结果 var numbers string.Join(, result.TextBlockDetails .Select(b b.Text) .Where(t t.All(char.IsDigit))); // 写入DataGridView dataGridView1.Rows.Add(Path.GetFileName(file), numbers, DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss)); } // 释放内存避免Bitmap对象堆积 GC.Collect(); } }批处理中还有一个容易被忽略的问题每次识别完成后Bitmap对象要及时释放。如果一次处理几百张图片不释放的话内存占用会飙升程序可能在处理到一半的时候内存溢出。我在代码里加了GC.Collect()这是比较粗暴的做法更好的方案是使用using语句确保对象在离开作用域时立即释放上面的代码就是这么处理的。识别结果的过滤也值得注意。通用OCR模型会把图片中的各种文字都识别出来包括单位符号、标签文字、Logo上的字等。对于数字识别需求我们通常只关心纯数字部分。我用了All(char.IsDigit)这个过滤条件只保留识别结果中全部由数字组成的文本块这样可以在很大程度上排除干扰信息。4. 常见问题与排查技巧实录4.1 模型加载失败、依赖DLL缺失怎么处理这类问题是最常见的尤其当你把程序复制到另一台电脑上运行时。典型报错有两种一是PaddleOCRSharp初始化时抛出DllNotFoundException二是OCRParameter构建时提示找不到paddle_inference.dll。这两种报错的根本原因通常是原生DLL没有被正确解压到程序运行目录。PaddleOCRSharp有个机制首次运行时会把inference文件夹中的DLL解压到当前目录如果程序放在有写入权限限制的路径下比如C:\Program Files解压就会失败。解决办法第一在正式部署前手动把inference文件夹复制到程序运行目录第二检查杀毒软件的隔离区看是否有DLL被误删。我在项目中就遇到过360把paddle_inference.dll当病毒隔离的情况添加信任后恢复正常。另外模型文件路径不要用相对路径加..这种写法建议在程序启动时动态拼接路径string baseDir AppDomain.CurrentDomain.BaseDirectory; ocrModelConfig.det_infer Path.Combine(baseDir, models, det_infer);4.2 识别精度不够、数字被漏识或误识如果发现数字识别结果不稳定先从图片预处理入手。不同光照条件下的图片二值化效果差异很大。Otsu自动阈值在光照均匀时效果很好但局部阴影严重时会有大片黑色区域覆盖数字这时候可以改用自适应阈值Cv2.AdaptiveThreshold(gray, binary, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 15, 5);如果识别的数字出现字符粘连、漏掉某个数字的情况可以在OCRParameter里把detDbUnclipRatio从1.6调大到2.0。这个参数控制文本框向外扩展的比例数值越大文本框对字符边界的截断越保守越不容易把数字截断。大量测试后发现还有一种情况也值得留意当图片中有多个相似的数字区域比如一个仪表上有多个参数显示识别结果会按文本框从上到下排列。如果你的业务逻辑只需要读取特定位置的数字建议用框坐标做区域筛选把关注区域外的识别结果过滤掉而不是简单地取所有结果。4.3 性能优化CPU推理如何跑得更快PaddleOCRSharp在CPU上的推理速度跟机器配置和图片大小直接相关。如果单张图片识别时间超过1秒可以按以下顺序优化降低输入图片分辨率。maxSideLen控制在960以内过大的图会显著增加检测耗时。裁剪感兴趣区域ROI。如果数字只在图片的某个固定区域直接裁剪出来识别耗时能下降一半以上。增加cpuThreadNum。这个值默认为-1自动可以手动设置为你机器的物理核心数。这里有个细节设置过高反而会因为线程切换导致性能下降一般4-8比较合适。避免在UI线程中调用识别方法。识别期间界面会卡住给用户的感觉就像程序崩溃了。用Task.Run把识别操作放到后台线程识别完成后通过Invoke更新UI控件。private async void btnRecognize_Click(object sender, EventArgs e) { btnRecognize.Enabled false; var result await Task.Run(() { using (Bitmap bmp new Bitmap(pictureBox1.Image)) { return engine.DetectText(bmp); } }); // 更新UI txtResult.Text string.Join(, result.TextBlockDetails.Select(b b.Text)); btnRecognize.Enabled true; }还有一个容易忽略的点PaddleOCRSharp在推理时会占用较高的CPU如果程序同时做相机取流、数据通信可能会出现整体卡顿。建议把OCR识别放到一个优先级较低的独立线程中执行并且控制识别频率比如每500毫秒识别一帧而不是每帧都做推理。对于工业现场来说稳定的识别节奏比极限速度更重要。最后再说两句PaddleOCRSharp这个项目让我最大的感触是C#开发者和深度学习之间的距离比想象中要近得多。以前觉得深度学习是Python的专利但现在成熟的模型和封装库越来越多C#工程师完全可以在不写一行Python代码的情况下把OCR、图像分类这些能力集成到自己的桌面应用里。回到数字识别这个demo其实它已经具备了扩展成完整工具的基础。你可以给它接上USB摄像头变成实时读数识别可以把识别结果写入数据库做成产线数据采集系统也可以加入定时轮询逻辑每隔一段时间自动拍照识别实现无人值守的巡检。只要把第一个demo跑通后续的扩展就是水到渠成的事。我在实际使用中还有一个体会这些OCR库虽然越来越智能但依然需要你自己把图片质量把控好。一个清晰的、对比度高的输入图能让识别率从90%直接提升到99%以上。所以在做这类项目时先把相机参数调好、把补光做好再考虑模型和算法往往是最快的优化路径。本文还有配套的精品资源点击获取
返回列表