C#与HALCON 24.11工业OCR实战:药品三期码与轮胎DOT码识别
2026/9/20 16:31:46 网站建设 项目流程

简介:一套以C#(VS2022+.NET Core)与HALCON 24.11为基础的工业级OCR字符识别实战指南,聚焦药品包装生产日期、轮胎DOT码环形识别两类典型场景,完整覆盖开发环境配置、图像预处理、文本定位、字符识别、结果验证与MQTT数据上报流程,适合熟悉C#与机器视觉的研发人员,以及工业自动化、智能制造领域工程师。资源包共1个PDF文件,大小599KB,内含HALCON核心算子解析、可运行代码示例、项目目录结构设计,以及多线程、GPU加速、ROI局部处理等性能优化策略,也涵盖错误处理与容错重试机制,并延伸讨论深度学习OCR集成、3D OCR应用、动态训练系统等前沿方向。目前已有282人学习下载,依据文中方案可实现识别准确率达99.2%以上的工业级OCR系统,对生产环境快速落地有直接参考价值。 做产线视觉这几年,被问得最多的需求就是“帮我在产品上读一串字”。药品包装要读三期码,轮胎厂要照DOT码,表面看着都是OCR,真做起来完全不是一回事。最近用C#配合HALCON 24.11把这两个项目完整跑了一遍,从相机选型、光源布局、C#上位机联动,到HALCON里的算子选型和结果后处理,踩了不少坑,也沉淀出了一套可以直接抄作业的流程。这篇文章就按项目实战的顺序,把关键设计思路、代码实现和排查经验整理出来,供做上位机开发和视觉调试的朋友参考。

1. 项目整体设计与技术选型

1.1 两个场景到底在“读什么”

药品包装上的字符通常是喷码机打上去的三期码,包括生产日期、有效期和批号。喷码属于点阵式打印,颜料附着力受包材材质影响很大,纸张、PVC泡罩、玻璃瓶、铝箔上的表现完全不同,经常出现字符断点、飞墨、深浅不一的问题。而且包装上还有其他印刷文字和图案,容易干扰识别,所以程序不能“见字就读”,必须通过定位把三期码区域从整张图里框出来。

轮胎DOT码是另一类典型需求。它刻在胎侧,通常以“DOT”开头,后面跟着工厂代码、规格代码,最后四位是生产周和年份,比如“DOT XXXXXXXXXX 2424”表示2024年第24周生产。胎侧是个圆弧面,橡胶字符是凸起的,光照稍有不均匀,字符和背景的对比度就会急剧下降,磨损胎上的DOT码更是时断时续。这两类项目放在一起做,恰好覆盖了工业OCR里最典型的两种难度:平面复杂背景字符和曲面低对比度字符。

1.2 为什么选C#加HALCON 24.11

很多朋友会问,开源方案那么多,PaddleOCR、Tesseract都在做通用文字识别,为什么还花成本用HALCON。我的看法是:工业OCR的难点从来不在“认字”本身,而在于把图像采集、区域定位、字符分割、识别、结果判定和PLC通信全部串成一套可靠流程。HALCON的价值在于它提供了完整的算子闭环——相机取流、标定、Blob分析、模板匹配、OCR、深度学习都在同一个环境里,版本兼容性有保障,不用来回拼接开源库。

C#这边主要负责上位机业务逻辑:WinForm/WPF界面、扫码枪串口事件、TCP通信、数据库和MES对接。HALCON 24.11在OCR引擎上做了不少升级,支持传统MLP分类器和深度学习文字检测共存,对于“药品三期码位置固定、轮胎DOT码位置变化大”这两种情况,可以用两条不同的技术路线去应对。另一个实际考虑是团队技术栈,C#工程师比C++视觉工程师好招,产线设备又基本都是Windows系统,这个组合的后期维护成本最低。

1.3 硬件架构与通信方案

这套系统的硬件链路是:工业相机加镜头加光源组成图像采集单元,扫码枪负责读产品条码触发拍照,PLC负责传感器联动和踢废信号,工控机跑C#上位机和HALCON。

相机上我们选的是海康威视的GigE千兆网相机,接入HALCON走标准GigE Vision协议。这里有一个容易被忽略的坑:海康相机SDK、相机固件和HALCON版本之间的兼容性必须测试,尤其是老固件配新SDK,或者HALCON升级后相机驱动没跟着更新,很容易出现取流失败或者画面撕裂。建议拿到设备后先做一次三端版本确认,再开始写代码。

