Halcon实例分析—— ball_seq.hdev检测环形边界

有些重复性的代码就一笔带过,主要是之前的篇幅已经说过了,有疑问的可以看看之前的文章。主要重点讲解之前没有见过的代码和思路以及带给我们的思考,例程为什么这么做,我么使用其他的方式可不可以?我们带着很多问题去看例程,HALCON的所以例程都是给我们提供解决问题的思路,我们在实际解决问题的时候可能会使用很多个例程提供的思路共同来解决一个问题

例程运行结果如下。我们拿到例程第一件事就是F5运行先看一下,从视觉上了解这个例程是干嘛的,从下图基本可以看出程序的主要作用是提取圆形边界的。

 例程位置位于方法中blob分析ball_seq.hdev

完整代码如下

* ball_seq.hdev: Inspection of Ball Bonding
* 
dev_update_off ()
ImageNames := 'die/' + ['die_02','die_03','die_04','die_07']
dev_set_colored (12)
read_image (Bond, ImageNames[0])
get_image_size (Bond, Width, Height)
dev_close_window ()
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
dev_set_draw ('margin')
dev_set_line_width (3)
NumImages := |ImageNames|
for I := 0 to NumImages - 1 by 1
    read_image (Bond, ImageNames[I])
    dev_display (Bond)
    min_max_gray (Bond, Bond, 0, Min, Max, Range)
    threshold (Bond, Bright, Max - 80, 255)
    shape_trans (Bright, Die, 'rectangle2')
    dev_display (Die)
    reduce_domain (Bond, Die, DieGrey)
    min_max_gray (Die, Bond, 0, Min, Max, Range)
    threshold (DieGrey, Wires, 0, Min + 30)
    fill_up_shape (Wires, WiresFilled, 'area', 1, 100)
    opening_circle (WiresFilled, Balls, 9.5)
    connection (Balls, SingleBalls)
    select_shape_std (SingleBalls, Rect, 'rectangle1', 90)
    difference (SingleBalls, Rect, IntermediateBalls)
    gen_empty_region (Forbidden)
    expand_gray (IntermediateBalls, Bond, Forbidden, RegionExpand, 4, 'image', 6)
    opening_circle (RegionExpand, RoundBalls, 15.5)
    sort_region (RoundBalls, FinalBalls, 'first_point', 'true', 'column')
    smallest_circle (FinalBalls, Row, Column, Radius)
    NumBalls := |Radius|
    Diameter := 2 * Radius
    meanDiameter := sum(Diameter) / NumBalls
    mimDiameter := min(Diameter)
    dev_display (RoundBalls)
    if (I != NumImages)
        disp_continue_message (WindowHandle, 'black', 'true')
    endif
    stop ()
endfor

分析代码

dev_update_off ()

每一篇文章我都会提到一点,就是不是所以的HALCON的算子都能导入到C#中直接使用的,这些算子是设置HDevelop这个环境的;

这里是关闭窗口自动更新的作用,主要是为了在连续运行算子的时候减少刷新,加快运行效率,当然导入到C#中就不需要了。

ImageNames := 'die/' + ['die_02','die_03','die_04','die_07']

设置图片地址变量

dev_set_colored (12)

下面这些来之官方文档,我做了翻译

Name
dev_set_colored — Set multiple output colors.

设置多个颜色输出

Signature
dev_set_colored( : : NumColors : )

Description
dev_set_colored allows to display tuples of regions, XLDs, and other geometrical objects in the graphics windows in different colors using a set of NumColors predefined colors. Valid values for NumColors can be queried with the operator query_colored.

这个算子可以设置区域,XLD数组等在图像窗口上显示多种颜色

Attention
Using the code export feature of HDevelop, the code that is generated for this operator may have a different behavior than the related HALCON operator. For a detailed description of the code export of HDevelop graphics operators into the different programming languages see in the “HDevelop User's Guide” the chapter Code Export -> General Aspects of Code Generation -> Graphics Windows.

