Halcon学习--(8)图像分割

前言

图像分割是计算机视觉的核心任务之一,也是Halcon工业检测中最基础的环节。本文系统梳理Halcon中四类主流分割方法------阈值分割、边缘检测、区域生长、分水岭------附带完整可运行代码和参数说明,适合从入门到工程落地。


一、图像分割概述

图像分割(Image Segmentation)的目的是将一幅图像分成若干个有意义的区域,以便后续进行特征提取和目标识别。在工业视觉检测中,图像分割是几乎所有检测流程的基础步骤。

图像分割的本质是:将图像中的像素按照某种规则(灰度值、颜色、纹理、边缘等)划分为不同的区域,使得同一区域内的像素具有相似的属性,不同区域之间具有明显的差异。

Halcon 提供了极为丰富的图像分割算子,主要分为以下几大类:阈值分割边缘检测分割区域生长分割分水岭分割。实际工程中往往需要组合多种方法才能获得最佳效果。


二、阈值分割

2.1 全局阈值分割 --- threshold

全局阈值是最基础的图像分割方法:凡灰度值在 [MinGray, MaxGray] 范围内的像素都归为"前景"区域。

算子原型:

复制代码
threshold(Image : Region : MinGray, MaxGray : )

参数说明:

参数 说明
Image 输入图像
Region 输出:满足阈值条件的区域
MinGray 最小灰度值(含)
MaxGray 最大灰度值(含),通常设为 255

示例代码:

复制代码
* 全局阈值分割示例
read_image(Image, 'pellets')
* 提取灰度值在 128~255 之间的亮区域
threshold(Image, Region, 128, 255)
* 断开相互连接的联通域
connection(Region, ConnectedRegions)
* 面积筛选
select_shape(ConnectedRegions, SelectedRegions, 'area', 'and', 500, 99999)
count_obj(SelectedRegions, Number)
dev_display(Image)
dev_set_colored(12)
dev_display(SelectedRegions)

注意: 全局阈值适用于背景与前景灰度差异明显、光照均匀 的场景。若光照不均匀,建议改用动态阈值(dyn_threshold)。


2.2 自动阈值 --- binary_threshold

当不确定合适的灰度阈值时,可以让 Halcon 自动计算最优分割阈值,支持"暗区"和"亮区"两种模式。

复制代码
binary_threshold(Image : Region : Method, LightDark : UsedThreshold)
参数 说明
Method 算法:'max_separability'(最大可分性,即 Otsu 法)
LightDark 'light' 提取亮区;'dark' 提取暗区
UsedThreshold 输出:实际使用的阈值

示例代码:

复制代码
* 自动阈值分割示例(Otsu法)
dev_close_window ()
dev_open_window (0, 0, 512, 512, 'black', WindowHandle1)
read_image(Image, 'particle')
binary_threshold(Image, LightRegion, 'max_separability', 'light', UsedThreshold)
dev_display(Image)
dev_set_color('red')
dev_display(LightRegion)
* 打印实际使用的阈值
write_string(WindowHandle1, 'Threshold = ' + UsedThreshold)

2.3 动态阈值分割 --- dyn_threshold

动态阈值(局部阈值)先对图像进行均值滤波得到背景图,再将原图与背景图的差值与偏移量 Offset 比较,从而自适应地分割出局部亮/暗区域。适用于光照不均匀的场景。

复制代码
dyn_threshold(OrigImage, ThresholdImage : RegionDynThresh : Offset, LightDark : )
参数 说明
OrigImage 原始图像
ThresholdImage 背景参考图(通常为均值滤波结果)
Offset 偏移量,像素值差超过此值则被分割
LightDark 'light' / 'dark' / 'equal'

示例代码:

复制代码
* 动态阈值分割示例
read_image(Image, 'food/cocoa_packages_01')
* 对原图进行均值滤波,得到背景图
mean_image(Image, MeanImage, 30, 30)
* 动态阈值:原图中比背景图亮5个灰度单位以上的区域
dyn_threshold(Image, MeanImage, RegionDynThresh, 5, 'light')
connection(RegionDynThresh, ConnectedRegions)
select_shape(ConnectedRegions, SelectedDefects, 'area', 'and', 20, 5000)
count_obj(SelectedDefects, NumDefects)
dev_display(Image)
dev_set_color('red')
dev_set_draw('margin')
dev_display(SelectedDefects)

