Halcon深度图转灰度图实战:电容测高项目中的图像处理技巧
最近在做一个电容高度测量的项目,客户要求对电容两端进行精确测高,并计算两端的高度差,用于后续的质量管控。这个需求听起来简单,但在实际工业视觉项目中,却遇到了几个典型的“坑”。最核心的一个问题就是:拿到的原始图像是32位的深度图,直接用Halcon打开,屏幕上几乎是一片漆黑,根本看不到任何有用的高度信息。这就像拿到了一张曝光严重不足的照片,细节全被“淹没”在黑暗里。另一个挑战是,如何在转换后的灰度图上,稳定、自动地识别出电容本体,并在其两端精准地生成用于测量的ROI区域,而不是每次都靠手动框选。今天,我就结合这个具体的电容测高案例,把深度图处理、目标自动定位和ROI生成的完整思路和代码实现,掰开揉碎了讲清楚。
1. 深度图“显影”:从一片漆黑到信息分明
在工业3D视觉中,深度图(Depth Map)记录了每个像素点到相机的距离或高度信息。通常,这些数据以32位浮点数(real)格式存储,其数值范围可能非常小,也可能跨度很大。当我们将这样一张图直接显示在只支持8位(0-255)灰度显示的屏幕上时,由于数值动态范围与显示范围不匹配,就会出现“一片黑”或“一片白”的情况,关键的高度变化信息完全无法被肉眼识别。
解决这个问题的核心思想是对比度拉伸,也就是将原始深度数据中我们感兴趣的那部分数值范围,线性映射到0-255这个显示区间。在Halcon中,我们主要有两种武器:scale_image 和 scale_image_range。别看它们名字像,用起来区别可不小。
1.1 线性变换:scale_image的灵活与风险
scale_image 算子执行的是最基础的线性变换:g' = g * Mult + Add。其中 g 是原图像素值,g' 是变换后的像素值,Mult 是乘数,Add 是加数。
* 假设Image是32位的深度图
scale_image (Image, ImageScaled, 20, -100)
convert_image_type (ImageScaled, ImageConverted, 'byte')
scale_image_max (ImageConverted, ImageScaleMax)
这段代码的逻辑是:
- 初步拉伸:
scale_image (Image, ImageScaled, 20, -100)。这里将原图乘以20,再减去100。如果原图高度值集中在很小的范围(比如5.0到5.2毫米),乘以20后范围变为100到104,再减100,就变成了0到4。这个步骤的目的是将微小的、有意义的差异“放大”。 - 转换类型:
convert_image_type将32位实数图像转换为8位字节图像。转换过程会进行截断,超出0-255范围的值会被置为0或255。所以上一步的变换范围需要大致控制在这个区间内,否则信息会丢失。 - 最大化对比度:
scale_image_max将8位图像自身的灰度范围拉伸到全范围0-255,让图像看起来对比度最高。
这种方法的特点是“手动调节”。Mult和Add参数需要根据你对工件高度的先验知识进行估算和反复调试。调好了,效果立竿见影;调不好,要么拉伸不足依旧看不清,要么拉伸过度导致背景噪声被放大,甚至目标区域过曝(全白)。
注意:
scale_image后直接convert_image_type存在风险。如果ImageScaled中的值有大量超出0-255的范围,转换时会丢失这些信息。更稳妥的做法是先使用min_max_gray等算子查看变换后的灰度范围,确保核心数据在有效区间内。
1.2 范围映射:scale_image_range的精准控制
scale_image_range 算子的思路更直接:我只关心某个特定范围的高度值。它将输入图像中指定区间 [Min, Max] 的像素,线性映射到输出图像的0-255,而小于Min的值置为0,大于Max的值置为255。
* 确定感兴趣的高度范围,例如电容区域的高度大致在1000到1200微米之间
lowValue := 1000
highValue := 1200
scale_image_range (Image, ImageScaled, lowValue, highValue)
convert_image_type (ImageScaled, ImageConverted, 'byte')
这种方法在工业检测中往往更可靠,因为它基于对被测物体的物理认知。例如,我知道电路板基准面高度大约是800微米,电容本体高度在1000-1200微米之间。那么我就将 [1000, 1200] 这个范围映射到全灰度级。这样,电路板(低于1000)显示为黑色,电容主体显示为清晰的灰色梯度,而任何高于1200的异常凸起(可能是灰尘或焊锡球)会显示为白色,非常便于观察和后续处理。
两种方法对比与选择:
| 特性 | scale_image (结合后续处理) | scale_image_range |
|---|---|---|
| 核心原理 | 全局线性变换 (g' = g*Mult+Add) | 指定区间线性映射至[0,255] |
| 参数意义 | Mult(乘数), Add(加数) | Min(最小映射值), Max(最大映射值) |
| 优点 | 灵活,可通过参数微调对比度 | 目的明确,直接关联物理量,结果稳定 |
| 缺点 | 参数需反复调试,易受异常值影响 | 需要预先知道目标的大致高度范围 |
| 适用场景 | 对目标高度范围未知,需快速可视化探索时 | 已知目标大致高度范围的精密测量项目 |
在电容测高这个项目中,由于电容的高度规格是已知的,scale_image_range 显然是更优选择。它一步到位,将我们关心的电容高度信息清晰地呈现出来,为后续的自动识别打下了坚实基础。
2. 电容的自动识别:从灰度图中“抓”出目标
得到一幅对比度合适的灰度图后,下一步就是让程序自动找到电容的位置。在电路板上,电容、电阻、驱动器(IC)等元件可能外观相似,我们需要设计可靠的筛选逻辑。
2.1 阈值分割与连通域分析
这是最经典的一步。基于上一步的灰度图,电容区域应该比背景(电路板)更亮。
* ImageReduced 可能是经过ROI缩减或预处理后的图像
threshold (ImageReduced, Region, lowThreshold, highThreshold)
connection (Region, ConnectedRegions)
这里的关键在于 lowThreshold 和 highThreshold 的设定。由于我们已经用 scale_image_range 将电容高度映射到了较亮的灰度区间,阈值可以相对宽松地设定在一个较高的范围,比如 [150, 255],以初步提取所有可能的高点区域。
connection 算子将阈值分割得到的区域打散成一个个独立的连通域,这样我们就可以对每一个“候选区域”进行单独评判了。
2.2 基于形状特征的精准筛选
电路板上不止有电容,还有形状类似的驱动器。在灰度图上,它们可能因为高度相近而同时被阈值分割出来。这时就需要引入形状特征过滤。
电容通常是直立的长方体,而驱动器(芯片)则更接近正方形。我们可以利用 面积(area) 和 各向异性(anisometry) 这两个特征来区分它们。各向异性近似于区域长轴与短轴的长度比,对于长方形,这个值明显大于1;对于正方形,则接近1。
* 假设我们已经通过统计或先验知识确定了电容的典型面积和长宽比范围
MINArea := 500
MAXArea := 5000
MINRab := 1.5 // 最小长短轴比,过滤掉过小的或圆形的噪声
MAXRab := 4.0 // 最大长短轴比,过滤掉正方形的驱动器
select_shape (ConnectedRegions, SelectedRegions, ['area', 'anisometry'], 'and', [MINArea, MINRab], [MAXArea, MAXRab])
- 面积过滤:
[MINArea, MAXArea]可以有效地滤除面积过小的噪声点(如焊点毛刺)和面积过大的非目标区域(如整个板框)。 - 各向异性过滤:
[MINRab, MAXRab]是关键。将长短轴比小于1.5的近似正方形区域(驱动器)和比例异常的区域排除,保留下来的就极有可能是我们寻找的直立电容。
这个筛选过程是鲁棒性(稳定性)的关键。在实际项目中,我通常会先用几个样本图像,通过Halcon的图形界面工具(如特征检测)统计出目标电容的典型特征值范围,再将这个范围作为代码中的参数。这样做比拍脑袋设定参数要可靠得多。
3. ROI的智能生成:在电容两端“画”出测量框
找到电容区域后,我们需要在其两端生成两个小的矩形ROI(感兴趣区域)来进行高度测量。这里的要求是:ROI必须精确地定位在电容长轴方向的两端中心,并且矩形的方向要与电容的朝向一致。
3.1 计算电容的方向与中心
首先,我们需要获取电容区域的方向角。Halcon的 orientation_region 算子可以计算一个区域的主轴方向(长轴方向),返回值 Phi 是相对于图像水平轴(x轴)的夹角,范围在 -π/2 到 π/2 之间。
area_center (SelectedRegions, Area, RowCenter, ColCenter)
orientation_region (SelectedRegions, Phi)
RowCenter 和 ColCenter 是电容区域的中心点坐标。Phi 就是电容长轴与水平线的夹角。有了中心点和角度,我们就可以通过几何计算,找到长轴两端我们想要放置测量点的位置。
3.2 几何计算确定两端点坐标
假设我们希望在距离电容中心点 Distance 像素的位置进行测量。那么,两端点的坐标可以通过三角函数计算得出。为了避免直接处理角度和三角函数带来的方向混淆,一个清晰的计算过程如下:
* 输入:单个电容区域 ObjectSelected, 测量点距离中心的距离 Distance
* 输出:两端矩形区域 outputRegion1, outputRegion2
* 1. 获取区域中心和方向
area_center (ObjectSelected, Area, Row, Column)
orientation_region (ObjectSelected, Phi)
* 2. 将角度Phi转换为弧度,并计算正弦和余弦值(Halcon三角函数使用弧度制)
PhiRad := rad(Phi) // 角度转弧度
cosPhi := cos(PhiRad)
sinPhi := sin(PhiRad)
* 3. 计算两端点的中心坐标
* 端点1坐标:沿角度方向移动 Distance
Row1 := Row + Distance * sinPhi
Column1 := Column + Distance * cosPhi
* 端点2坐标:沿角度反方向移动 Distance
Row2 := Row - Distance * sinPhi
Column2 := Column - Distance * cosPhi
这里有一个非常重要的细节:在图像坐标系中,Row 是垂直方向(向下为正),Column 是水平方向(向右为正)。而三角函数的标准定义是基于水平轴逆时针旋转。因此,计算沿某个角度方向的偏移时,sinPhi 对应 Row 方向的增量,cosPhi 对应 Column 方向的增量。这个对应关系与常见的物理坐标系不同,是图像处理中需要特别注意的地方。
3.3 生成定向矩形ROI
知道了端点坐标和电容的方向角,我们就可以用 gen_rectangle2 来生成ROI了。这个算子生成的矩形是带方向的,非常适合贴合电容边缘。
* 定义测量矩形ROI的尺寸
Width := 10 // 矩形宽度(平行于短边方向)
Height := 5 // 矩形高度(平行于长边方向)
* 生成两端的矩形ROI,方向与电容方向一致
gen_rectangle2 (outputRegion1, Row1, Column1, PhiRad, Width, Height)
gen_rectangle2 (outputRegion2, Row2, Column2, PhiRad, Width, Height)
现在,outputRegion1 和 outputRegion2 就是精准定位在电容两端、方向与电容一致的测量窗口。你可以将它们叠加显示在图像上,直观地检查定位是否准确。
4. 高度测量与差值的稳定性优化
有了ROI,测量高度就相对直接了。我们可以使用 gray_features 或 intensity 算子来获取ROI内的平均灰度值,这个灰度值对应着我们之前通过 scale_image_range 映射的高度信息。
* 假设 ImageScaled 是经过 scale_image_range 处理后的32位图像
* 在 outputRegion1 和 outputRegion2 内计算平均灰度(即高度)
intensity (outputRegion1, ImageScaled, GrayValue1, Deviation1)
intensity (outputRegion2, ImageScaled, GrayValue2, Deviation2)
* 计算高度差
HeightDiff := abs(GrayValue1 - GrayValue2)
然而,在实际工业环境中,直接使用单次测量值可能受噪声影响。为了提升测量的稳定性和可靠性,可以考虑以下几个优化策略:
1. 多ROI测量取平均: 不在电容两端只放一个矩形,而是放置一排小矩形,分别计算平均高度后再求整体平均。这能有效抵抗局部缺陷或灰尘的影响。
* 伪代码思路:沿电容长轴方向,在两端各生成3个紧密排列的小矩形ROI
* 分别计算3个ROI的平均高度,再对3个值求平均,作为该端的高度值。
2. 高度映射的校准:
我们之前将 [lowValue, highValue] 映射到了 [0, 255]。为了得到真实的物理高度(微米),需要进行校准。最准确的方法是使用一个已知高度差的标准块。
- 步骤一:测量标准块在图像中的灰度差
GrayDiff_Std。 - 步骤二:已知标准块的物理高度差
PhysDiff_Std(例如 200微米)。 - 步骤三:计算比例因子
K = PhysDiff_Std / GrayDiff_Std。 - 步骤四:对于任何工件,其实测物理高度差
PhysDiff = GrayDiff * K。
3. 结果的滤波与判断: 在连续生产线上,可以对连续多个产品的高度差测量值进行移动平均或中值滤波,以平滑随机波动。同时,设置合理的公差带(Upper/Lower Limit),对超出公差的产品进行自动报警或标记。
* 简单的移动平均示例(在程序循环中实现)
* 维护一个数组或队列存储最近N次的高度差测量值
* 当前最终结果 = 最近N次测量值的平均值
* if (最终结果 > UpperLimit) or (最终结果 < LowerLimit)
* * 触发报警或NG处理
* endif
把这些技巧融入你的Halcon程序中,就能构建一个不仅“能用”,而且“稳定、可靠”的工业视觉检测方案。从一张漆黑的深度图,到自动输出精确的电容翘曲高度差,整个过程充满了对细节的考量。每个算子的选择、每个参数的设定、每一个几何计算的细节,都直接影响着最终系统的成败。
398



被折叠的 条评论
为什么被折叠?