这里需要注意的是,如果是把这个算子导出到其他语言,可能在运行的时候会有问题,所以我们如果需要导出,就需要查询用户手册了解详细情况

Parameters
NumColors (input_control)  integer → (integer)
Number of output colors.

Default value: 6

List of values: 3, 6, 12

 值的范围不是想设置多少就设置多少的,上面列出了我们可以设定的范围;

 read_image (Bond, ImageNames[0])
get_image_size (Bond, Width, Height)

读取图片和获取图片大小

dev_close_window ()
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)

关闭当前窗口,创建一个和图像尺寸一样大的窗口,

关闭重新打开的目的是为了使窗口使用图片

set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
 

第一行是设置字体显示的大小,

set_display_font( : : WindowHandle, Size, Font, Bold, Slant : )

第一个参数是窗口句柄

第二个参数是字体大小

Size (input_control)  integer → (integer)
The font size on Windows systems. If Size=-1, the default of 16 is used. On Linux systems, ALL values are multiplied with 1.25 and then mapped to [11, 14, 17, 20, 25, 34].

Default Value: 16

建议值: [9, 11, 14, 16, 20, 27]

第三个参数是设置字体样式,一般我们选择mono

Font (input_control)  attribute.value → (string)
The font name. Either 'mono', 'sans', 'serif' or a specific font name can be used. 'mono' will be mapped to 'Courier New' on Windows, to 'courier' on Linux, and to 'Menlo' on OS X. 'sans' will be mapped to 'Arial' on Windows and OS X, and to 'helvetica' on Linux. 'serif' will be mapped to 'Times New Roman' on Windows and OS X, and to 'times' on Linux.

Default Value: 'mono'

建议值: 'mono', 'sans', 'serif'

第四个参数是是否加粗

Bold (input_control)  attribute.value → (string)
If set to 'true', a bold font is used

Default Value: 'true'

值的列表: 'true', 'false'

第五个参数是是否为斜体

Slant (input_control)  attribute.value → (string)
If set to 'true', a slanted font is used

Default Value: 'false'

值的列表: 'true', 'false'

dev_set_draw ('margin')
dev_set_line_width (3)

第一行是设置填充方式,margin是不填充内部,fill是填充内部

第二行是线条宽度

上面这三个这只导入到C#中都是非常好用的。

上面这些都是先把环境,参数设定好,后面只需要处理即可;

我们在导入到C#中其实也是这个思路,检测部分是一个模块,设置部分是一个模块,设置好参数等后,保存到电脑本地,等检测的时候需要调用,只要读取即可。

下面使用了一个for循环,循环读取图片做处理

for I := 0 to NumImages - 1 by 1
    read_image (Bond, ImageNames[I])
    dev_display (Bond)
    min_max_gray (Bond, Bond, 0, Min, Max, Range)
    threshold (Bond, Bright, Max - 80, 255)
    shape_trans (Bright, Die, 'rectangle2')
    dev_display (Die)
    reduce_domain (Bond, Die, DieGrey)
    min_max_gray (Die, Bond, 0, Min, Max, Range)
    threshold (DieGrey, Wires, 0, Min + 30)
    fill_up_shape (Wires, WiresFilled, 'area', 1, 100)
    opening_circle (WiresFilled, Balls, 9.5)
    connection (Balls, SingleBalls)
    select_shape_std (SingleBalls, Rect, 'rectangle1', 90)
    difference (SingleBalls, Rect, IntermediateBalls)
    gen_empty_region (Forbidden)
    expand_gray (IntermediateBalls, Bond, Forbidden, RegionExpand, 4, 'image', 6)
    opening_circle (RegionExpand, RoundBalls, 15.5)
    sort_region (RoundBalls, FinalBalls, 'first_point', 'true', 'column')
    smallest_circle (FinalBalls, Row, Column, Radius)
    NumBalls := |Radius|
    Diameter := 2 * Radius
    meanDiameter := sum(Diameter) / NumBalls
    mimDiameter := min(Diameter)
    dev_display (RoundBalls)
    if (I != NumImages)
        disp_continue_message (WindowHandle, 'black', 'true')
    endif
    stop ()