触发链路我倾向于走扫码枪串口触发:扫码枪读到条码后通过串口发送字符串,C#的SerialPort.DataReceived事件触发视觉程序拍照检测。相比扫码枪键盘模拟模式,串口模式不依赖窗口焦点,程序在后台运行时不会丢字符。PLC信号则通过TCP或者Modbus接入,用于接收检测结果和执行踢废动作。

2. HALCON 24.11 OCR核心原理与算子选型

2.1 三条技术路线怎么选

HALCON里做OCR可以走三条路。第一条是经典路线:图像预处理、字符分割,然后用MLP或SVM分类器识别,核心算子是create_ocr_class_mlp、do_ocr_multi_class_mlp。这条路速度极快,单字符识别在毫秒级,适合字符区域固定、背景干净的场景,药品包装三期码如果打光稳定,完全可以用。

第二条是HALCON新版本主推的find_text系列。它基于集成式OCR引擎,底层是深度学习和传统方法的结合,核心算子包括create_text_model_reader、find_text、get_text_result。这条路线不需要自己分割字符,模型能直接输出文字行和置信度,适合字符排列不固定、背景有干扰的场景,轮胎DOT码这类位置变动的字符用起来很省心。

第三条是纯深度学习,用HALCON的深度OCR模型自己训练。这条路的效果上限最高,但需要采集大量样本、标注、训练、调参,项目周期明显拉长。实际项目中我的选择标准很简单:字符区域能通过模板匹配或Blob稳定定位的,就用传统MLP保证速度;定位困难的,用find_text;现场样本充足且允许两到四周调模型周期的,再考虑走深度训练路线。

2.2 预处理与ROI定位的底层逻辑

识别之前先要把“读哪里”定清楚。药品包装三期码的位置可能因为包装膜偏移产生小幅变动,轮胎DOT码在胎侧的位置更是每次不同,所以ROI定位是OCR流程的第一步。

ROI定位我常用两种方式。一是基于形状的模板匹配,在HALCON里create_shape_model加find_shape_model,定位后通过hom_mat2d做仿射变换把标准ROI映射到当前图像上。二是纯Blob分析,适合字符区域和背景灰度差异明显的场景,通过threshold提取候选区域,再用特征筛选过滤掉干扰。

预处理是决定识别率的关键环节。我先说一个原则:工业OCR的预处理不是“美化图像”,而是“增大字符和背景的可分性”。常用的手段包括中值滤波去除喷墨噪声、局部动态阈值解决光照不均、形态学闭运算把点阵字符断点连起来。HALCON的掩膜机制也很有用,可以手工框出一个排除区,把包装上的印刷图案、轮胎上的其他纹路直接从识别区域里去掉,用reduce_domain和change_domain配合绘制区域就能实现。

3. 从图像采集到结果输出的C#实现

3.1 环境准备与授权部署

HALCON 24.11是商业软件,安装包需要从MVTec官方渠道获取,开发阶段可以申请评估授权,产线部署必须购买正式授权,开发版和运行时版授权要区分清楚。这个问题一定不能含糊,没有合法的运行时授权,程序在客户现场启动时会直接报错。

安装完成后,在Visual Studio的C#工程里添加对halcondotnet.dll的引用。这里有两个注意事项。第一,HALCON 24.11的.NET程序集默认按.NET Framework和.NET Core分开,选对应版本引用,否则编译时会出现类型加载异常。第二,整个解决方案的平台目标保持X64,HALCON运行时是64位的,混用AnyCPU加X86会出现BadImageFormatException,排查起来很浪费时间。

3.2 相机采集与扫码枪触发联动

相机采集有两种方式。一种是用HALCON自己的GigE接口,代码里调用open_framegrabber加GrabImage,HALCON通过GigE Vision协议直接跟相机通信,不依赖厂商SDK。另一种是先用海康的SDK取流到内存,再转成HObject交给HALCON处理。前一种代码少、链路短,但不是所有厂家的扩展功能都支持,比如相机参数精细控制就不一定全;后一种更灵活,但要自己处理内存拷贝和格式转换,代码量多一截。

触发联动我选择串口扫码枪方案。扫码枪通过串口发送ASCII码,C#里SerialPort的DataReceived事件触发回调,回调中先更新界面显示条码,再调用相机采集和OCR识别。这里有一个经验:串口收到的一包数据可能是多个字符分批次到达的,处理时不要直接按一次事件处理一帧,而是累积到换行符再统一解析,否则条码经常被拆成两半。