核心思路: dyn_threshold 判断 原图 - 参考图 > Offset 则为 light 区域。Offset 越小,越敏感;越大,越严格。


2.4 基于直方图的阈值分割

通过分析灰度直方图,找到谷底(波谷)作为分割阈值,适用于图像灰度分布呈明显双峰的场景。

复制代码
*获取图像
read_image (Image, 'D:/Program Files/MVTec/HALCON-22.05-Progress/examples/images/pill/ginseng/contamination/pill_ginseng_contamination_045.png')
*自动阈值分割
auto_threshold (Image, Regions, 5)
*显示分割区域
dev_display (Regions)

三、边缘检测分割

边缘是图像中灰度值发生突变的区域。通过检测边缘,可以找到目标的轮廓,从而实现分割。Halcon 提供了多种边缘检测算子,涵盖像素级与亚像素级精度。

3.1 Sobel 边缘检测 --- edges_image

Sobel 算子通过计算图像梯度来检测边缘,是最经典的边缘检测算子之一。

复制代码
edges_image(Image : ImaAmp, ImaDir : Filter, Alpha : )
参数 说明
ImaAmp 输出:边缘幅度图像(梯度强度)
ImaDir 输出:边缘方向图像
Filter 滤波器类型:'sobel''prewitt''kirsch''canny'
Alpha 平滑参数(仅 Canny 有效)
复制代码
* Sobel 边缘检测
read_image(Image,'fabrik')
edges_image(Image,Amp,Dir,'lanser2',0.5,'none',-1,-1)


* 对边缘幅度图像进行阈值处理,得到边缘区域
threshold(Amp, EdgeRegion, 20, 255)

3.2 Canny 边缘检测(推荐)

Canny 是综合效果最好的边缘检测算子,具有低误检率、高定位精度和单边缘响应等特点。

复制代码
* Canny 边缘检测(通过 edges_image 算子)
read_image(Image, 'fabrik')
edges_image(Image,Amp,Dir,'lanser2',0.5,'none',-1,-1)
hysteresis_threshold(Amp, EdgeRegion, 20, 40, 10)
dev_display(Image)
dev_set_color('red')
dev_display(EdgeRegion)

3.3 亚像素级边缘检测 --- edges_sub_pix

当需要高精度定位(亚像素级别)时,使用 edges_sub_pix,输出为 XLD 轮廓(不是区域),精度可达 0.1 像素以内。

复制代码
edges_sub_pix(Image : Edges : Filter, Alpha, Low, High : )
参数 说明
Edges 输出:XLD 亚像素轮廓
Filter 'canny''sobel''lanser2'
Alpha 高斯平滑程度,越大越平滑,推荐 0.5~2.0
Low, High 磁滞阈值(Low 为弱边缘阈值,High 为强边缘阈值)
复制代码
dev_close_window()
dev_open_window (0, 0, 512, 512, 'black', WindowHandle1)
* 亚像素边缘检测示例
read_image(Image, 'fabrik')
rgb1_to_gray(Image, GrayImage)
* canny 算子 + 亚像素精度
edges_sub_pix(GrayImage, Edges, 'canny', 1.0, 20, 40)
dev_display(GrayImage)
dev_set_colored(12)
dev_set_line_width(2)
dev_display(Edges)
* 显示轮廓数量
count_obj(Edges, NumEdges)
write_string(WindowHandle1, 'Edges: ' + NumEdges)

XLD(扩展线描述符) 是 Halcon 中表示亚像素精度轮廓的数据类型,区别于普通的区域(Region)。XLD 可用于测量、拟合直线/圆弧等精密测量场景。


四、区域生长分割

区域生长法从一个或多个"种子点"出发,根据像素间的相似性(灰度差、颜色差等)逐渐向外扩展,将相邻的相似像素合并到同一区域,直到没有满足条件的像素为止。

