文章目录
- 一、引言
- 二、检测圆形
- 三、总结
一、引言
前面的文(用户指南/快速向导)差不多已经把HALCON的基本内容讲完了,并且在学习过程中还跑过一个简单示例——在单一背景下定位回形针。示例跑过,顿时觉得自己行了,但如果此时(假设你和我一样没有其他图像处理经验)你想上手解决一个实际问题,很可能寸步难行。
就拿我来说,随手找了张图片,想定位图片中的目标图形——硬币。
于是,按照回形针示例中的步骤,借用灰度图像来阈值化,得到的图片是这样的,
先不谈背景中的大红块,连颜色相近的文字也被保留了下来,这不是我希望保留的。不过也很正常,因为用到的图像处理是基于形态学的,简单讲就是颜色、形状、面积的匹配,是比较傻瓜式的,实际上计算机并不认识硬币。
那如何去掉这些背景中不希望保留的元素呢,经过我一番查阅,知道了两种常用操作——腐蚀(erosion)和膨胀(dilation)。因为本文不是主要介绍它们的实现,所以只简单谈一下经过它们处理的效果。
-
腐蚀(erosion)
-
自然界中的腐蚀,假设你有一块铁板,经过腐蚀后,可能会变得像下图一样锈迹斑斑且有许多破洞,这些破洞大的区域,可能原本就有损坏、比较脆弱;而一些保留相对完好的区域,原本可能是成块的、比较结实的部分(假设腐蚀对每块区域作用都一样)。
而图像处理中的腐蚀,作用于引言中的示例图后,就得到了下图。可以看到,其实效果类似铁板腐蚀,大块图形保留较好,而一些细小图形直接消失了(被腐蚀掉了);大图形上的一些小孔,经过腐蚀也变大了。
膨胀(dilation)
-
说到膨胀,自然就想到吹气球,气球充气后变大了(当然也可能爆掉)。图像处理中的膨胀也类似(但不会爆),它是与腐蚀相反的操作。把图像中的像素/区域想成一个气球,经过膨胀后,它就变大了。下面对示例图做膨胀,原来那些密密麻麻的字体明显变粗壮了,就像墨水在纸上洇开来一样。
Ok,关于腐蚀和膨胀到这里就差不多了(应用章节能理解其效果即可)。现在开始跑一个官方示例 Inspection of Ball Bonding ,在HDevelop示例程序中搜索 ball 关键字,可以找到该示例工程,选中 ball.hdev 打开。
二、检测圆形
这是示例原图,现在要找到并标注黑色圆形粘接口,接下来一行行学习它的程序(共五十多行)。
HALCON程序的头几行,
* ball.hdev: Inspection of Ball Bonding
*
dev_update_window ('off')
dev_close_window ()
dev_open_window (0, 0, 728, 512, 'black', WindowID)
*(星号)开头的是注释,剩下三行以 dev_ 前缀的与图形处理无直接关系,它们的作用分别是:
- 会关闭图像变量向图形窗口的自动输出(设置为’off’,还有一点好处是当你选中图像变量,图形窗口中只会显示选中的图像变量,方便观察,不然需要手动再选一次 显示/清除)
- 关闭活动的图形窗口
- 并打开一个728×512的黑色背景窗口。
接着,读取原图,
read_image (Bond, 'die/die_03')
虽然上面代码中关闭了图像变量自动显示在活动窗口,但在你单步调试程序时,这点会被无视掉。下面用 dev_display 显式输出读取的图像到图形窗口。
dev_display (Bond)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
disp_continue_message (WindowID, 'black', 'true')
stop ()
剩下两句看似是系统算子,但实际上是预定义好的外部函数。效果就是在图像下方显示文字,接着 stop() 暂停程序执行。
接下来是老朋友了,阈值化。
threshold (Bond, Bright, 100, 255)
阈值化后是一个新的算子 shape_trans ,它可以变换区域的形状。这边填的参数为 ‘rectangle2’ ,在输入区域中外接最小矩形。上面阈值化后的图像,顶部明显有一片区域是黑的,也就是没有像素的,这部分区域在后续处理过程中是不需要的,我们如何去掉它呢,就需要外接矩形了。
shape_trans (Bright, Die, 'rectangle2')
接下来几步是在外接矩形外围画一个框,并显示预定义好的文字提示信息,然后暂停一下。
dev_set_color ('green')
dev_set_line_width (3)
dev_set_draw ('margin')
dev_display (Die)
disp_continue_message (WindowID, 'black', 'true')
stop ()
你可能会问,有这么一个矩形有啥用呢?
别急,接下来的算子 reduce_domain 中会用到它,
reduce_domain (Bond, Die, DieGrey)
它会从原图中裁剪出矩形区域的图像,
这里的裁剪出图像,其实图像的尺寸未发生变化,你可以看到黑色背景还是在的,这相当于把矩形以外的区域去掉了,但保留背景/画布(HALCON中还有一个算子)。
上面这几步操作(获取目标矩形,用矩形裁剪图像)很常用,通常获取ROI区域就是这么做的。
接下来对裁剪得到区域再次进行阈值化,这次保留了灰度值低的区域(靠近0接近黑色,靠近255接近白色),另外图像顶部的黑色区域其实是没有像素的,所以并未选中
threshold (DieGrey, Wires, 0, 50)
下一步又是一个新的算子 fill_up_shape ,它的作用是填充区域中具有某些特征的空洞,下面参数表示的特征是面积,范围是1-100。也就是将该范围面积的空洞填满,
fill_up_shape (Wires, WiresFilled, 'area', 1, 100)
来看下放大后的效果吧,处理前,
处理后,
还是很明显的,区域内的小圆孔被填充满了。你可能会问,不是填充满吗,为什么消失了?因为图案是接近黑色的,填充后看起来就是消失了。
接下来又是一些额外操作,显示原图底片,用红色填充满处理后区域,
dev_display (Bond)
dev_set_draw ('fill')
dev_set_color ('red')
dev_display (WiresFilled)
disp_continue_message (WindowID, 'black', 'true')
stop ()
下一步是一个很有用的操作——开操作(halcon中名称为opening的都是开操作相关的),它的原理其实是腐蚀和膨胀的结合,即先腐蚀后膨胀,
opening_circle (WiresFilled, Balls, 15.5)
处理后的效果,是不是非常离谱,但理解了文章开头说的效果后,其实不难想象。
接下来,切换了输出颜色,重新输出处理后的区域,
dev_set_color ('green')
dev_display (Balls)
disp_continue_message (WindowID, 'black', 'true')
stop ()
接下来三个算子, 先是connection 寻找区域内的连通部分;
第二个是新算子 select_shape ,找到具有目标特征的形状,这边填写的参数是 ‘circularity’ ,就是类圆的图形;
第三个算子就是字面意思,对图形按位置排序(这边参数表示按列值升序排)。
connection (Balls, SingleBalls)
select_shape (SingleBalls, IntermediateBalls, 'circularity', 'and', 0.85, 1.0)
sort_region (IntermediateBalls, FinalBalls, 'first_point', 'true', 'column')
然后,又是换色显示原图对比,并输入提示文本,
dev_display (Bond)
dev_set_colored (12)
dev_display (FinalBalls)
disp_continue_message (WindowID, 'black', 'true')
stop ()
最后,
smallest_circle 确定这些圆形区域的最小外接圆,并将输出的坐标和半径做处理后输出到图形窗口。
smallest_circle (FinalBalls, Row, Column, Radius)
NumBalls := |Radius|
Diameter := 2 * Radius
meanDiameter := mean(Diameter)
minDiameter := min(Diameter)
dev_display (Bond)
disp_circle (WindowID, Row, Column, Radius)
dev_set_color ('white')
disp_message (WindowID, 'D: ' + Diameter$'.4', 'image', Row - 2 * Radius, Column, 'white', 'false')
dev_update_window ('on')
至此,示例程序处理完毕。
之后,我用了示例方法处理文章开头的示例图,得到如下效果
三、总结
整个过程其实并不复杂,毕竟我们只是调用算子,不需要关注算法细节。
但这里还是得稍微总结一下所涉及的常用操作,
- 阈值化,拿到图之后通常会先阈值化一下,方便后续处理。
- 选取ROI,通常获取一个形状,然后用该形状去裁剪原图,可以减少后续图像处理量,并减少干扰区域。
- 腐蚀和膨胀,上面示例中的开操作(opening)就包含了腐蚀和膨胀,你也可以分开来使用,常见的还有闭操作(closing)。这些操作通常用来去掉图像背景中的干扰区域,填充空缺。
- 基于的特征的图形选择,这步是在寻找连通区域(connection)之后的。
上面提到的阈值化、选取ROI等操作都可以UI交互实现,比直接调参更方便。