把图像的灰度看作高度图,图像中每个像素点的灰度值看作该点的高度,高灰度值代表山脉,低灰度值代表盆地,每个局部最小值及其周围区域称为集水盆,而集水盆的边界则形成分水岭。
1.彩色图像转化成单通道灰度图
2.求梯度图
3.在梯度图的基础上进行分水岭算法,求取分区域的边缘线。
对应的是一个盆地的最小值,当我们在盆地里面滴一滴水的时候,由于重力作用,水最终会汇聚到该点。
该位置滴的水滴会汇聚到局部最小点。
在盆地的最小值点,打一个洞,然后往盆地里面注水,并阻止两个盆地的水汇集,我们会在两个盆地的水汇集的时刻,在交接的边缘线上(即分水岭),建造大坝,来阻止两个盆地的水汇集成一个区域。图像被分为了两个像素集,一个是注水盆地像素集,一个是分水岭线像素集。
在真实图像中,由于噪声点的影响或者其他干扰因素的存在,使用分水岭算法常常出现过度分割的现象,这是因为很多很小的局部极值点的存在,这样的分割效果是毫无作用的。
为了解决过度分割的问题,我们可以使用基于标记图像的分水岭算法,就是通过设定一个阈值指导分水岭算法,以便获得更好的图像分割效果。在这个区域的洪水淹没过程中,水平面都是从定义的高度开始的,这样可以避免一些很小的噪声极值区域的分割。
算子:
1.直接提取图像的盆地区域和分水岭区域:
watersheds (Image, Basins, Watersheds)
输入是图像,输出的第一个参数是盆地区域,第二的参数是分水大坝区域。
简单的案例,图片可以从这里下载
*获取图像
read_image (image, 'particle')
*对单通道图像进行高斯平滑处理,去除噪声
gauss_filter (image, ImageGauss, 11)
*将图像颜色进行反转
invert_image (ImageGauss, ImageInvert)
*对高斯平滑后的图像进行分数岭处理,提取出盆地区域和分水岭大坝区域
watersheds (ImageInvert, Basins, Watersheds)
dev_display (ImageInvert)
dev_display (Watersheds)
2.阈值化分割分水岭盆地区域。
watersheds_threshold (Image, Basins, 10)
注意,这里输出参数只有盆地区域,Basins即盆地区域,无分水岭区域,因此是从白色背景里面,提取黑色区域。若是黑色背景里提取白色目标,只需要图像image_invert即可。
关键是第三个参数如何理解,以及这个参数对最终的算法分割结果的影响。
查看Halcon的帮助文档可以看到,
max
{
W
−
B
1
,
W
−
B
2
}
<
Threshold.
\max \left\{W-B_{1}, W-B_{2}\right\}<\text { Threshold. }
max{W−B1,W−B2}< Threshold.
B
1
B_{1}
B1,
B
2
B_{2}
B2分别代表两个盆地的最小灰度值,
W
W
W代表的是两个盆地的交界处的灰度值,即交界处分水岭的灰度值。取两者差值的最大值,如果最大值小于threshold,即两块盆地之间的灰度值差值很小,代表两个盆地可以合并在一起。因此这个threshold值越小,分割出来的盆地数目越多,分割的越精细。数值越大,分割的盆地区域越少。
像素之间的距离有欧式距离,城市街区距离,棋盘距离等
欧式距离:就是高中课本定义的真实的两点之间的距离
城市街区距离:就是两个邻边长度之和
D
=
∣
x
−
s
∣
+
∣
y
−
t
∣
D = \left | x-s \right | +\left | y-t \right |
D=∣x−s∣+∣y−t∣
棋盘距离:就是取长或者宽差值的最大值。
D
=
m
a
x
(
∣
x
−
s
∣
,
∣
y
−
t
∣
)
D = max(\left | x-s \right | ,\left | y-t \right | )
D=max(∣x−s∣,∣y−t∣)
所以,对于任意两个像素点而言,一般棋盘距离<欧式距离<城市街区距离。
算子:
distance_transform (Regions, Image, 'octagonal', 'true', Width, Height)
输入是区域,这里的区域可以是多个不同的区域,输出是图像。 距离变换可以简化分水岭的复杂度。经格式变换后,region看起来变小了(腐蚀实现的是原图区域变小),同时图像背景全部为0,即背景变成黑色。
那么距离变换底层原理是如何实现的?
第一个参数,在图像处理中,区域本身代表的是图像的某一部分,其本身是不含灰度值的,很多时候,halcon的输入是图像和region一起的,这里只输入了region,没有输入图像,默认输入的是二值化图像的region,因此region部分的灰度值是1,代表目标区域,背景部分设定为0,代表的是背景区域。所以这里的输入是只含0和1的二值化图像。用矩阵表示就是:
第二个参数,输出是Image,这里的Image指的是另一副全新的,尺寸大小为Width*Height的含距离信息(不是灰度信息)的图像。此时这个图像的像素点的数值不是灰度值,而是区域中的点到背景之间的距离。用矩阵表示就是:
按住CTRL,鼠标显示图像信息是距离信息,而不是灰度值。
第三个参数,代表距离公式的选取:
'city-block’代表的是街区距离
'chessboard’代表的是棋盘距离
'octagonal’代表的是欧式距离
第四个参数,ture代表的是目标区域到背景的距离。false代表的是背景区域到目标区域边缘的距离,两个成像正好相反。
dev_close_window ()
dev_open_window (0, 0, 800, 800, 'black', WindowHandle)
read_image (Image, 'pellets.png')
get_image_size (Image, Width, Height)
**********第一步,先把目标区域提取出来
*均值去噪,排除干扰
mean_image (Image, ImageMean, 9, 9)
*全局阈值
threshold (ImageMean, Regions, 113, 202)
connection (Regions, ConnectedRegions)
**********第二步,做距离变换
*距离变化的目的是将图像二值化
distance_transform (ConnectedRegions, DistanceImage, 'octagonal', 'true', Width, Height)
*因为分水岭用的二值化图像是从白色背景里提取黑色目标区域,因此需要做invert_image
invert_image (DistanceImage, ImageInvert)
**********第三步,分水岭阈值分割
*距离变换后图像数据更改,在halcon中更换图像数据类型,可以使用convert_image_type
*然而convert_image_type在改变图像类型的情况时,还会把像素值根据图像类型做像素值变换
*变换,因此显示的图像会改变,所以也可以使用scale_image_max
scale_image_max (ImageInvert, ImageScaleMax)
watersheds_threshold (ImageScaleMax, Basins, 50)
**********分水岭的盆地区域与原区域做交运算,相当于拆分原区域
intersection (Basins, ConnectedRegions, RegionIntersection)
*区域显示
dev_display (Image)
dev_display (RegionIntersection)
stop()
*边缘显示
gen_contour_region_xld (RegionIntersection, Contours, 'border')
dev_display (Image)
dev_display (Contours)
stop()