4.1 regiongrowing 算子

复制代码
regiongrowing(Image : Regions : Row, Column, Tolerance, MinSize : )
参数 说明
Row, Column 种子点之间的步长(像素),控制种子点密度
Tolerance 灰度容差,相邻像素灰度差小于此值则合并
MinSize 最小区域面积,小于此值的区域被丢弃
复制代码
* 区域生长分割示例
read_image(Image, 'mreut')
* 种子步长=3, 容差=8, 最小区域面积=100
regiongrowing(Image, Regions, 3, 3, 8, 100)
dev_display(Image)
dev_set_colored(12)
dev_display(Regions)
count_obj(Regions, NumRegions)
write_string(WindowHandle, 'Regions: ' + NumRegions)

4.2 regiongrowing_mean 算子

改进版区域生长,以区域内的平均灰度值 作为合并判据,比 regiongrowing 更稳健。

复制代码
read_image (Image, 'fabrik')
* 读取 fabrik 图像(Halcon自带示例图,工厂场景)

dev_close_window ()
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'white', WindowID)
* 关闭旧窗口,按图像尺寸打开新窗口,背景白色

* ====== 第一阶段:中值滤波 + 粗区域生长,目的是定位种子点 ======
median_image (Image, ImageMedian, 'circle', 2, 'mirrored')
* 中值滤波去噪,圆形掩模半径2,边界镜像处理
* 这一步很关键:区域生长对噪声敏感,先平滑才能避免过分割

regiongrowing (ImageMedian, Regions, 1, 1, 2, 5000)
* 粗区域生长:步长Row=1,Column=1(每个像素都是种子点)
* 容差Tolerance=2(灰度差≤2就合并),最小面积MinSize=5000
* → 得到大面积的粗分割区域

shape_trans (Regions, Centers, 'inner_center')
* 对每个粗区域取"内中心点"——即区域质心附近的一个点
* 把大面积区域压缩成一个个种子点

connection (Centers, SingleCenters)
* 断开连通域,确保每个种子点是独立的

area_center (SingleCenters, Area, Row, Column)
* 获取每个种子点的坐标(Row, Column) —— 这就是传给下一步的关键参数

* ====== 第二阶段:以精确种子点做均值区域生长 ======
regiongrowing_mean (ImageMedian, RegionsMean, Row, Column, 25, 100)
* ↑ 核心:Row/Column 是上一步算出的种子点坐标(不是步长!)
* Tolerance=25(容差更大,因为有了精确种子点,不怕误合并)
* MinSize=100(最小面积更小,保留细节区域)

dev_clear_window ()
dev_set_draw ('fill')
dev_set_colored (12)
dev_display (RegionsMean)
* 填充模式彩色显示结果

dev_set_color ('black')
dev_set_draw ('margin')
dev_display (RegionsMean)
* 再用黑色边框描边显示,方便看区域边界

五、分水岭分割

分水岭算法将灰度图像看作地形图,灰度值越高代表"山峰"越高。通过模拟"注水"过程,水从低处(灰度谷底)积聚,当来自不同谷底的水即将汇合时,在汇合处建立"分水岭",从而将图像分割成不同的"盆地"(连通区域)。

分水岭分割特别适合分离相互黏连的目标物体,例如相互挤靠在一起的圆形颗粒、细胞等。

5.1 watersheds 算子

复制代码
watersheds(Image : Basins, Watersheds : )
输出参数 说明
Basins 各个"盆地"区域(分割结果)
Watersheds "分水岭"本身(边界线区域)
复制代码
* 基础分水岭分割
read_image(Image, 'particle')
rgb1_to_gray(Image, GrayImage)
* 高斯平滑,避免过分割
gauss_image(GrayImage, GaussImage, 3)
* 执行分水岭分割
watersheds(GaussImage, Basins, Watersheds)
dev_display(GaussImage)
dev_set_colored(12)
dev_display(Basins)

5.2 watersheds_threshold 算子(带阈值,推荐)

原始 watersheds 对噪声极为敏感,容易产生"过分割"(分割出大量细小无意义区域)。watersheds_threshold 通过合并灰度差小于阈值 Threshold 的相邻盆地来解决此问题。