endfor

 read_image (Bond, ImageNames[I])
    dev_display (Bond)

读取序列图片,显示图片

 min_max_gray (Bond, Bond, 0, Min, Max, Range)

min_max_gray — Determine the minimum and maximum gray values within regions.

确定一个区域内最大和最小的灰度值,range最大灰度值和最小灰度值之间的差值

在C#Halcon处理框架中,我们检测的顺序中有这样一步

相机拍照,注册一个良好的图片,所谓良好的图片,我们需要调节光亮,相机曝光,然后鼠标在图片上移动观察灰度值的范围,是不是我们想要的范围;

这个例子中的第一步是为了线切出整个产品需要的部分,可以使用rectangle切出,我们观察产品亮色的区域正好四边沿都有,那我们使用阈值提取把亮色的提取出后,转换成矩形,就能把需要的部分整个切出来。但是我们不知道亮色的部分是多大,所以这里使用这个算子来计算,我们就不需要一个个的灰度值去测试了,比较方便。当然不是说一定需要这个算子,这里也是一样,为我们提供了这样一个算子,后面在一些检测缺陷中,如果有灰度值不确定的地方,我们可以使用这个算子去自动计算

 threshold (Bond, Bright, Max - 80, 255)

通过最大最小计算得出最大灰度值后,使用阈值就很好提取范围灰度值了;

 

  shape_trans (Bright, Die, 'rectangle2')

 阈值提取的是不规则的图形,我们可以使用转换算子转换成长方形,这里rectangle1是标准长方形,rectangle2是带角度的,

 dev_display (Die)
    reduce_domain (Bond, Die, DieGrey)

显示切得的图形,这里处理完后如果想看到结果都需要执行显示算子,因为例子的开头我们关闭了自动更新

 min_max_gray (Die, Bond, 0, Min, Max, Range)
    threshold (DieGrey, Wires, 0, Min + 30)

 同之前的一样,在上图我需要提取黑色的部分,使用最大最小灰度值确定黑色区域的灰度值

   fill_up_shape (Wires, WiresFilled, 'area', 1, 100)

 填充面积在1到100范围内图形的孔洞,这里是以防万一的作用,提高整个算法的稳定性,针对于这一张图片,我们可能觉的这行算子好像没有什么用,因为黑色的部分都提取到了,但是如果有大量图片,我们的阈值算子里的灰度值不是很好,那么,选择的区域就会有孔洞,我们就需要这行算子来填充

 opening_circle (WiresFilled, Balls, 9.5)

 使用开运算来去除杂点和其他图形,保留类似园的结构

opening_circle — Open a region with a circular structuring element.

使用圆形结构对区域进行开运算

我们来看测试几张图片,看一下,腐蚀,膨胀,开运算,闭运算的区别,这里我们发现有不太懂的算子的时候,第一件事去是去看官方文档的描述,看完后,最好再使用图片测试一下

原图如下,任意提取了一张这个例子中的一个脚

 使用膨胀运算,圆形结构,半径是3.5

  dilation_circle (Region, RegionDilation, 3.5)

 由下图可以看出,确实在原来的基础上膨胀了几个像素

 

我们使用半径6.5

 

 

我们再使用长方形结构来测试一下

  dilation_rectangle1 (Region, RegionDilation, 3, 3)

 3X3的长方形没有膨胀很多,基本一个像素

 

我们使用9X9

 由上可以看出针对于有弧度的图形同样是膨胀,圆形结构可以使用较为小的参数实现较大的膨胀,而且还能保证周边还是圆弧;使用方形结构的话,虽然参数放大也能实现,但是周边会越来越接近于方形;

也就是使用什么结构去膨胀图形,需要看我们想提取的图形是弧形结构还是方形结构,那么对应的就使用什么结构去膨胀。

这里我们可以举一反三,那么如果是腐蚀的话,也应该是同样的道理,这里大家可以自己测试,包括开运算和闭运算,