3.3 OCR主流程的C#代码实现

下面这段代码代表我们项目里轮胎DOT码识别的主流程,使用HALCON 24.11的find_text引擎。先创建文本模型,再执行查找和识别,最后取出单词结果:

// 加载当前采集到的图像 HImage image = new HImage("D:/capture/dot_tire.png"); // 创建文本模型读取器,auto模式让HALCON自动选择合适模型 HTuple textModel = null; HOperatorSet.CreateTextModelReader("auto", new HTuple(), out textModel); // 执行文本查找与识别 HObject textRegion; HTuple textResultId, score; HOperatorSet.FindText(image, textModel, out textResultId, out score, out textRegion); // 获取识别的单词结果 HTuple wordList; HOperatorSet.GetTextResult(textResultId, "word", out wordList); // 输出结果和控制台检查 for (int i = 0; i < wordList.Length; i++) { Console.WriteLine($"识别结果: {wordList[i].S}, 置信度: {score[i].D}"); }

如果场景是字符位置固定的药品三期码,走传统MLP路线速度更有优势,核心代码是这样:

// 读取HALCON自带的工业字符模型,覆盖0-9和A-Z HTuple ocrHandle; HOperatorSet.ReadOcrClassMlp("Industrial_0-9A-Z_NoRej.omc", out ocrHandle); // 对分割好的单个字符区域执行识别 HTuple charClasses, charConfidences; HOperatorSet.DoOcrMultiClassMlp(characterRegions, image, ocrHandle, out charClasses, out charConfidences);

传统方案的关键是前面的字符分割必须准确,分割错一个字符,后面全部错位。HALCON里常用sort_region按行列排序字符区域,再用tuple_concat拼接结果,这些细节直接决定最终输出是否有序。

3.4 C#集成HALCON的三种方式

C#工程里集成HALCON,我试过三种做法。第一种是在HDevelop里把流程写出来,调试通过后导出成C#代码,编译进工程。这种方式调试最方便,HDevelop里能看到每一步的图像和变量内容,是项目初期最推荐的方式。缺点是导出代码偏底层,变量命名不够友好,需要二次封装。

第二种是用HDevEngine引擎在C#里动态加载hdev脚本。脚本和程序分离,现场调整算法逻辑时不用重新编译上位机程序,只要替换脚本文件即可。缺点是HDevEngine本身会有一定调用开销,极端高速场景可能不够快。

第三种是直接在C#里调用HALCON .NET算子,也就是上面代码展示的方式。这种方式最灵活,代码和业务逻辑完全融合,可读性好,但开发阶段需要不断回HDevelop验证算子参数。

我的建议是:项目从HDevelop脚本起步,跑通后导出C#代码,再封装成Dll或类库统一调用,兼顾调试效率和工程可维护性。

4. 两个落地场景的识别细节

4.1 药品包装三期码的难点与参数整定

药品包装的喷码是典型的点阵字符,每个字符由密密麻麻的小点组成,点与点之间还有断连。预处理时我习惯先做一次中值滤波去掉孤立噪点,然后用闭运算把相邻点连成完整笔画。闭运算的核大小要微调,核心太小断点连不上,核心太大字符会糊成一团甚至跟背景粘连。

ROI定位我们用模板匹配加掩膜组合。先训练一个三期码区域整体的形状模板,匹配成功后做仿射变换映射标准掩膜区域,掩膜里排除掉包装上的其他印刷文字和色块。识别后的后处理同样重要:三期码的日期部分可以用正则表达式校验格式,比如“20YY-MM-DD”或者“YYYY/MM/DD”,批号部分用长度和字符集过滤。这样即使某个字符识别置信度不高,只要整体格式不合法,程序就能判定为NG,避免把错码放过去。

4.2 轮胎DOT码的曲面展开与2.5D视觉思路

轮胎DOT码的难点在胎侧曲面。整车胎侧不是平面,字符区域会有一定弧度,直接用平面图像识别,边缘字符经常变形。HALCON提供了极坐标展开算子polar_trans_image_ext,可以把圆弧形的字符区域展开成矩形平面图,展开后再做识别,边缘字符的识别率会明显提升。操作步骤是先拟合字符区域所在圆弧的圆心和半径,再调用极坐标变换,展开参数的选择需要根据实际胎型微调。

