资讯动态

C#与Halcon车牌识别系统源码解析:从图像处理到字符识别

发布时间:2026/9/8 8:48:41 来源:尧图企业网站定制
简介这是一份基于C#与Halcon机器视觉工具库实现的车牌识别系统完整工程面向计算机、自动化、人工智能等专业的学生与开发者尤其适合用于毕业设计、课程设计或期末大作业。项目通过手动滑动阈值与面积定位完成车牌区域提取识别成功率可达90%整体思路清晰便于二次开发和训练调整。压缩包共29个文件约1.35MB包含C#窗体源码.cs、Halcon运行库.dll与.xml、可直接运行的.exe程序、项目说明文档.md、示例车牌图片.png及Visual Studio工程配置文件等结构完整打开即可运行。目前已有292人学习浏览代码经过本地调试测试适合入门者模仿学习也可在此基础上扩展中文车牌识别或优化识别算法具有较高的参考价值。 做机器视觉这些年车牌识别算是被问得最多的实战项目之一。这次要分享的是一套用C#开发的、基于Halcon算法库的车牌识别系统完整源码实测综合识别率能到90%左右还带项目说明文档整个链路从图像采集、车牌定位、字符分割到识别输出都是能跑的。搞这套系统的场景主要有两类一类是毕业设计或课程项目需要一套能演示、能讲原理的完整工程另一类是公司里做车辆进出的原型验证想先确认Halcon这条路能不能满足需求。无论是哪一类这套源码的定位都是“先跑通再优化”。有些人可能会问Halcon不是已经有很多现成的车牌识别示例了吗确实有但官方示例更偏向算法验证和一套完整的C#上位机程序之间还隔着不少工程问题。Halcon负责图像处理C#负责窗体、相机、数据保存和业务逻辑这种组合是当前机器视觉项目里最常见的架构。下面我会按方案设计、环境搭建、核心算法、C#实现、实测调参和问题排查几个维度把这套系统的完整思路拆开讲清楚。1. 整体方案与设计思路1.1 为什么用“Halcon算法C#上位机”这个组合机器视觉项目选型时最忌讳一上来就纠结“哪个技术栈最牛”。实际做项目看的是“谁能用最短时间把功能稳定跑起来”。Halcon在车牌识别这类传统视觉任务上有大量成熟算子比如颜色提取、形态学处理、连通域分析、OCR分类器开发效率比OpenCV手写流程高不少。C#则胜在界面开发快和相机SDK、数据库、串口、网络通信的集成资料非常多做上位机几乎是不二选择。这个组合里Halcon不负责界面C#也不碰像素级操作两者通过HalconDotNet这个封装库通信。Halcon里的HObject图像对象可以被C#直接持有算法过程可以在HDevelop里调试好后导出也可以封装成dll供C#调用。调试时看效果用HDevelop落地时用Visual Studio两边互不干扰。1.2 先搞清楚90%识别率是在什么条件下测出来的任何识别率都必须绑定测试条件否则就是空话。这套项目里定义的90%指的是“整牌正确率”也就是一张测试图片里的牌照字符全部识别正确哪怕一个字符错了也算整体失败。测试集主要来自园区出入口的固定相机图片包含白天、傍晚、夜间补光、轻微倾斜和部分遮挡样本单张图片里的车牌宽度基本在120像素以上相机正对车头或车尾角度偏差在15度以内。在这个前提下整牌正确率做了90%。如果是现实中那种随意角度、强逆光、车速很快、车牌污损严重的场景90%是达不到的。记住这个边界条件后面调参和评估才有意义。1.3 从图像到结果标准车牌识别流程整套系统的算法链路可以拆成六个环节图像采集、预处理、车牌定位、倾斜校正、字符分割、字符识别。每个环节都有明确输入输出也都有独立调试空间。图像采集负责拿到Bitmap或HObject类型的原始图预处理做灰度化、增强、去噪目的是降低光照干扰车牌定位在整幅图里找到车牌区域这是最容易出问题的一步倾斜校正通过计算偏转角度把车牌拉正避免字符分割出错字符分割把连续的车牌区域切成一个个独立字符字符识别则对每个字符做分类最终拼成车牌字符串。项目里这六步分别封装在不同方法里任何一步效果不好都能单独替换。2. 环境准备与工程源码结构2.1 需要准备哪些开发环境建议的配置是Windows 10或Windows 11 64位系统Visual Studio 2019或2022Halcon 17.12以上版本运行时选择.NET Framework 4.7.2或.NET 6都可以。这里特别提醒Halcon版本和HalconDotNet引用dll必须对应比如装了Halcon 20.11就直接在安装目录下的bin文件夹里引用halcondotnet.dll不要从别的项目里拷贝版本不一致经常导致“找不到命名空间HalconDotNet”。如果只是单独跑算法不接相机在HDevelop里写脚本就够。但要做C#上位机还需要把Halcon安装目录里bin\dotnet35或dotnet4.0下的dll引入工程同时在系统环境变量里配置HALCONROOT。官方试用License能覆盖全部算子但试用期有限等正式部署时再考虑商业授权项目代码本身不受影响。2.2 源码工程目录怎么看拿到源码后不要急着双击运行先看目录结构。一个比较规范的工程会分成MainForm、CameraService、HalconHelper、Algorithm、Model、Logger等区域。MainForm放窗体和交互逻辑CameraService封装相机SDK把回调图像转成HObjectHalconHelper封装图像转换、保存、显示等通用操作Algorithm放车牌定位、分割、识别的主体算法Model定义车牌结果类Logger负责日志记录。我当时自己整理这个工程时踩过最大的坑是“UI逻辑和算法逻辑揉在一起”结果界面一卡就不知道是相机问题还是算法问题。后来强制规定窗体里不能直接调HDevelop脚本所有算法入口都走Algorithm层界面只负责显示结果。这个习惯在后续维护时帮了大忙。2.3 Halcon算法如何集成进C#集成方式一般有两种。第一种是在HDevelop里写完整脚本调试通过后导出成C#代码再把代码段复制到C#工程里第二种是用HDevEngine动态加载.hdev或.hdvp脚本好处是换算法不用重新编译C#坏处是运行环境要额外部署脚本文件。我最终用的是“导出C#代码封装成独立类”的方式运行效率更高也方便做单元测试。下面是一段简化的C#调用示意真正项目里的处理链比这长using HalconDotNet; public class PlateRecognizer { public static PlateResult Recognize(HObject image) { PlateResult result new PlateResult(); HObject gray, region; HOperatorSet.GenEmptyObj(out gray); HOperatorSet.GenEmptyObj(out region); // 彩色图转灰度 HOperatorSet.Rgb1ToGray(image, out gray); // 这里省略车牌定位、分割、OCR等完整流程 // 正常流程应参照HDevelop中已调通的PlateProcess.hdev result.PlateText 苏A12345; result.Confidence 0.9; return result; } }从这段可以看出C#层和算法层是通过HObject对象传递图像数据的算法细节被封装在方法内部外部只关心输入图像和输出结果。3. 核心算法模块实现拆解3.1 车牌定位颜色特征加几何筛选车牌定位是整个系统里最重要的环节定位一旦偏了后面所有步骤全是错的。我采用的不是单一路径而是把颜色特征和几何特征结合起来。首先把图像从RGB转到HSV或HSL空间普通蓝牌在H通道上有明显的蓝色区间黄牌和新能源绿牌也能通过对应色相区间提取出来。接着做开运算和闭运算目的是去掉细小噪声、把断裂的车牌区域连成完整块。这一步很多人调不明白核心经验是开运算核的尺寸不要大于字符笔画宽度否则字符区域会被吃掉闭运算核的尺寸又要比车牌的字符间隙大一些否则字符之间连不起来。提取到候选区域后用select_shape算子按宽高比过滤。标准车牌宽高比接近3:1到4:1面积和宽度也要设下限太小的区域基本可以排除。如果图像里有其他蓝色物体比如蓝色招牌、车身贴纸这一步能过滤掉大多数干扰。光照不均匀时固定阈值效果很差要换成局部自适应阈值。3.2 倾斜校正与字符分割定位到车牌区域后不能直接切字符因为相机安装角度不可能绝对正车牌在图像里经常带一点旋转。校正的方法是用最小外接矩形或者Radon变换算出偏转角度再把图像旋转回去。HDevelop里可以用orientation_region和vector_angle_to_rigid实现核心逻辑是先通过边缘或角点找到特征方向。角度不大时一般不追求完全水平允许一点余量因为后续字符分割对小幅倾斜是有容忍度的。字符分割我用的是垂直投影法。把校正后的车牌区域做二值化然后统计每一列上的前景像素数量字符之间会出现明显的“低谷”这些低谷就是分割边界。实际操作中有两个常见问题一是字符和边框粘连二是汉字内部笔画断裂或结构复杂导致投影低谷不明显。解决办法是先做一次形态学处理去掉上下左右边框再对投影曲线做平滑避免因为个别噪点切出错误边界。3.3 字符识别OCR模型怎么选车牌字符集由省份汉字、英文字母和数字组成规模不大但汉字和数字、字母之间的形似问题不少像“0和O”、“1和I”、“8和B”都比较容易混。Halcon自带的OCR分类器可以用但通用中文字符集不一定覆盖所有省份简称想提高准确率建议用自己采集的车牌字符样本重新训练MLP分类器。训练流程是先把车牌字符归一化到统一尺寸比如20x40提取灰度或梯度特征凑成足够多样的样本集用create_ocr_class_mlp创建分类器再用trainf_ocr_class_mlp训练最后用write_ocr_class_mlp保存。样本数越多越好尤其是“鲁、苏、浙、粤”这些笔画密集的汉字多角度多光照都要覆盖。识别时一句关键代码类似do_ocr_multi_class_mlp (CharRegions, Image, OCRHandle, ClassOut, Confidence)这个算子的输出是每个字符的识别结果和置信度C#侧拿到后可以直接拼接成完整车牌号并判断置信度阈值是否值得采纳。4. C#上位机功能实现4.1 支持本地图片、文件夹批量与相机实时采集一个车牌识别系统不能只处理单张图片。源码里做了三种输入打开单张图片、批量选择文件夹、相机实时采集。批量模式特别适合测试识别率把大量图片放一个文件夹里程序自动识别并把结果汇总成表格和人工标注对比后就能统计整牌正确率。相机实时采集这块不同相机SDK的接口不同但共同点是回调函数不能直接做耗时识别否则相机会丢帧界面也会卡死。我的做法是在相机回调里只拿Bitmap并标记“新帧到达”然后由后台线程每隔一段固定时间取最新的那帧去做识别。这样即使识别耗时100毫秒界面依然能正常刷新。4.2 识别结果显示、保存与日志识别完成后的成果要能在界面上直观看到。我用HWindowControl显示原始图和识别结果在车牌区域画一个矩形框旁边标注识别出的字符串和置信度。同时把每一帧的识别结果追加到DataGridView里包含时间、图片路径、车牌号、置信度、是否人工确认等字段。这些记录除了展示更重要的是便于回头分析。程序支持一键导出CSV可以拿到Excel里统计失败案例。日志文件单独写到一个log目录记录每次识别耗时、Halcon异常、相机断连等等。项目做久了你会发现日志越全定位问题越快。很多“神秘”的识别失败最后都是通过日志才还原出当时的图像条件的。4.3 扫码枪触发与循环采集时如何避免UI卡顿项目里还接入了扫码枪触发模式。扫码枪本质就是一个键盘输入设备扫到条码后会快速输出字符串程序在输入框的KeyDown事件里判断字符串正好是扫码结果后就触发一次车牌识别。这个功能在物流园区很实用扫快递单的同时抓拍车辆不需要人手点按钮。循环采集时最恶心的问题是UI刷新卡顿。Halcon的图像运算非常消耗CPU如果直接在UI线程里调用识别方法鼠标会转圈窗口拖不动。推荐用Task.Run配合Control.BeginInvoke来解耦识别在后台线程做UI只负责显示返回结果。还要注意图像对象用完必须Dispose否则内存上涨速度会让你怀疑人生。Task.Run(() { PlateResult result PlateRecognizer.Recognize(image); this.BeginInvoke(new Action(() { labelResult.Text result.PlateText; dataGridView1.Rows.Add(result.PlateText, result.Confidence); })); });这段代码模式简单但能解决绝大多数循环采集和UI刷新冲突问题。5. 实测数据与调参记录5.1 测试样本和统计结果我在整理这套项目说明时专门做了一轮测试测试图片200张来源是不同时段拍摄的园区进出车辆。其中白天顺光样本占60%傍晚和夜间补光样本占25%轻微倾斜和部分遮挡样本占15%。最终整牌识别正确率是90.5%字符级正确率在95%以上单张平均耗时约80毫秒用的是一块普通i5处理器的工控机。识别失败的情况也有明确统计定位失败占失败总数的60%主要发生在夜间强反光、车牌被拖车钩遮挡的场景分割错误占25%大多是边框和字符粘连OCR识别错误占15%集中在形近字和形近字母数字。5.2 最典型的三种失败情况与处理第一种是蓝色车牌区域被强光洗白HSV颜色提取直接失效。处理思路是增加一条灰度纹理检测路径车牌区域即使颜色失真字符和底板之间的对比度仍然存在可以通过边缘密度判断。第二种是夜间补光不均匀车牌一半亮一半暗。这时全局阈值肯定玩不转要用局部自适应阈值并对图像做一次伽马校正把暗部细节拉出来。第三种是汉字特征被模糊后误识别为其他字。这类问题靠调预处理参数收效甚微最终还是要回归到训练数据上多收集这类模糊汉字样本重新训练OCR分类器。5.3 影响识别率的硬件与环境因素算法再强也架不住图像本身质量差。根据我的实测车牌区域像素宽度低于80时字符分割就开始不稳定低于60时基本无法可靠识别。因此相机选型上200万像素在普通车道足够但车道宽度大、识别距离远时建议直接上500万或800万像素。安装角度也很关键俯视角度大于30度时车牌在图像里会严重透视变形单纯旋转变换已经拉不正需要做透视矫正。补光灯的位置不要正对车牌否则会产生大面积反光稍微偏一个角度效果反而好很多。做现场测试时先固定好这些硬件条件再去调算法参数否则每次调试场景都不一样结果没有可比性。6. 常见问题定位与排查6.1 Halcon环境报错的处理思路新手最常见的问题就是程序一运行就报“Halcon can not find feature”或者HALCON error #1201之类。这类错误绝大多数是运行环境不完整最常见的是系统里装了Halcon运行时但C#工程引用的dll版本和运行时版本不一致。遇到这种问题先检查环境变量HALCONROOT是否指向正确安装目录再从安装目录bin文件夹下重新添加halcondotnet.dll引用不要用旧项目遗留的dll。还有种可能是在开发机上能跑换一台没有装Halcon的电脑就跑不起来。解决办法是用Halcon的Runtime安装包一起部署或者把运行所需dll全部拷贝到exe同级目录。源码里也包含一份部署说明按里面的清单操作基本不会漏。6.2 C#调用Halcon的异常和内存问题运行时间久了报“Out of memory”通常是HObject没有释放。Halcon的HObject是托管包装但其内部图像数据仍在非托管内存中仅靠GC回收不够及时。规范做法是每用完一个中间对象就调用Dispose或者用try/finally确保释放。C#里循环处理图像时尤其要注意灰度图、区域、连通域这些临时对象。另一个常见问题是“试图访问无效的HObject”出现这个多半是因为图像处理链中某一步没有输出有效区域后续步骤却继续使用。排查时只要把每个中间步骤的结果用disp_obj显示在窗口里很快就能定位是哪一步断掉了。6.3 识别率上不去的排错顺序如果实测识别率明显低于90%我的建议是按“图像质量-定位-分割-识别”的顺序排查不要一开始就怀疑OCR模型。先看测试图片清晰度、车牌大小、光照条件是否合理再看定位区域是否准确框住了车牌然后检查字符分割图是不是每个字符都独立切出最后再分析字符识别结果。这个顺序的错误率最高也是最容易被人忽略的。曾经有一次识别率突然掉到70%整个团队折腾两天最后发现是相机对焦松了图像整体发虚。所以排错前先把素材截图存下来对比失败案例的共性往往比瞎试参数有效得多。7. 后续可以怎么扩展7.1 从传统视觉到深度学习识别90%在受控场景下够用但如果要面对更复杂的真实环境建议考虑Halcon的深度学习OCR或外部深度学习模型。传统方法对光照、角度、遮挡的鲁棒性提升空间有限模型方法在数据量足够的情况下能进一步逼近98%以上。当然接深度学习意味着需要GPU、数据标注和训练流程工程复杂度会明显上升。我的看法是传统算法作为兜底和快速原型非常合适当项目验证通过、有持续数据积累后再逐步切换或融合深度学习方案风险更小。7.2 多摄像机与结果联动源码目前面向单相机识别做成多路相机时需要把每路的识别任务放到独立线程或“线程池任务队列”中识别结果通过队列写数据库。这样每条车道都能独立工作某一台相机掉线也不会影响整体系统。项目说明里也预留了接口设计就是希望有人往这个方向再走一步。我在实际项目中还有一个小技巧把识别到的车牌和车辆入场时间绑定做成简单的进出场记录表后续再对接道闸和数据库就能变成一个轻量级停车场系统。骨架已经在这了扩展只是时间问题。最后再分享一个个人经验这套系统最花时间的不是算法而是图像质量的稳定。如果你准备复现建议先搞定相机角度、补光和触发逻辑再回头调整Halcon参数。先把“90%”这个基础目标做好有了稳定的测试基线再去追更高的识别率才不会白费力气。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价