我们再测试一个闭运算

closing_circle (Region, RegionClosing, 6.5)

运算前

运算后

 我们可以看出闭运算的作用是可以把多个分散的区域联合起来,而不像膨胀那样只是放大区域。所以我们可以推断出,开运算的话应该是把一些小的区域去除,留下大的区域。

具体大家可以自己测试一下。

connection (Balls, SingleBalls)

此算子的作用是把很多个区域连接成一个区域。

阈值提取的十多个区域,连成一个区域的目的是方便处理,不然我们需要一个一个的去处理,连接后直接批量处理

 

 select_shape_std (SingleBalls, Rect, 'rectangle1', 90)
    difference (SingleBalls, Rect, IntermediateBalls)

上图中有个方形也被提取出来,我们需要的是只有圆形,所以利用选择算子去把方形选择出来,然后差分运算,剪掉;当然这里你直接使用选择圆形区域我个人认为也是可以的,这里也是同样提供的是思路不是必须

 expand_gray (IntermediateBalls, Bond, Forbidden, RegionExpand, 4, 'image', 6)

 执行这个算子后,我们可以看到,在原来的基础上图形变大了。

 

 

 

 作用:填充区域间的间隙或者分割重叠区域(主要依据灰度值或者色差);

 

参数翻译(对应上看参数顺序):
输入区域、
输入图像、
输入不发生扩张的区域、
输出结果区域、
输入迭代次数(1 ≤ Iterations ≤ 500 (lin))、
输入模式( 'image', 'region')、
输入区域边界上的灰度值或颜色与待扩展候选区域之间的最大差异(1 ≤ Threshold ≤ 255 (lin));
 

描述:
expand_gray填补了在分割图像时,由于需要抑制小的区域导致的区域之间的间隙(mode为image时),或者是在分割重叠区域时(mode为region时);这两种方式都是区域膨胀的结果。该算子的工作原理是在区域中添加一个像素宽的条带,并且其灰度值和区域边界上相邻的像素的灰度值最大落差不能超过参数threshold中设置的阈值(如下图,上图为未执行expand_gray,下图为执行后的mode=image)。对于“循环”类型的图像(例如方向图像),输出区域还会添加灰度差值至少为255 - Threshold的点。

 膨胀只发生在被指定为不“禁止”的区域(参数ForbiddenArea)。

 

注意:
mode中的Image和region有以下的不同,
'image':输入区域在被膨胀迭代的过程中,直到它们接触到另一个区域或图像边界,或者由于过高的灰度值差异而停止迭代。由于expand_gray同时处理所有区域,区域间的间隙会均匀分布到所有灰度值相似的区域,在分割重叠区域时是将此区域均匀的分布到两个区域;
 

'region':不执行膨胀操作,只是通过将重叠区域(具有相似的灰度值)均匀的分配到各个区域,然后再分割重叠区域,所以输出区域是会有间隙的。


 expand_gray (IntermediateBalls, Bond, Forbidden, RegionExpand, 4, 'image', 6)
    opening_circle (RegionExpand, RoundBalls, 15.5)
    sort_region (RoundBalls, FinalBalls, 'first_point', 'true', 'column')
    smallest_circle (FinalBalls, Row, Column, Radius)

 这几段代码执行的目的我个人的理解是尽量选出的图形能够近似为圆,

 第一次选择是红色边界,基本类似椭圆,expand_gray后区域变大了一些。

然后再次使用开运算,得到内部更近似的圆

 这里不必太纠结,因为我们可以这样考虑,如果你希望提取这些焊点的圆形,怎样做才能更精准呢,如果我们只是使用这个例子的前半部分,提取的基本都是椭圆,圆形很难精确,只有多次进行开运算,才能达到准确提取圆中心坐标的目的。

所有还是那句话,例程都是提供思路,帮助我们了解算子的功能,不要太纠结合理性,把所有HALCON的例程看一遍,你能了解很多算子的运用和解决缺陷的思路基本就已经非常好了。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无锡伶俐科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值