光源是另一个决定因素。橡胶胎侧的DOT字符是凸起的,用普通的正向高角度光,凸起字符和背景的灰度差异不大。我们用低角度环形光,让光线从侧面掠过胎面,凸起字符的侧壁会产生阴影,字符轮廓一下子就清晰了。如果现场允许,还可以增加一个同轴光作对照,两路光配合拍多张图,选对比度最好的一帧参与识别。

再进阶一点就是2.5D视觉方案。用HALCON的photometric_stereo算子,在不同角度光源下拍多张图,重建出物体表面的法向或反射率图,凸起的DOT字符在这种高度信息里比普通灰度图更突出。实际产线如果经常换轮胎型号、字符磨损严重,我会优先建议客户上这个方案,整体可靠性比单纯调2D打光高很多。

4.3 结果判定与追溯逻辑

识别出字符串只是前半段,后半段是业务判定。药品三期码的输出需要拆成三行独立字段,分别做合规校验,和MES系统里的生产工单比对,日期对不上或者批号不存在,直接判NG并输出到PLC踢废。轮胎DOT码需要检查三点:是否以DOT开头、长度是否满足标准、末尾四位的周和年是否在合理范围内。

追溯方面,我们会在每次检测后把原图、ROI区域、识别结果、置信度和时间戳一起存档,文件名用条码加时间拼接。这样产线出现批量客诉时,可以直接按条码索引调出当时的图像和识别数据,快速定位是来料问题、喷码机问题还是视觉算法问题。这个设计看起来不起眼,真出了问题能省下大量扯皮时间。

5. 高频问题与避坑指南

5.1 误识别和漏识别的排查方向

遇到误识别,我一般按顺序排查:先看图像质量,ROI区域内字符是否清晰,曝光时间是不是太长导致反白,光源角度是不是让字符产生了反光;再看字符分割是否完整,点阵字符有没有被当成噪点滤掉;最后看分类模型,默认的工业模型覆盖字符集有限,如果现场有特殊符号,需要用实际样本重新训练分类器。

漏识别通常和预处理太激进的参数有关,最常见的是中值滤波过滤掉了细小的字符笔画。调参时不要一次性把参数调到看起来很“干净”,要反复对比识别输出,确保图像上的每个有效字符都保留下来。

5.2 触发不同步和丢帧问题

扫码枪键盘模拟模式是后台丢字符的重灾区,解决办法是切换成串口模式,用SerialPort事件接收数据,程序里按换行符拼接完整条码。GigE相机取流偶尔丢帧,排查时先看网卡是否是千兆并关闭节能模式,然后检查相机包大小和网卡巨型帧设置是否匹配,这两项不匹配会出现规律性花屏。

还有一个容易被忽视的点:C#和HALCON的线程模型。相机采图事件和OCR处理不能全挤在UI线程里,否则界面会卡死。我的做法是采集线程负责抓图,识别线程负责HALCON运算,UI线程只负责显示结果,三个线程之间用线程安全队列传递图像和结果数据。

5.3 部署现场的环境注意事项

程序部署到客户现场,最容易翻车的不是算法,而是运行环境。HALCON运行时授权没有安装,程序直接报License错误;VC++运行库缺失,Dll加载失败;halcondotnet.dll版本和主程序不一致,启动时抛异常。建议做部署清单时,把HALCON运行时安装包、Visual C++运行库、相机驱动、网卡驱动全部列进去,安装后先用官方例程验证一遍环境,再运行自己的程序。

5.4 学习资料和排查思路

HALCON自带的示例程序是很好的学习资料,在HDevelop里搜索OCR、find_text、DOT相关例子,基本能覆盖90%的常见场景。算子参数记不住就查HALCON官方文档,自带的算子参考支持中文检索,快速定位含义和调用方式,比在网上零散找代码效率高。遇到实在解决不了的问题,记录好HDevelop版本、HALCON版本、相机型号和复现步骤,带着这些信息去技术社区提问,得到的方案会精准很多。

最后再分享一点体会:工业OCR项目真正花时间的往往不是识别算法本身,而是把“识别不出来的时候怎么处理”想清楚。图像质量兜底、格式校验兜底、超时和重试机制兜底,这几层兜底做好了,识别率哪怕从99%提到99.9%,在客户现场的体验也是天壤之别。做视觉项目,先把下限抬起来,再去追上限。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询