复制代码
watersheds_threshold(Image : Basins : Threshold : )

完整工业案例(配合距离变换分离黏连目标):

复制代码
* 带阈值的分水岭分割(完整工业案例)
read_image(Image, '素材/4-6 4-7.jpg')
rgb1_to_gray(Image, GrayImage)
​
* 高斯平滑去噪
gauss_image(GrayImage, SmoothedImage, 3)
​
* 距离变换(用于分离黏连目标)
* 先二值化
threshold(SmoothedImage, BrightRegion, 100, 255)
* 计算距离变换图像
distance_transform(BrightRegion, DistanceImage, 'octagonal', 'true', Width, Height)
convert_image_type(DistanceImage, ImageConverted, 'byte')
* 图像取反(距离变换后取反,使中心变为"山谷")
invert_image(ImageConverted, ImageInvert)
* 按比例增强对比度
scale_image_max(ImageInvert, ImageScaleMax)
​
* 带阈值的分水岭分割,合并高度差<30的相邻区域
watersheds_threshold(ImageScaleMax, Basins, 30)
​
* 筛选合理面积的区域
select_shape(Basins, SelectedBasins, 'area', 'and', 500, 50000)
​
* 显示结果
dev_display(GrayImage)
dev_set_colored(12)
dev_set_draw('margin')
dev_set_line_width(2)
dev_display(SelectedBasins)
​
count_obj(SelectedBasins, Number)
dev_set_color('green')
set_display_font(WindowHandle, 28, 'mono', 'true', 'false')
set_tposition(WindowHandle, 15, 15)
write_string(WindowHandle, 'Count = ' + Number)

六、Hough变换分割

Hough变换是一种用于检测图像中几何形状(直线、圆、椭圆等)的经典算法,常用于检测工业零件中的圆孔、直线边缘等规则形状。

6.1 直线检测 --- hough_lines

复制代码
* Hough 直线检测
read_image(Image, 'fabrik')
rgb1_to_gray(Image, GrayImage)
edges_sub_pix(GrayImage, Edges, 'canny', 1.0, 20, 40)
* 生成二值化边缘图
gen_region_contour_xld(Edges, EdgeRegion, 'filled')
* Hough 直线检测
hough_lines(EdgeRegion, HoughImage, Lines, AngleResolution, DistanceResolution, Threshold, AngleGap, DistGap)
dev_display(GrayImage)
dev_set_color('red')
dev_set_line_width(2)
dev_display(Lines)

6.2 圆检测 --- 最小外接圆 + 圆度筛选

复制代码
* 圆检测示例(形态学 + 圆度筛选)
read_image(Image, 'rings_and_nuts')
rgb1_to_gray(Image, GrayImage)
threshold(GrayImage, Region, 0, 128)
connection(Region, ConnectedRegions)
select_shape(ConnectedRegions, SelectedRegions, 'circularity', 'and', 0.8, 1.0)
* 计算圆的最小外接圆
smallest_circle(SelectedRegions, Row, Column, Radius)
dev_display(GrayImage)
dev_set_color('green')
disp_circle(WindowHandle, Row, Column, Radius)

七、分割方法对比总结

分割方法 适用场景 优点 缺点 常用算子
全局阈值 高对比度、光照均匀 简单、快速 不适合光照不均 threshold
自动阈值 双峰灰度分布 无需手动调参 仅适合双峰分布 binary_threshold
动态阈值 光照不均匀 自适应局部背景 参数选取有技巧 dyn_threshold
边缘检测 边界明显的目标 定位精度高(亚像素) 对噪声敏感 edges_sub_pix
区域生长 均匀灰度区域 区域完整、连通 对种子选择敏感 regiongrowing
分水岭 黏连目标分离 能分离相互接触目标 过分割,需配合阈值 watersheds_threshold
Hough变换 规则形状检测 抗噪声、不依赖连通性 计算量大,形状固定 hough_lines / smallest_circle

八、综合实战案例 --- 芯片焊点检测

结合多种分割方法,实现对 PCB 板上焊点的自动检测与计数:

