
1. 项目概述焊点检测中的背景干扰难题在工业视觉检测领域焊点检测是一个经典且极具挑战性的课题。无论是PCB板上的微小焊盘还是汽车零部件上的大型焊缝其质量直接关系到产品的电气连通性与结构强度。然而实际产线环境远非理想焊点周围常常充斥着各种干扰可能是残留的助焊剂、氧化产生的不同颜色、金属基材的反光、甚至是相邻元器件的阴影。这些干扰背景与焊点本身的灰度、纹理特征往往交织在一起使得简单的“找亮点”或“找圆形”算法彻底失效。标题“从干扰背景中提取焊点”精准地戳中了这个痛点——核心任务不是识别一个孤立的、特征明显的对象而是要从一个复杂的、信噪比很低的图像场景中将目标“剥离”出来。这就像在一张沾满污渍和划痕的老照片里辨认出一个特定人物的轮廓。你不能只靠亮度因为污渍可能更亮也不能只靠形状因为划痕可能形成类似的线条。你需要一套组合策略去理解图像中不同区域的“本质”。HALCON作为一款强大的机器视觉软件库提供了从底层图像处理到高级形态学分析的一整套工具链正是解决此类问题的利器。本次分享我将结合一个典型的带干扰背景的焊点图像案例拆解从图像预处理、阈值分割到后处理优化的完整流程重点讲解如何利用灰度直方图分析、局部阈值以及形态学工具稳健地“揪出”那些隐藏在杂乱背景中的焊点。无论你是刚接触HALCON的新手还是希望优化现有检测方案的同仁相信这套思路都能带来直接的启发。2. 核心思路与方案选型为何是阈值分割与形态学面对干扰背景首要任务是确定技术路线。深度学习分割如UNet固然强大但对于许多实时性要求高、样本获取成本大的工业场景传统图像处理方案因其稳定、可控、无需大量标注数据的优势依然是首选。我们的核心思路可以概括为“分而治之”先抑制或分离背景干扰再增强焊点特征最后进行精确提取与筛选。2.1 为何首选阈值分割在众多图像分割方法中全局或局部阈值分割是我们应对焊点检测的第一把钥匙。原因有三计算效率极高阈值操作本质是像素级的比较速度远超区域生长、边缘检测等需要复杂迭代或卷积的方法能满足产线毫秒级的响应需求。物理意义明确焊点尤其是良好的锡点由于其材质和曲面结构在特定光源如环形同轴光、低角度光下通常会呈现局部高亮镜面反射或与背景有稳定对比度的区域。这种亮度差异是阈值分割能够生效的物理基础。与预处理组合性强阈值分割的效果严重依赖于输入图像的质量。我们可以通过一系列预处理滤波、增强来“塑造”图像的灰度直方图使得焊点与背景的灰度分布更容易被一个或一组阈值区分开。然而直接对原图使用threshold算子往往效果不佳。因为干扰背景可能包含与焊点亮度相似的区域。这时灰度直方图分析就成为我们决策的“眼睛”。通过histogram或gray_histo算子查看图像的灰度分布我们能直观判断是否存在双峰或多峰。理想情况下焊点峰会与背景峰分离。但现实中经常是重叠的单峰或平坦的直方图这提示我们需要进行图像增强或转换颜色空间/通道。2.2 辅助工具顶帽与底帽运算当背景光照不均或存在缓慢变化的纹理时全局阈值会失灵。此时顶帽运算和底帽运算这类形态学工具就闪亮登场了。它们基于开运算和闭运算专门用于提取比结构元素小的亮细节或暗细节。顶帽运算原图减去开运算结果。开运算能平滑物体轮廓、断开狭窄连接、消除细小突出物。因此顶帽运算能提取出图像中比背景亮的细小物体正好对应我们想要提取的明亮焊点。对于暗背景上的亮焊点这是一个非常有效的预处理步骤。底帽运算闭运算结果减去原图。闭运算能平滑轮廓、融合狭窄缺口、消除细小孔洞。因此底帽运算能提取出比背景暗的细小物体。如果焊点因氧化或特定光照显得比背景暗则可以使用底帽运算。通过顶帽/底帽处理我们可以将焊点从背景中“凸显”出来生成一幅背景更均匀、焊点更突出的新图像为后续阈值分割创造极佳的条件。2.3 方案选型总结基于以上分析我们制定的核心方案流程如下图像获取与初步观察获取原始图像目视评估干扰类型纹理、阴影、反光、污渍。预处理与特征增强根据干扰类型选择滤波如median_image去噪、色彩空间转换如提取Value通道或形态学处理顶帽/底帽来压制背景、增强焊点。阈值分割尝试对预处理后的图像结合灰度直方图尝试全局阈值、自适应阈值或动态阈值。区域后处理对分割出的区域进行形态学操作如closing_circle弥合小缺口、opening_circle去除小噪点、连通域分析connection和特征筛选select_shape基于面积、圆度等。结果输出与验证将最终区域叠加到原图显示并输出位置、数量等结果。这个方案的优势在于模块化每一步都可调、可解释。接下来我们进入实战环节看看每一步具体如何操作又会遇到哪些坑。3. 实战拆解从一张“脏”图像中提取焊点假设我们有一张电路板图像上面有多个焊点但背景存在明显的纹理干扰如纤维板纹理和明暗不均。我们的目标是完整地提取出所有焊点区域。3.1 第一步读图与灰度直方图诊断read_image (Image, ‘board_with_solder_points.jpg‘) get_image_size (Image, Width, Height) dev_display (Image) * 转换为灰度图如果原图是彩色 rgb1_to_gray (Image, GrayImage) * 计算并显示灰度直方图 gray_histo (GrayImage, GrayImage, AbsoluteHisto, RelativeHisto) dev_open_window (0, Width10, 512, 256, ‘black‘, WindowHandle1) dev_set_window (WindowHandle1) plot_tuple (AbsoluteHisto)执行后我们重点关注直方图形态。如果直方图是清晰的双峰那么恭喜问题很简单直接取谷底作为阈值即可。但更可能的情况是直方图呈单峰且宽泛焊点峰隐藏在背景峰的“肩膀”上。这说明直接全局分割行不通。注意gray_histo算子的第二个参数是“区域”这里我们用整个图像区域GrayImage表示计算全图的直方图。如果你想分析特定ROI可以先画一个区域。3.2 第二步预处理——使用顶帽运算压制背景纹理我们的图像背景有纹理焊点是亮的。顶帽运算是理想选择。关键是结构元素SE的选取。* 使用圆形结构元素其半径应大于背景纹理的宽度但小于焊点的半径。 * 这需要根据实际图像像素尺寸来估算。假设纹理宽度约5像素焊点直径约20像素。 gen_disc_se (SE, ‘byte‘, 11) * 创建一个半径为11像素的圆形结构元素 top_hat (GrayImage, SE, ImageTopHat) dev_display (ImageTopHat)查看ImageTopHat图像你会发现背景纹理被大幅削弱甚至变得均匀而焊点作为亮细节被保留并相对增强了。如果效果不理想可以调整结构元素的半径。半径太小去不掉纹理半径太大可能会腐蚀焊点边缘甚至小焊点。3.3 第三步阈值分割与二值化对顶帽处理后的图像进行阈值分割此时效果会好很多。* 再次查看顶帽后图像的直方图此时焊点峰可能更突出 gray_histo (ImageTopHat, ImageTopHat, AbsoluteHisto2, RelativeHisto2) * 假设通过直方图观察或经验设定一个阈值范围 threshold (ImageTopHat, Regions, 120, 255) dev_display (Regions)threshold得到了一个区域Regions。但此时区域可能包含一些残留的噪声点或者焊点区域内部有孔洞因反光不均导致。3.4 第四步区域后处理——形态学与特征筛选* 1. 连通域分析将阈值区域拆分成独立的连通区域 connection (Regions, ConnectedRegions) * 2. 先闭运算弥合区域内部的小孔洞和细小缺口 closing_circle (ConnectedRegions, RegionsClosed, 3.5) * 使用半径为3.5的圆形结构元素 * 3. 再开运算去除那些面积很小的噪声点 opening_circle (RegionsClosed, RegionsCleaned, 2.5) * 4. 特征筛选选择面积和圆度符合焊点特征的区域 select_shape (RegionsCleaned, SelectedRegions, [‘area‘, ‘circularity‘], ‘and‘, [150, 0.6], [800, 1.0]) * 假设焊点面积在150到800像素之间圆度大于0.61为完美圆形select_shape是极其强大的筛选工具。circularity圆度的计算公式是(周长^2) / (4*π*面积)越接近1越圆。对于近似圆形的焊点这是一个很好的筛选特征。3.5 第五步结果可视化与输出dev_display (Image) dev_set_color (‘green‘) dev_set_draw (‘margin‘) * 只显示区域轮廓 dev_display (SelectedRegions) * 获取最终焊点的数量、位置等信息 count_obj (SelectedRegions, Number) area_center (SelectedRegions, Area, Row, Column)至此我们成功从干扰背景中提取出了焊点区域。可以将Row,Column坐标输出给下游的检测逻辑如焊点直径测量、缺陷判断等。4. 关键参数调试与避坑指南上面的流程看似顺畅但每个环节都暗藏玄机。参数调不好结果可能天差地别。4.1 结构元素半径的抉择在顶帽运算中结构元素半径是最关键的参数。这里有一个实操心得不要凭感觉猜用inspect_shape_model或直接可视化来辅助决策。在HDevelop中使用dev_open_window打开两个窗口并排显示。左边显示原图右边显示top_hat结果。写一个循环让半径从5到20以步长1变化每次循环都更新右侧图像。肉眼观察找到背景纹理刚好被抹平而焊点细节损失最小的那个半径值。这个值就是最优解。4.2 阈值范围的确定不要只靠直方图threshold的上下限[MinGray, MaxGray]决定了哪些像素被选中。直方图是重要参考但并非唯一。动态阈值法当光照不均时可以考虑dyn_threshold算子。它需要一个预处理过的“参考图像”通常是用mean_image或gauss_filter对原图进行平滑得到的背景估计图然后将原图与参考图逐像素比较。这对于背景缓慢变化的场景非常有效。mean_image (GrayImage, ImageMean, 31, 31) * 使用31x31的均值滤波器估计背景 dyn_threshold (GrayImage, ImageMean, RegionsDyn, 15, ‘light‘) * 提取比背景亮15灰度值以上的区域二分迭代法如果焊点灰度范围相对稳定可以写一个简单循环从高阈值往低试探直到提取出的区域面积发生跃变意味着开始包含大量背景然后回退一个步长作为MinGray。4.3 特征筛选的陷阱select_shape用起来爽但参数设置不当会误杀或漏放。面积范围一定要先计算一下理想焊点的大致像素面积。用draw_region手动勾选一个典型的“好焊点”然后用area_center算它的面积以此作为基准进行上下浮动设定范围。圆度不是所有焊点都是完美的圆。特别是拉尖、焊锡不足的缺陷焊点其圆度会下降。如果你的目的是检出所有焊点包括疑似缺陷的那么圆度下限要设得宽松一些比如0.4。如果只找合格焊点可以设高如0.7。切记圆度对区域边缘的锯齿非常敏感。先进行适度的closing_circle平滑边缘能让你得到的圆度值更稳定、更有意义。组合特征除了面积和圆度还可以考虑紧密度compactness、矩形度rectangularity等。通过观察好/坏样本在这些特征空间中的分布来设定更精准的筛选条件。5. 进阶策略当简单方法失效时怎么办如果经过上述流程提取效果仍然不理想比如焊点和背景灰度完全重叠或干扰物与焊点形态极度相似我们就需要祭出更高级的策略。5.1 色彩信息的利用如果原始图像是彩色的灰度信息可能不足以区分目标。焊锡尤其是氧化后和背景纹理在颜色上可能有本质区别。转换色彩空间将RGB图转换到HSV或HSL空间。decompose3 (Image, R, G, B) * 如果读入的是三通道彩色图 trans_from_rgb (R, G, B, H, S, V, ‘hsv‘)分析通道通常S饱和度通道能很好地区分彩色区域和无色彩的灰度区域H色调通道对颜色种类敏感。可能背景纹理在S通道很弱而焊点即便发暗在H通道有特定值。对S或H通道进行阈值分割可能会得到比灰度图更好的二值图。5.2 基于边缘或梯度的分割当焊点与背景的边界清晰但内部灰度不均时可以尝试从边缘入手。edges_image算子可以提取边缘。threshold_sub_pix可以对边缘进行亚像素阈值分割得到轮廓。然后使用gen_region_contour_xld将轮廓转换为区域再用fill_up或union1填充内部。 这种方法对高反光导致焊点内部过曝一片白的情况特别有效因为过曝区域的边缘通常是完整的。5.3 模板匹配与差分法如果焊点的位置大致固定例如在PCB的特定焊盘上可以采用更“暴力”但稳健的方法获取一张“标准”无焊点或都是好焊点的图像作为模板。对待测图像进行仿射变换与模板进行像素级对齐使用vector_field_to_hom_mat2d或基于特征点的匹配。计算待测图与模板图的绝对差分图abs_diff_image。对差分图进行阈值分割显著变化的区域就可能是焊点或缺陷。 这种方法能极大消除固定背景干扰但对位置变动和光照变化比较敏感需要精确的配准。6. 常见问题排查与实战技巧实录在实际项目中你一定会遇到各种意想不到的情况。下面是我踩过的一些坑和总结的技巧。6.1 问题分割出的区域总是粘连在一起焊点连成一片原因阈值过低或者预处理如顶帽的结构元素太小未能有效分离相邻焊点之间的背景。解决尝试提高threshold的MinGray值让分割更“苛刻”。增大顶帽运算的结构元素半径但要注意不要伤及焊点本身。分割后使用watersheds算子进行分水岭分割可以将粘连区域根据地形可理解为梯度进行分离。* 假设Regions是粘连的区域 watersheds_threshold (Regions, Basins, 10) * 10是阈值用于控制分水岭的深度如果焊点本身是圆形可以尝试distance_transform结合h_max寻找距离变换图中的极值点作为种子点再进行区域生长。6.2 问题小焊点总是被当成噪声过滤掉原因opening_circle的半径或select_shape的面积下限设置过大。解决在开运算去噪前先使用select_shape将面积明显过小的噪声区域剔除比如面积50像素保留所有可能的小焊点。对剩余区域进行特征分析。小焊点可能圆度也不错。可以设置一个专门针对小面积但高圆度的筛选条件。考虑是否光照不足导致小焊点对比度低尝试在预处理阶段使用emphasize或scale_image增强局部对比度。6.3 问题算法在不同批次产品上表现不稳定原因可能是光源衰减、相机参数微调、或产品本身基底颜色有批次差异。解决动态参数不要将阈值等参数写死。可以每帧或每N帧自动计算一次。例如使用gray_histo计算直方图后用histo_2dim和auto_threshold算子尝试自动寻找阈值。归一化处理在预处理环节加入图像归一化消除整体亮度波动的影响。mean_deviation (GrayImage, Mean, Deviation) scale_image (GrayImage, ImageScaled, 1.0/Deviation, -Mean/Deviation) * 归一化为零均值、单位方差建立黄金样本库保存不同批次、不同状态下的“合格”样本图像。在新批次上线前用这些样本快速测试并微调参数。6.4 一个关键的调试技巧可视化中间结果在HDevelop中调试时务必养成将关键中间图像和区域显示出来的习惯。我个人的习惯工作流是开4个窗口原图、预处理后图、阈值二值图、最终结果图。使用dev_set_color为不同步骤的结果设置不同颜色如预处理用蓝色阈值区域用红色最终区域用绿色。使用dev_update控制窗口更新在循环调试参数时设置为‘off’可以避免闪烁观察单次效果时再设为‘on’。善用disp_message在图像上直接打印关键参数值如当前使用的阈值、检测到的数量一目了然。从干扰背景中提取焊点是一个典型的“信号与噪声”博弈问题。没有放之四海而皆准的银弹参数核心在于深刻理解你的图像数据焊点和干扰物在物理上是如何成像的并熟练运用HALCON工具箱里的各种“手术刀”预处理、形态学、特征分析进行有针对性的处理。每一次成功的提取都是对图像理解的一次深化。当你面对一张新的、更棘手的图像时不妨回到起点再看一眼直方图再想想顶帽和底帽的物理意义再审视一下你筛选特征的逻辑。这个过程本身就是机器视觉工程师的核心价值所在。