资讯动态

Halcon HSV颜色匹配实战:从RGB阈值不稳到稳定定位

发布时间:2026/9/15 2:45:15 来源:尧图企业网站定制
做机器视觉这些年被问得最多的问题之一就是“怎么按颜色找目标”。我最早做颜色检测的时候用的办法很直接把图像的RGB三个通道拆开然后对着R、G、B分别设阈值。一开始觉得挺合理R通道值大就是红色嘛后来被现实狠狠教育了一顿。有一次做红色塑料件的颜色定位同一台相机、同一个工件因为工位在窗边中午和下午的光照条件不一样R通道的数值直接漂了将近50个灰度级固定的RGB阈值根本锁不住目标。后来把颜色空间从RGB换成了HSV阈值一下就稳定了项目也顺利交付了。这篇东西我就把在Halcon里做HSV颜色匹配的完整思路、可复现代码和踩过的坑整理出来给正在做颜色识别、颜色分选、视觉定位的朋友一个参考。1. RGB直接阈值分割为什么到HSV这里才是正解1.1 RGB颜色空间的“直觉陷阱”RGB是显示器、相机传感器最底层的颜色表达方式但它的结构对人类理解颜色非常不友好。RGB三个通道各自表示红、绿、蓝三种色光的强度两个颜色在视觉上是否“接近”在RGB空间里并不是一个线性的距离关系。举个例子一个红色物体在标准白光下可能是RGB(200, 30, 25)但当光照变弱之后三个通道的数值会同步缩小变成(100, 15, 12)甚至(60, 8, 6)。从RGB坐标来看这两个颜色在三维空间里的欧氏距离非常远但在人眼和相机看来它们就是同一个红色只是亮度不同。你用固定阈值的RGB分割去处理这样的场景等于把同一个颜色切成了两半结果当然不稳定。这里有一个更隐蔽的问题RGB三个通道的数值是高度耦合的。只要光照强度变化三个通道一起变只要光源色温变化三个通道的比例也会变。在工业现场光源老化、环境光干扰、工件表面高低起伏导致的反光差异都会让RGB数值出现不可控的漂移。你就算把阈值区间放宽也只能在“把目标认全”和“把背景也认进来”之间反复横跳。1.2 HSV分离“颜色”与“亮度”到底意味着什么HSV颜色空间把颜色拆成了三个互补的维度HHue色相表示这是什么颜色SSaturation饱和度表示颜色浓不浓VValue明度表示整体亮不亮。这个结构最核心的价值在于把“颜色信息”和“亮度信息”解耦了。光照变化影响最大的是V通道对H通道的影响要小得多。也就是说只要目标还是那个颜色不管光照偏亮还是偏暗H值基本能保持在一个相对稳定的区间。这一点在实际项目中非常关键。同样是红色塑料件RGB空间里光照一变就“变色”HSV空间里H值不动S和V虽然会漂移但你可以通过放宽S/V的阈值来吸收这部分波动。工业视觉里还有个常见的困惑黑白灰物体怎么处理白色是“高V低S”黑色是“低V低S”灰色是“中V低S”。只要在HSV空间里同时约束S和V这些非彩色区域很容易被剔除掉。而在RGB空间里白色区域的R、G、B三个值都很大黑色区域三个值都很小如果目标本身是深红色黑色区域和暗红色区域在R通道上几乎无法区分。我用一个生活化的类比帮助新人理解H是颜料本身的种类S是往颜料里掺了多少白颜料V是拿手电筒往画上照的光强。你想按颜色找东西应该盯住颜料种类而不是被手电筒亮不亮带偏。1.3 哪些场景该用HSV哪些不该HSV并不是万能的它适合的场景有很明显的边界。我做过的项目里以下几类用HSV效果最好颜色分选类比如不同颜色的药片、塑料粒、糖果分类目标颜色饱和度高、彼此差异大。色差明显的缺陷检测例如白色底板上找黄色污渍、红色异物。按颜色定位目标区域颜色鲜明且相对均匀比如红色圆形盖子、蓝色端子座。但不建议无脑上HSV的场景也很多。比如目标颜色和背景颜色非常接近色差很小浅灰和白的区分、透明件上的轻微划痕HSV未必帮得上忙又比如丝绸、拉丝金属这类高反光材质表面会出现镜面反射反光区域在HSV空间里表现为S极低、V极高会破坏颜色区域的完整性。这种情况下与其死磕颜色空间不如先改善打光方式比如用漫射光源、低角度光源压制反光。2. Halcon环境准备与HSV空间的三个认知误区2.1 Halcon的H值范围不是0到360这是所有从OpenCV转过来的人最容易踩的坑。OpenCV把HSV的H值范围定义为0到180因为要压缩进一个字节MATLAB里H是0到1而Halcon的H通道在byte类型图像里的范围是0到255对应色相环的0到360度做线性映射。这个差异不是小问题。你在OpenCV里用H范围0到10提取红色到了Halcon里还是写0到10结果就是什么都提不到。我用一张映射表整理了常见颜色在Halcon HSV空间里的参考区间方便你调试时心里有数颜色色相角度Halcon H值参考区间典型S值区间典型V值区间红0°/360°附近0~20 或 235~25580~25560~255黄60°38~4860~255100~255绿120°78~9260~25570~255青180°120~13660~25580~255蓝240°160~18080~25560~255品红300°200~22280~25570~255注意上表只是参考起点。相机的色彩还原特性、光源色温、目标材质都会造成偏移实际项目中一定要用真实图像重新标定。但知道这个映射关系你至少不会把初始值设到离谱的位置。2.2 用rgb1_to_hsv还是trans_from_rgbHalcon里做RGB转HSV有两个常见的算子选哪个取决于你手上有什么形式的图像数据。如果读进来的是一张三通道RGB图像直接用rgb1_to_hsv最简洁read_image (Image, color_image.png) rgb1_to_hsv (Image, ImageH, ImageS, ImageV)如果你已经把多通道图像拆成了R、G、B三个单通道图或者你从Camera直接拿到的是三个独立的通道数据就用trans_from_rgbdecompose3 (Image, R, G, B) trans_from_rgb (R, G, B, ImageH, ImageS, ImageV, hsv)这里有三个细节值得注意第一rgb1_to_hsv期望输入是byte类型的RGB图像如果是其他类型比如uint2或int4需要先做类型转换。很多工业相机输出的是12位或16位灰度图要合成RGB后再转不要直接在原图上硬转。第二trans_from_rgb的最后那个颜色空间参数写成hsv还可以填cielab、cielch、yiq等这是后续做色彩空间对比实验很方便的一个入口。第三decompose3拆出来的是三个通道的单通道图这三个图在Halcon里显示出来都是灰的这是正常现象不代表你的图像没有颜色信息。很多人第一次看到H通道图傻眼了以为转换失败其实只要用阈值一分割颜色区域马上就能看出来。2.3 显示问题H通道看起来根本不是彩色的这是第二个高频误解把ImageH用dev_display显示出来屏幕上是一个灰色图像于是立刻怀疑算子用错了。我在这里很确定地告诉你没有错。Halcon对单通道图像的默认显示方式就是灰度渲染H、S、V三张图拆开之后每一张都是单通道看起来都是灰度图。H通道的灰度级255对应色相环上的一个完整圆周但人眼没法从灰度上直接还原出“红色”还是“蓝色”。所以调试HSV阈值时不能只靠眼睛看必须结合灰度直方图来判断。用gray_histo可以统计指定区域内的灰度分布你可以在目标区域上画一个ROI看H通道直方图分布在哪个区间再用这个区间去设阈值。这个方法比我“凭经验猜数值”高效得多。如果你实在不习惯这种灰度显示可以点HDevelop菜单栏里的“助手-Color Inspection”这个工具可以可视化地显示颜色区域交互式地选目标颜色并自动生成阈值代码。我实测它生成出来的初始参数可以直接跑通八成情况剩下两成需要针对现场光照手动微调。3. 完整实例HSV颜色匹配定位红色圆形工件3.1 实例目标和图像预处理这次的实际项目场景是一条装配线机器人要从传送带上抓取一个红色圆形塑料盖视觉系统负责输出目标的像素坐标。背景是深蓝色输送带周围偶尔出现金属反光件。目标特征非常典型颜色鲜明、形状规则、面积相对稳定。这种场景就是HSV颜色匹配的主场。读取图像之后我的习惯是先看一眼图像尺寸再做一步常规检查read_image (Image, red_cap.png) get_image_size (Image, Width, Height) dev_display (Image)有些项目会在预处理阶段先做高斯滤波去噪再转HSV。我的建议是第一版先不做滤波直接转HSV看原始效果。原因很简单HSV分割的鲁棒性主要靠阈值区间而不是靠滤波。滤波反而可能模糊颜色边界让后续的形态学处理变得复杂。只有在噪声确实导致阈值分割出现大量碎点的时候再考虑加一个轻度的median_image。3.2 核心步骤转HSV、H通道阈值、S/V联合约束下面是完整的HDevelop关键代码我加了详细注释* 读取图像 read_image (Image, red_cap.png) * RGB转HSV rgb1_to_hsv (Image, ImageH, ImageS, ImageV) * 第一步H通道阈值锁住“红色”区间 * Halcon色环里红色在0附近这里取了0~20这个区间 threshold (ImageH, RegionRedH, 0, 20) * 第二步S通道阈值排除白色和浅色区域 * S太低说明颜色很淡接近白色需要剔除 threshold (ImageS, RegionRedS, 60, 255) * 第三步V通道阈值排除过暗的阴影和过亮的镜面高光 threshold (ImageV, RegionRedV, 40, 220) * 三个条件求交集同时满足才是我们认的“目标红” intersection (RegionRedH, RegionRedS, RegionRedHS) intersection (RegionRedHS, RegionRedV, RegionRed) * 形态学处理先开运算去碎点再闭运算填充内部小孔 opening_circle (RegionRed, RegionOpened, 2.5) closing_circle (RegionOpened, RegionClosed, 3.5) * 连通域分析 connection (RegionClosed, ConnectedRegions) * 按面积筛选排除明显不可能是目标的小噪点和大型干扰块 select_shape (ConnectedRegions, SelectedRegions, area, and, 800, 50000) * 输出目标的面积、中心坐标 area_center (SelectedRegions, Area, Row, Column)这段流程看着不复杂但每一步都有它的用意。重点说三个容易被忽略的地方。第一为什么H通道阈值取0到20而不是一次性把整个H范围都算上因为红色在Halcon的色环上是一个很窄的区间而且它跨越了0度这个起点所以实际红色区域会分布在0~20和235~255两段里。这个特性我单独拿出来讲见下一小节。第二S和V的约束为什么不能省我调试的时候见过很多次这样的情况单看H通道红色目标的区域确实被提取出来了但图像里一些白色背景的边缘、灰色工件上的反光点它们的H值恰好落在0~20这个区间里。这是因为对于任何饱和度极低的像素H值都变得没有意义可以落在任意位置。加上S和V的联合约束之后这些干扰像素基本都能被筛掉。第三形态学操作的半径不是随手填的。开运算半径2.5对应的是这个场景里常见的孤立噪点直径。V通道的离散噪声通常会形成1到3像素的碎点取半径2.5可以一次性清掉闭运算半径3.5是因为目标内部偶尔会出现V值过高的镜面小点造成几个像素的孔洞用这个半径可以补上。如果你换了一个像素分辨率更高的相机这些半径需要按比例同步放大。3.3 处理红色“跨边界”的情况上一节我留了一个尾巴红色在HSV色环上跨越了0度边界。也就是说一个完整红色区域在H通道里会被切成两段一段在0附近一段在255附近。如果你只取0到20亮度偏暗、色相略微偏品红的红色区域可能落在235到255这段里直接丢失。解决办法也很简单分两次阈值、再求并集threshold (ImageH, RegionRedH1, 0, 20) threshold (ImageH, RegionRedH2, 235, 255) union2 (RegionRedH1, RegionRedH2, RegionRedH)这段代码把H通道的“红色两端”都收进来再和S、V区域求交集。我工作中处理红色目标时几乎永远都写这两条threshold。顺便提一句如果你要提取的目标颜色是品红或者偏紫的颜色也要注意它在色环上同样可能跨边界只是位置在255附近的那一边更宽。你可能还会碰到另一种情况一张图里有亮红和暗红两种红。比如工件表面有一半被阴影遮挡。亮红的S和V都高暗红的V低、S也可能略低。这时候单纯加大V的阈值下限会把暗红区域切开不加大又可能把背景里的暗色噪声收进来。我的做法是优先保目标完整性V的阈值下限宁可放宽让更多背景进来然后靠面积筛选过滤。因为颜色分割的后续还有一步select_shape兜底目标是最大面积的那个连通域这个策略在实际中非常有效。4. 实测中那些不写在文档里的坑4.1 光照一变阈值全部作废这是我做颜色匹配项目以来体会最深的一条HSV里的H通道相对稳定但绝对不是恒定不变。我在一个项目里做过实测同一块红色面板上午九点和下午三点的自然光条件下H通道均值偏移了8到15个灰度级S和V的偏移则更夸张普遍在30个灰度级以上。为什么会这样因为相机的自动白平衡会随着环境光色温变化而调整整体的颜色偏移。如果你的相机开着自动白平衡H通道的漂移会更明显。所以我的第一条建议是工业现场的颜色匹配相机尽量用手动白平衡、固定曝光时间、固定增益把这些变量全部钉死。光源选择色温稳定的LED光源并且考虑加遮光罩把环境光的干扰降到最低。如果现场条件实在不允许固定光照软件上也有补救空间。一种做法是动态阈值定期拍一张标准色卡统计已知色块在当前光照下H/S/V的均值然后按偏差量平移阈值区间。这种方案能做但维护成本高适合项目验收后没有太多后续干预的场合。我更推荐另一个思路从源头保证光源稳定把算法的压力降到最低。4.2 反光和镜面高光是颜色匹配的隐形杀手金属件、光滑塑料、覆膜表面这三个材质放在一起基本就是颜色匹配的噩梦。它们的共同问题是会产生镜面反射。镜面反射在图像里表现为高亮白斑这些白斑在HSV空间里V值极高、S值极低H值则完全随机。我在调试红色塑料盖项目时就遇到过红色盖子的正中央有一小块由于凸起产生的高光面积不大但它的S值掉到了20以下V值飙到了240以上。H通道阈值和S/V联合约束直接把它从目标区域里挖掉了一个洞。解决办法是闭运算closing_circle把洞补上。但如果高光区域很大或者高光出现在目标的边缘区域形态会被严重破坏单靠形态学就有点吃力了。更稳妥的做法是把高光区域的“特征”利用起来。高光区域的S值低、V值高这是一个非常稳定的判据。你可以用阈值专门把高光区域提取出来threshold (ImageS, RegionLowSat, 0, 30) threshold (ImageV, RegionHighVal, 220, 255) intersection (RegionLowSat, RegionHighVal, RegionGlare)把RegionGlare和之前得到的颜色区域做并集或者先用difference把高光从目标区域里挖掉再用形态学补洞效果都比单纯调大闭运算半径更好控制。4.3 颜色匹配和形状匹配怎么配合纯HSV分割有个明显的短板它对“颜色对但形状不对”的干扰无能为力。比如场景里同时出现了一个红色方形标签和一个红色圆形盖子HSV都能提出来但你要的是圆形盖子。这种情况下形状特征筛选是第一道关卡。最简单的筛选是基于select_shape的区域特征参数。常用的特征有area面积、circularity圆度、convexity凸度、outer_radius外接圆半径。以圆形目标为例圆度接近1正方形区域圆度约0.78矩形和细长条更低。设一个circularity下限0.85方形和条状干扰会被直接淘汰。如果项目对定位精度要求很高比如机器人抓取需要亚像素级的中心坐标我建议把颜色区域的结果当成ROI而不是最终输出。具体做法是先用HSV得到目标的大致区域再用reduce_domain把图像裁到该区域然后在该区域内用亚像素边缘提取算子找目标边缘重新拟合圆心。这个组合方案既能享受HSV的“颜色锁定”优势又能拿到形状匹配级别的定位精度是目前产线上最实用的思路之一。5. 从“能跑通”到“扛得住产线”光照鲁棒性与进阶思路5.1 先问自己目标颜色在HSV里的稳定度很多新手拿到HSV代码跑通一次就觉得自己完成了直到产线连续运行两小时才开始出问题。我个人的习惯是方案开发阶段就做一次色彩稳定性实验。连续拍摄同一种工件100张图每一张都统计目标区域在H/S/V三个通道的均值和标准差。用intensity算子可以直接从区域和通道图里算出这两个值intensity (SelectedRegions, ImageH, MeanH, DeviationH) intensity (SelectedRegions, ImageS, MeanS, DeviationS) intensity (SelectedRegions, ImageV, MeanV, DeviationV)这三个标准差就是你的“颜色稳定性评分”。如果H通道标准差小于10个灰度级S和V标准差在20到40之间这个项目的HSV方案大概率是稳的如果H通道标准差超过了20说明光源或白平衡已经严重影响色相了你得回头看硬件而不是继续调算法。这一步花不了十分钟但能帮你预判项目交付后会不会半夜被电话吵醒。5.2 几个提升稳定性的实用手段如果稳定性测试发现V通道整体漂移严重但H通道还好可以考虑在算法里加入亮度归一化。思路不复杂统计目标区域周边背景的V通道均值把它当作当前光照强度的指示值。光照变强背景V值上升光照变弱背景V值下降。做阈值时根据这个指示值动态调整S和V的上下限。另一个很实用的操作是ROI裁剪。全图像中颜色匹配的最大成本是阈值分割需要遍历每个像素。如果你的目标始终出现在传送带的固定区域先用reduce_domain把ROI限制在一个矩形里之后的threshold、connection、select_shape都只在这个局部区域运行。一拍的耗时能从几十毫秒降到几毫秒这个优化在产线节拍要求高的时候是刚需。5.3 替代色彩空间CIELab和颜色分类器HSV并不是颜色匹配的唯一答案。如果你发现HSV在某类材料上就是不稳定我的建议是不要恋战直接试CIELab。Halcon里trans_from_rgb的颜色空间参数支持cielabCIELab的a通道表示从绿色到品红的对立维度b通道表示从蓝色到黄色的对立维度这两个维度对光照变化的鲁棒性通常比HSV更好代价是计算量略大且阈值调试的直觉性不如HSV。还有一种场景目标颜色不是均匀色块而是带有纹理或渐变例如木纹、迷彩、带有渐变的喷漆面。阈值分割天然不擅长处理这类目标。此时可以考虑Halcon的机器学习颜色分类器。用create_class_mlp创建多层感知机分类器用add_sample_class_mlp把手工标定的多个颜色的H/S/V样本送入分类器训练完成后用classify_class_mlp逐像素或者逐区域分类。这本质上是“用数据学颜色”效果在复杂颜色场景里往往比固定阈值好得多缺点是需要标定样本和训练时间。5.4 工业部署的几个小细节最后补充几个实用部署细节。HDevelop里跑通的脚本导出到C#或C时记得检查Halcon运行时库的版本和授权。不同版本的Halcon之间有些算子的性能差异不小特别是rgb1_to_hsv这类逐像素算子在旧版本上跑起来明显偏慢。导出代码后如果发现逐像素颜色转换耗时偏高可以考虑只在ROI区域内执行转换或者用rgb1_to_hsv的多线程版本在HDevelop的算子窗口里有并行选项。另外正式运行时记得关闭所有dev_*显示相关的调用dev_update_off和不在循环里dev_display能让程序速度上一个台阶。我个人在实际项目中的体会是颜色匹配的项目十有八九的最终问题不出在算法上而出现在“我有没有把现场的光照、材质、相机参数这些不确定因素先摁住”。HSV只是给了你一个对光照更宽容的颜色表达方式它不等于免死金牌。把光源做成稳定的把目标颜色的H/S/V波动范围测清楚再动手调阈值这个项目就已经成功了八成。希望这篇东西能帮你少走一点我当年走过的弯路。

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

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

免费获取报价