复制代码
* 芯片焊点检测完整流程
dev_update_off()
dev_close_window()
read_image(Image, 'die/die_03')
get_image_size(Image, Width, Height)
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
dev_set_part(0, 0, Height-1, Width-1)
set_display_font(WindowHandle, 16, 'mono', 'true', 'false')
dev_display(Image)
stop()
​
* ====== 第1步:分割芯片主体区域 ======
* 二值化提取亮区
threshold(Image, Bright, 100, 255)
* 形状变换:用最小外接矩形定位芯片位置
shape_trans(Bright, Die, 'rectangle2')
dev_set_color('green')
dev_set_draw('margin')
dev_display(Die)
stop()
​
* ====== 第2步:在 ROI 内定位焊点 ======
* 限定 ROI 到芯片区域
reduce_domain(Image, Die, DieGrey)
* 二值化提取暗焊点
threshold(DieGrey, Wires, 0, 50)
* 填充小孔(面积1~100的孔洞)
fill_up_shape(Wires, WiresFilled, 'area', 1, 100)
dev_set_draw('fill')
dev_set_color('red')
dev_display(WiresFilled)
stop()
​
* ====== 第3步:形态学开运算分离焊点 ======
* 圆形结构元素开运算,半径15.5像素
opening_circle(WiresFilled, Balls, 15.5)
​
* ====== 第4步:连通区域分析 ======
connection(Balls, SingleBalls)
* 按圆度筛选(0.85~1.0为圆形)
select_shape(SingleBalls, IntermediateBalls, 'circularity', 'and', 0.85, 1.0)
* 按列方向排序
sort_region(IntermediateBalls, FinalBalls, 'first_point', 'true', 'column')
​
* ====== 第5步:计算并显示结果 ======
dev_display(Image)
dev_set_colored(12)
dev_display(FinalBalls)
​
smallest_circle(FinalBalls, Row, Column, Radius)
NumBalls := |Radius|
Diameter := 2 * Radius
meanDiameter := mean(Diameter)
​
dev_set_color('white')
disp_circle(WindowHandle, Row, Column, Radius)
disp_message(WindowHandle, 'D: ' + Diameter$'.3', 'image', Row - 2*Radius, Column, 'white', 'false')
​
set_tposition(WindowHandle, 15, Width / 2)
write_string(WindowHandle, 'Total Balls = ' + NumBalls + '  MeanDia = ' + meanDiameter$'.2f')
dev_update_window('on')

流程解析:

  1. 分割主体区域threshold + shape_trans 定位芯片位置

  2. ROI内定位焊点reduce_domain 限定区域 + threshold 提取暗焊点 + fill_up_shape 填充孔洞

  3. 形态学分离opening_circle 圆形开运算分离黏连焊点

  4. 连通分析connection + select_shape 按圆度筛选

  5. 结果展示smallest_circle 计算外接圆 + disp_message 显示直径与数量


九、常用算子速查

算子 功能 关键参数
threshold 全局阈值分割 MinGray, MaxGray
binary_threshold 自动阈值(Otsu) Method, LightDark
dyn_threshold 动态(局部)阈值 Offset, LightDark
gray_histo_abs 计算绝对灰度直方图 BinSize
histo_to_thresh 直方图自动求阈值 -
edges_image 边缘检测(像素级) Filter, Alpha
edges_sub_pix 亚像素边缘检测 Filter, Alpha, Low, High
hysteresis_threshold 双阈值磁滞滤波 Low, High, MaxLength
regiongrowing 区域生长 Row, Column, Tolerance, MinSize
regiongrowing_mean 均值区域生长 Tolerance, MinSize
watersheds 分水岭分割 -
watersheds_threshold 带阈值分水岭(推荐) Threshold
distance_transform 距离变换(配合分水岭) Metric
invert_image 图像取反 -
scale_image_max 图像灰度拉伸 -
fill_up_shape 按形状特征填充区域 Feature, Min, Max
select_shape 按形状特征筛选区域 Feature, Operation, Min, Max
connection 断开联通区域 -
opening_circle 圆形结构开运算 Radius
smallest_circle 计算最小外接圆 -