搞机器视觉这行的,几乎没人能绕开颜色识别这个需求。药片分拣要按颜色分开,电子元件的色环电阻要读颜色码,印刷品检测要查色差,包装袋上的封口有没有印错颜色,这些都逃不过颜色识别这四个字。而这几年我在实际项目里用得最顺手的工具就是Halcon,特别是它的HDevelop交互式开发环境,能把颜色识别这种本来很啰嗦的活儿干得非常利落。
这篇东西我不会跟你讲太虚的概念,就围绕Halcon怎么做颜色识别这件事,把思路、原理、算子、参数、坑全部过一遍。适合刚接触Halcon、准备用他做颜色分类或者颜色检测的工程师,也适合已经用OpenCV做过颜色识别、想对比一下Halcon方案的开发人员。
1. 颜色识别需求与整体思路拆解
1.1 什么样的场景需要颜色识别
先别急着写代码,想清楚场景比什么都重要。颜色识别在工业现场的需求大致可以分成三类。
第一类是颜色分类。就是物体本身有几种固定颜色,需要把不同颜色的目标分开。最常见的是流水线上的塑料瓶盖分拣、药片分色、糖果分拣、马赛克瓷砖分色。这类需求的特点是颜色种类有限、色差明显,但往往对速度要求很高,一分钟几百个是常态。
第二类是颜色检测。目标颜色是固定的,只需要判断“对不对”。比如包装盒上的Logo印刷颜色是否达到标准,手机外壳的漆面色差是否在允许范围内,布料的染色是否均匀。这类需求表面上是求“是”或者“否”,实际上对稳定性要求极高,因为工业现场的光照会漂移,相机白平衡也会变,颜色判断稍微一抖就是大量误判。
第三类是颜色定位。就是用颜色特征把目标区域找出来,再在这个区域内做后续的OCR、缺陷检测或者尺寸测量。比如很多Halcon的定位项目里,先用颜色的强对比把目标从背景里捞出来,再局部放大看细节。
我在一个电子元件分选项目里就踩过这方面的大坑。当时客户拿来的样品在实验室灯光下颜色非常正,红就是红绿就是绿,结果上了产线,现场日光灯和窗外自然光混在一起,识别率直接从98%掉到70%。所以记住一句话:颜色识别从来不是算法问题,是光和颜色空间的问题。
1.2 为什么用Halcon而不是OpenCV
很多人问我,OpenCV也能做颜色识别,为什么要选Halcon?先声明我没有贬低OpenCV的意思,它免费、开源、社区大,做原型验证非常合适。但是放到工业项目里,Halcon的优势是实打实的。
最大的优势是HDevelop这套交互环境。OpenCV写颜色识别的流程大家都很熟悉:BGR转HSV、inRange、找轮廓、过滤面积。这套流程你一旦写成C++代码,改一个阈值就要重新编译一次。而Halcon的HDevelop里,阈值调整是实时的,鼠标一拖,结果就刷新在图像窗口上。调试一个颜色识别的视觉方案,用HDevelop可能半小时搞定,用OpenCV写原生代码可能要半天。
第二是算子库的成熟度。Halcon里跟颜色相关的算子非常齐全,从基础的通道分离decompose3、颜色空间转换trans_from_rgb,到复杂的色彩分类、颜色聚类,都是直接封装好的。更关键的是这些算子在工业PC上跑了很多年,稳定性和速度都经过验证,不会出现OpenCV不同版本之间API变动导致代码大面积重写的情况。
第三是部署链路完整。Halcon代码可以很方便地导出为C++、C#的DLL,不管是集成到Qt界面、C#的WinForm,还是嵌入到PLC控制的工控机程序里,都有成熟的接口。这也是热词里“qt怎么调用halcon”“halcon导出dll”被频繁搜索的原因——说明大家开发环境虽然不同,但最后都绕不开把它变成一个产线能用的模块。
当然Halcon是商业软件,授权费不便宜。但如果在项目预算允许的情况下,我个人的看法是工具的成本早就被开发效率摊薄了,一个工程师省下来的一周调试时间,往往比授权费值钱得多。
1.3 整体识别思路:分离—转换—阈值—特征
Halcon做颜色识别的套路,我总结下来就四步。
第一步是通道分离。工业相机拍出来的图像通常是RGB三通道,Halcon里图像是被当作一个整体对象处理的,你要先用decompose3把R、G、B三个通道分开。这一步看着多余,其实是为了后面灵活处理——有时候你根本不需要三个通道参与,比如红色提取主要看R通道的突出程度。
第二步是颜色空间转换。把RGB转到HSV、HSL或者CIELAB这样的空间里,目的是把颜色的本质信息和亮度信息解耦。这个后面会详细讲,关键点在于RGB在光照变化下的稳定性太差,而HSV/HSL的色相(H)通道描述的就是“什么颜色”本身,和亮暗关系不大。
第三步是阈值分割。在选定通道上做一个threshold,得到目标区域。红、绿、蓝这类颜色,在HSV空间的H通道上有明显的区间,比如红色大致在0到10和156到180(Halcon里H分量范围是0到360),绿色在50到70左右,蓝色在100到130左右。通过区间设定,就能把对应颜色的像素捞出来。
第四步是特征提取与结果输出。分割出来的区域往往有噪点,需要做形态学处理、连通域分析,再用select_shape筛选出符合面积、宽高比的目标,最后用area_center或者get_region_points输出目标的位置和数量。如果项目需要,这一步还可以和OCR、条码识别串起来。
这四个步骤是Halcon颜色识别的通用骨架,下面所有的原理和代码都是围绕这个骨架展开的。
2. Halcon颜色识别核心原理
2.1 RGB与颜色空间的那些事
要把颜色识别做好,理解颜色空间至关重要。我们日常接触最多的RGB,其实是面向显示设备的模型,红色、绿色、蓝色的加色混合。但RGB有一个致命的弱点:它对光照极其敏感。
举个最简单的例子,一块红色的塑料板,在强光下拍摄,R通道数值可能接近200,G和B也可能被环境光抬到100以上;在阴影里拍摄,R可能掉到100,G和B大概在50左右。如果你直接对RGB三通道做固定阈值,同样的物体在不同亮度下像素值完全不同,阈值没法定死,识别就不稳定。
所以工业上做颜色识别的第一课,就是脱离RGB思维,把颜色和亮度分开看。HSV和HSL模型就是为了解决这个问题而生的。
HSV把颜色拆成三个分量:色相H、饱和度S、明度V。色相H描述的是“这是红还是绿还是蓝”,用一个角度表示,0度是红色,120度是绿色,240度是蓝色。饱和度S描述颜色的鲜艳程度,一张纯红色的卡片S值很高,而一张经过光照射褪色的卡片S值较低。明度V描述的是亮度,和颜色没什么关系。
HSL和HSV结构类似,差别在于L亮度分量的定义方式不同,但做阈值分割的时候,我们最关心的是H分量,所以两个模型都可以用。Halcon里trans_from_rgb支持多种目标空间,可以用'xyz'、'hsv'、'hls'、'cielab'、'cielch'等等,实测下来hsv和hls在颜色分类上的表现非常接近。
另外还有一个非常值得关注的模型是CIELAB。工业上如果对颜色精度要求极高,比如色差检测、印刷品颜色校准,CIELAB是首选。因为它基于人眼对颜色的感知设计,Lab分量之间的距离和人的感知色差是线性对应的,可以方便地定义“允许最大色差是多少”。Halcon里用trans_from_rgb转换到'cielab'后,a通道从绿到红,b通道从蓝到黄,这两个通道组合起来对颜色描述能力非常强。
2.2 通道分离与转换算子怎么用
通道分离用decompose3,这是最基本的操作。假设你已经把图像读到了变量Image里,一行代码就能把RGB拆出来:
decompose3 (Image, ImageR, ImageG, ImageB)拆完之后的ImageR、ImageG、ImageB分别对应红色、绿色、蓝色通道的单通道图像。你可以用dev_display分别查看每个通道的灰度情况,比如红色区域在ImageR里会显得很亮,在ImageB里则接近黑色,这种灰度差异本身就是一种分类依据。
接下来是颜色空间转换。算子叫trans_from_rgb,它处理的就是我们刚刚分解出来的三个通道。常见用法是把RGB转到HSV:
trans_from_rgb (ImageR, ImageG, ImageB, ImageH, ImageS, ImageV, 'hsv')关键是最后一个参数,目标颜色空间。可选值包括'hsv'、'hls'、'cielab'、'cielch'、'xyz'等。输出图像ImageH、ImageS、ImageV分别对应转换后的三个分量。
这里有一点必须提醒:trans_from_rgb对输入图像的字节类型有要求。如果输入的是byte类型的图像,转换后的H分量依然是byte类型,范围被映射到0到255,但实际H分量的理论范围是0到360。所以当你用threshold提取H分量区域时,H=255对应的是理论上的360度,分割阈值需要按这个比例换算。我当时第一次做红色提取就卡在这里,按H应该在0到10度附近去设阈值,结果用byte图像阈值设成0到8,还算能用,但换了uint2类型图像的场景,同样的物理颜色阈值就完全要重设,这一点极易踩坑。
如果需要输出更高精度的浮点分量,可以在读取图像后先用change_domain或者convert_image_type把图像转成real类型再转颜色空间,这样H分量保留的角度信息更完整,代价是计算量增加,工业现场一般不必这么做。
2.3 阈值分割的策略对比
分割这一步,不同场景有不同策略。我自己在项目里常用三种方式。
第一种是直接对H通道做单阈值分割。这适合颜色种类固定、色相区间明确的情况。比如绿色背景上找红色目标,直接对H通道threshold,把红色区间捞出来,又快又准。
第二种是同时约束H和S两个通道。很多情况下,只用H通道会误检。比如一个亮灰色物体,它的色相H值不稳定,S值却很低,说明它是一个不饱和的颜色,也就是接近灰白的无彩色。如果不约束S,这个物体可能会被某个H区间误判成彩色。所以在做阈值判断时,一般会把S也纳入条件:既要求H在目标色区间,又要求S足够高,确保它是一个真正的饱和色。
Halcon支持多通道组合条件的模板,最方便的方式是写一个条件数组并调用threshold。实际上threshold是单图像操作,要处理多通道条件,可以先在H通道上阈值得到区域RegionH,再在S通道上阈值得到区域RegionS,最后用intersection求交集。
第三种是使用颜色聚类或分类器。如果目标颜色受光照影响很大,颜色种类又多,固定的阈值就很难覆盖所有情况。这时候可以用Halcon的add_samples_image_class_gauss之类的算子采集样本,训练一个高斯分类器,然后对全图进行分类,输出每个像素属于哪个颜色类别的概率。这个方案灵活性强,但对算力和开发周期的要求也高,我通常只在颜色特别接近、阈值法完全搞不定的时候才上。
阈值分割没有绝对的金标准,核心衡量指标是:在正常工况的光照波动范围内,分割结果是否稳定。稳定压倒一切,哪怕阈值设在看起来“不那么完美”的位置,只要它在所有样本上都能正确分出来,它就是好阈值。
3. 完整实操流程与代码实现
3.1 环境准备与图像采集
动手写代码之前,先确认环境没问题。Halcon安装、License激活这些基础步骤我就不啰嗦了,网上的教程一搜一大把。这里只强调两个容易被忽略的点。
第一,HDevelop的环境变量。很多人把Halcon导出成DLL的时候,C++项目编译总报找不到头文件,多半是环境变量没配。Halcon安装完以后,HALCONROOT这个环境变量会被写进系统,C++项目里要用到的头文件路径一般是$(HALCONROOT)/include/halconcpp,库文件路径是$(HALCONROOT)/lib/$(HALCONARCH)。如果编译器提示找不到hdevelop相关的库,优先检查这两个路径配没配对。
第二,图像采集接口。颜色识别项目里,相机的色彩还原度是底层保障。USB相机在控制成本和开发速度上有优势,但如果你发现颜色总是飘,先别急着怀疑算法,先查一下相机是不是自动白平衡开着。工业现场做颜色识别,相机的自动白平衡、自动增益、自动曝光全部要关掉,用固定参数拍摄。否则算法调的再好,相机参数一变,阈值全部失效。我在一个项目里花了整整两天排查颜色漂移问题,最后发现是相机的自动曝光没有禁用,午后阳光照进车间,曝光时间自动变短,颜色整体偏暗,阈值全乱。这个教训相当深刻。
通常我在新项目里会先用HDevelop连接相机,执行grab_image连续采集十几张图像,确认颜色稳定后才开始写识别逻辑。
3.2 核心算子的选择与参数配置
正式进入颜色识别流程,核心算子我刚才已经提过几个,这里逐一说明用法和参数。
read_image负责从文件读图:
read_image (Image, 'test_image.png')如果是连接相机,则是:
open_framegrabber ('GigE Vision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'camera_serial', 0, -1, AcqHandle) grab_image (Image, AcqHandle)通道分离和颜色转换:
decompose3 (Image, ImageR, ImageG, ImageB) trans_from_rgb (ImageR, ImageG, ImageB, ImageH, ImageS, ImageV, 'hsv')阈值分割和区域处理:
threshold (ImageH, RegionH, 0, 20) threshold (ImageS, RegionS, 60, 255) intersection (RegionH, RegionS, RegionRed) connection (RegionRed, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 500, 99999)最后一行的select_shape是筛选区域,'area'表示按面积过滤,'and'表示条件与关系,后面的500到99999是面积范围,单位是像素。这个参数要根据你的相机分辨率和目标大小调整,比如500万像素相机下一个药片可能有几万个像素,面积范围就要放大。
如果要把识别结果显示在窗口上,还需要这样处理:
dev_clear_window () dev_display (Image) dev_set_color ('red') dev_display (SelectedRegions)3.3 一个完整的HDevelop示例脚本
我写一个完整的颜色识别示例,目标是从图像里把红色、绿色、蓝色三种圆形目标分别提取出来,并输出位置坐标。这个脚本可以直接在HDevelop里跑通,你把图像路径换成本地文件就行。
* 读取图像 read_image (Image, 'color_sample.png') * 通道分离与颜色空间转换 decompose3 (Image, ImageR, ImageG, ImageB) trans_from_rgb (ImageR, ImageG, ImageB, ImageH, ImageS, ImageV, 'hsv') * 提取红色区域:H在0~20和156~180之间,S大于60 threshold (ImageH, RegionH1, 0, 20) threshold (ImageH, RegionH2, 156, 180) concat_obj (RegionH1, RegionH2, RegionHR) threshold (ImageS, RegionS, 60, 255) intersection (RegionHR, RegionS, RegionRedFull) * 提取绿色区域:H在40~80之间,S大于60 threshold (ImageH, RegionHG, 40, 80) intersection (RegionHG, RegionS, RegionGreenFull) * 提取蓝色区域:H在100~140之间,S大于60 threshold (ImageH, RegionHB, 100, 140) intersection (RegionHB, RegionS, RegionBlueFull) * 区域后处理:形态学开运算去除噪点 opening_circle (RegionRedFull, RegionRedOpened, 3.5) opening_circle (RegionGreenFull, RegionGreenOpened, 3.5) opening_circle (RegionBlueFull, RegionBlueOpened, 3.5) * 连通区域划分 connection (RegionRedOpened, RedRegions) connection (RegionGreenOpened, GreenRegions) connection (RegionBlueOpened, BlueRegions) * 按面积筛选,排除杂点 select_shape (RedRegions, RedSelected, 'area', 'and', 1000, 99999) select_shape (GreenRegions, GreenSelected, 'area', 'and', 1000, 99999) select_shape (BlueRegions, BlueSelected, 'area', 'and', 1000, 99999) * 输出每个区域的面积和中心坐标 area_center (RedSelected, RedAreas, RedRow, RedColumn) area_center (GreenSelected, GreenAreas, GreenRow, GreenColumn) area_center (BlueSelected, BlueAreas, BlueRow, BlueColumn) * 显示结果 dev_clear_window () dev_display (Image) dev_set_color ('red') dev_display (RedSelected) dev_set_color ('green') dev_display (GreenSelected) dev_set_color ('blue') dev_display (BlueSelected)这个脚本的核心逻辑就是在HSV空间里分别对H区间做阈值,配合S通道一起限制,再经过形态学和连通域处理得到干净的目标区域。实际项目里,根据不同颜色占比和干扰情况,可能还要加开闭运算、填充孔洞的操作,提醒一句:开运算用的圆形结构元素半径不宜过大,过大的开运算会把小目标整个抹掉,一般从1.5到5之间起步调。
3.4 通过参数调整提高识别鲁棒性
脚本跑通只是第一步,真正考验人的是参数调整。我刚入行的时候,以为阈值区间是固定死的,后来才发现,好参数是“试”出来的,但试得有方法。
第一个建议是采集一组覆盖不同光照条件的样本图。不要在单一时刻的单一图像上调参,要故意采集早上、中午、傍晚、开灯、关灯各种情况下的样本,放到一组里,然后逐个测试阈值。如果某组阈值在20张样本上都能稳定分割,那才是靠谱的阈值。
第二个建议是善用HDevelop的“实时调参”特性。把阈值参数定义成变量,比如MinH、MaxH,然后修改这些变量时观察分割结果。HDevelop左边参数栏改了值,右边窗口立刻刷新,这种实时反馈是OpenCV给不了的体验。
第三个建议是程序里尽量用相对值而不是绝对值。比如直接用threshold的绝对阈值定H区间是可行的,但如果整幅图像亮度波动范围大,建议先用scale_image_max做一次对比度拉伸,或者用equ_histo做直方图均衡化来减轻光照变化影响。当然这些预处理会轻微改变颜色语义,所以要在样本上验证后决定是否使用。
第四个建议是把各颜色的阈值写进配置文件,而不是硬编码在代码里。产线设备种类不同、光源不同,同一台机器的阈值换到另一台就要微调。把阈值外置成参数文件,到了现场只要在界面上改几个值,不用动代码,售后成本能低一半。
4. 实战案例:红绿蓝三色圆片分拣
4.1 项目背景和图像特征分析
这个项目是给一家电子元件厂做的,产线上有红、绿、蓝三种颜色的圆形塑料垫片,需要按颜色分到三个料盒里。传送带速度不算快,节拍大约每秒3个。相机是500万像素的工业黑白相机加彩色滤镜?不对,颜色识别必须用彩色相机。用的是500万像素的彩色工业相机,镜头焦距16mm,拍摄视野大概20cm×15cm,目标垫片直径约2cm,在图像里占的像素面积大约1.2万左右,还是很清晰的。
这个项目最麻烦的地方在于传送带表面是深灰色的,垫片放在上面对比度一般,而且深灰色属于低饱和度颜色,如果不加S通道约束,可能会被误分为某种彩色。另外垫片表面略带反光,强光下高光区域会泛白,颜色饱和度下降,这部分区域容易被阈值漏掉。
我当时的处理思路是充分利用H+S双通道约束,同时对高光区域做容错处理。
4.2 步骤演示与分色逻辑
完整流程在3.3节已经写了,这里重点说几个项目里针对实际情况做的调整。
首先是ROI区域的限制。传送带边缘、相机视野之外的部分都是干扰,直接用reduce_domain把识别范围限定在传送带中央区域,能少处理很多背景噪点。代码上就是先画一个矩形RegionROI,然后reduce_domain(Image, RegionROI, ImageReduced),后续所有操作都基于ImageReduced。
其次是针对反光高光部分的处理。垫片高光区域在HSV空间里的表现是S值很低,H值紊乱。这部分像素虽然属于垫片,但被S通道过滤掉了。解决方法是形态学闭合:颜色分割完区域后,用closing_circle把小的空洞和断裂边缘补上。我用的半径是5.0,效果很好,垫片区域变得完整连续。
再来是分类逻辑。红绿蓝三种颜色同时存在时,要避免同一个目标被判成两种颜色。比如一个偏紫的红色区域,H在150附近,可能同时落在红色的边界区间(156~180)和蓝色的边界区间(100~140)外层。为了避免重判,对每个连通域,用sort_region按面积排序后,再套一层“最大所属区域”逻辑:如果一个连通域重心落在多个颜色区域内,只统计面积最大的那个颜色。
最后实际效果如下表:
| 颜色 | H区间(度) | S最小值 | 开运算半径 | 正确识别率(200个样本) |
|---|---|---|---|---|
| 红色 | 0~20 或 156~180 | 60 | 3.5 | 98.5% |
| 绿色 | 40~80 | 60 | 3.5 | 97.5% |
| 蓝色 | 100~140 | 60 | 3.5 | 99.0% |
整体识别率能达到98%以上,剩余1%~2%的误判主要发生在垫片严重磨损、表面颜色变淡或者光照突变的情况下。这些特殊情况在产线人工质检时也是会被挑出来的,所以客户对这个结果很满意。
4.3 结果评估与误差分析
要客观评价颜色识别方案,不能光看正确率,还要看误差来源。我复盘这个项目时总结出三个误差来源。
第一是相机色彩还原误差。不同相机对红色的响应曲线不同,同一个物体用两个牌子相机拍,RGB值是有差异的。所以在项目初期选型时就要用目标物体的实物做测试,不要只看厂商给的参数表。
第二是光源老化。LED光源用久了亮度会下降,色温也可能漂移,导致H区间整体偏移。我一般建议客户每个月做一次亮度校准,用标准色卡拍一张参考图,比对当前图像和基准图像的颜色分布,发现偏差超过设定值就提醒维护。
第三是目标物自身颜色漂移。比如塑料垫片在注塑过程中,不同批次之间的染料配比可能有细微差异,颜色有时候偏亮红、有时候偏暗红。这个没法靠算法完全消除,只能尽量把颜色阈值区间设置得足够宽,同时保证目标颜色区间不会被其他颜色占用。这就是为什么我在做产品化的视觉方案时,一定会要求客户提供不同批次的样品,而不是只看几个“理想样本”。
5. 常见问题与排查技巧实录
5.1 光照变化导致误识别怎么办
这是颜色识别项目里被问得最多的问题,也是最难根治的问题。我的排查顺序是:先看相机设置,再看光源,最后才看算法。
相机方面,把自动曝光、自动白平衡、自动增益全部关掉,这是基础。光源方面,尽量用稳定的白色光源,比如白色LED条形光源或者环形光源,避免频闪。现场光线如果昼夜变化很大,建议在相机外增加遮光罩,把环境光挡住。
算法层面,优先使用HSV的H通道分割,因为H通道对光照变化相对不敏感。如果H通道还是漂,可以考虑转用CIELAB颜色空间,它对人眼感知色差的建模更精细。再不行就上分类器,用add_samples_image_class_gauss采集多组样本训练高斯分类器,让算法学习光照变化下的颜色分布,而不是死守固定阈值。
我见过一个案例,客户死活不想改光源,结果我们只能把H阈值范围放宽10%,代价是增加了误检率。最终通过加了一个“面积必须大于N像素”的限制条件才压住误检。这个思路也可以参考——有时候别指望一个条件解决所有问题,多个弱条件叠加,效果常常比一个强条件更稳定。
5.2 颜色相似的物体区分不开
如果两种颜色的色相区间重叠,阈值分割就直接失效了。比如深蓝色和紫色,红色和橙色,有时候肉眼都容易搞混,算法更难。
几个可以尝试的方向:一是把颜色的亮度维度也纳入判断,比如红色和橙色在H上接近,但红色的亮度通常比橙色低,加上V通道的阈值就可以进一步区分。二是转到CIELAB空间,用a、b通道的欧氏距离来计算颜色相似度,设定一个最大允许距离,超过就判定为颜色不符。三是增加纹理特征辅助,有些颜色相近但表面纹理不同的物体,可以通过频域特征区分。
如果这些方法都试了还不行,那就要考虑换思路,比如用多光谱相机或者增加偏振光滤镜来获取额外信息。但这些都是成本较高的手段,适合高端项目。
5.3 识别速度慢,节拍跟不上
颜色识别如果做全图逐像素处理,几百毫秒就没了,产线根本扛不住。优化方向有两个:缩小处理范围和简化算法。
缩小处理范围是最有效的。用reduce_domain只处理目标出现的区域,其他地方全部跳过,计算量直接降一个量级。如果目标位置和姿态在传送带上是固定的,甚至可以只对几个固定位置的小ROI做识别,速度极快。
简化算法方面,如果只是判断“有没有某种颜色”,就不需要做完整的连通域分析。可以先对全图做一次阈值,统计大于阈值的像素数量,数量超过设定值就判定为存在该颜色,这样速度能提升好几倍。
另外一个容易忽略的点是图像分辨率。很多颜色识别任务根本不需要500万像素全图,把感兴趣区域裁剪出来或者缩小图像尺寸再处理,识别速度能提升不少。我在调试时发现,300万像素的图像缩到80万像素,颜色识别的准确率几乎不变,速度却快了一倍多。
5.4 和深度学习、OCR等技术的配合
颜色识别经常不是孤立存在的,它往往会和Halcon的其他功能配合使用。这里列几个常见的组合场景。
如果你要做的是螺纹、焊缝这种磨砂面的缺陷检测,颜色信息往往是失效的,因为表面颜色太单一,需要的是光度立体或者自适应边缘提取这类技术。但颜色识别可以作为第一步的区域定位手段:先用颜色把工件从背景里分割出来,再用光度立体或边缘提取方法在工件区域内做精细检测。两个阶段各司其职,效果很理想。
和深度学习的配合也很常见。Halcon自带的深度学习目标检测可以先用训练好的模型把目标物体框出来,然后在框内做颜色识别。这样颜色识别不需要在全图范围内做,既提高了速度,又避免了背景颜色干扰。反过来,颜色识别也可以作为深度学习模型的辅助输入,比如先用颜色特征筛选出疑似缺陷区域,再用训练好的分类模型对区域做最终判断,两者结合可以有效降低误检率。
再把颜色识别和OCR放一起,场景也很常见。有些药品包装上的生产日期是彩色的,背景也是彩色的,直接用OCR读会受背景干扰。先做颜色分割把字符颜色对应的区域提取出来,再把区域交给OCR识别,成功率会明显提升。Halcon的OCR本身就支持在区域上识别,这个组合非常顺手。
5.5 关于导出DLL与跨语言调用的补充
很多工程师在HDevelop里调通了颜色识别,下一步就要集成到自己的软件里。热词里“qt怎么调用halcon”“halcon导出dll”“将halcon代码生成dll”被反复搜索,说明这是普遍痛点。
在HDevelop里调通代码后,用菜单File→Export就可以导出C、C++或者C#代码。导出后把核心函数封装成独立的类,需要特别注意图像对象的生命周期管理,Halcon的HObject对象在C++里是引用计数的,传递的时候不能随便释放底层句柄。
用C#集成的话,Halcon提供HalconDotNet库,直接引用即可。在Qt里集成,无非是调用C++的DLL或者直接用C++接口编译Halcon库,关键是包含路径和库路径要配对。这里有一个通用的建议:导出DLL时把输入输出定义成最基本的数据类型,比如图像路径、阈值参数、识别结果的结构体,不要让上层应用直接操作HObject,这样接口清晰、耦合度低,后期也方便维护。
不过说句实在话,DLL封装和跨语言调用的问题,属于颜色识别项目之外的工程问题,如果你在HDevelop里流程已经调通,大致框架已经确立,回过头来细抠DLL接口,一般不会出太大的幺蛾子。
结语与个人经验
做了这么多年视觉项目,我愈发觉得颜色识别像是一个“嘴上说简单、上手才知道水有多深”的活儿。表面上看就是几个阈值、几个算子,但真实落地的时候,光照、相机、物体材质、环境背景,每个环节都能给你写好几页的“惊喜”。
我个人的体会是:做颜色识别不要一上来就埋头调参,先花时间把环境和图像质量搞稳定,比什么都重要。我踩过最大的坑就是相机自动曝光没关,导致调好的参数在第二天早晨一开机就全废。从那以后,我每个项目的启动会议上都会把“相机参数固定、光源稳定、标准色卡入厂”这几条作为硬性约束写进需求文档。
最后再分享一个小技巧:在HDevelop里调试颜色阈值时,不妨把阈值参数做成变量,再用调试手柄实时滑动找最佳值,找到之后别急着写死,多采集几十张现场样本验证一下。颜色识别这个东西,样本量越大,你越能发现它的脾气,也越能做出真正在产线上扛得住的方案。希望这篇文章能帮你少走一些弯路,有具体问题也欢迎在评论区聊,我尽量抽时间回复。