Halcon提供多种边缘提取算法。像素提取方法有常用的边缘提取算子或深度学习分割模型等。考虑到精度问题可能需要提取亚像素边缘。当然也可以提取轮廓:线、圆、椭圆等。本文只讨论提取轮廓。
1 基本概念
正常情况下,无需特殊操作即可提取边缘轮廓。
1.1 获取图像
获取图像。
1.2 提取轮廓或线
Halcon提供各种亚像素级轮廓提取算子。通常标准算子基于一阶导数。它将获取图像并返回XLD轮廓。当使用二阶导数时,必须使用拉普拉斯算子,然后才能够提取沿零点相交的高等线。不仅可用灰度值方法提取,还可以根据颜色提取边缘。
除了提取边缘,Halcon 还提供了提取线段的算子。与边缘相反,一条线由两个灰度值过渡组成。因此,可将一条线看做事平行的边。
1.3 一个简单的例子
下面程序中结束边缘提取的基本概念。提取边缘轮廓的算子edges_sub_pix,该算子将返回XLD轮廓。参数选择“lanser2”,使用中等大小的平滑蒙版。参数Low的地址确保即使低对比度的也可提取轮廓 。为显示多个轮廓,选择12-color颜色模式。
read_image(Image,'mreut4_3')
edges_sub_pix(Image,Edges,'lanser2',0.5,8,50)
dev_set_colored(12)
dev_clear_window()
dev_display(Edges)
2 扩展概念
2.1 图像校正
为实现高精度提取边缘或线条,相机应具有线性响应函数,即图像中的灰度值与入职能量呈线性关系。考虑到有些相机没有线性响应功能,Halcon提供所谓的辐射校准,通过操作符radiometric_self_calibration,可以确定相机逆响应函数,然后再执行编译或线条提取之前使用lut_trans将此函数应用于图像中。
2.2 感兴趣区域
使用感兴趣区域可加快边缘提取速度,鲁棒性好。
2.3 提取变换或线条
边缘轮廓提取最常用的算子edges_sub_pix。通过使用filter参数指定相应的名称,可以选择各种过滤方法。通常情况,‘canny’(基于高斯卷积)或’lanser2’。‘lanser2’的优点是递归实现,当平滑值在增大会增加执行时间。如果图像没有收到噪声或图像模糊,可考虑使用’sobel_fast’。算子zero_crossing_sub_pix可以结合拉普拉斯算子使用。通常情况拉普拉斯算子主要应用于医学领域。考虑到在彩色图像中提取边缘,可使用算子edges_color_sub_pix。类似的,图像没有噪声或模糊,参数建议使用’sobel_fast’进行快速边缘提取。
提取线最常用的运算符是lines_gauss。与lines_facet相比,它更灵活。提取线条宽度由参数指定:Sigma。如果需要线条宽,可以使用算子zoom_image_factor缩小图像,减少执行时间。对于彩色线条可使用算子lines_color。
2.4 轮廓属性
提取边缘或线不仅提供XLD轮廓,还提供属性。属性是数值,它们要么与轮廓的每个控制点相关联(称为轮廓属性),要么与每个轮廓作为一个整体相关性(全局轮廓属性)。可以通过算子get_contour_attrib_xld和get_contour_global_attrib_xld指定属性名称。
属性值以数字元祖的形式返回。典型的边缘属性如振幅和方向。对于行,典型的属性是行宽度。可以使用query_contour_attribs_xld和query_contour_global_attribs_xld查询给定轮廓的可以属性。
2.5 处理XLD轮廓
通常,仅通过提供轮廓和访问属性并不能完成任务。Halcon提供轮廓分割、特征提取等。
2.6 将结果转换为世界坐标系
在需要利用中,等高线的坐标需要转换为另一种坐标系。校正系统中,算子contour_to_world_plane_xld算子轻松转换,同时可以消除镜头和透视变换的畸变。
2.7 可视化结果
将处理结果显示在图像上。
3 程序示例:
* rim_simple.hdev: measures the diameter of drill-holes
*
dev_update_off ()
* ****
* step: acquire image
* ****
read_image (Image, 'rim')
get_image_size (Image, Width, Height)
dev_open_window_fit_image (Image, 0, 0, Width, Height, WindowID)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
* ****
* step: determine region of interest (ROI)
* ****
threshold (Image, Dark, 0, 128)
connection (Dark, DarkRegions)
select_shape (DarkRegions, Circles, ['circularity', 'area'], 'and', [0.85, 50], [1.0, 99999])
boundary (Circles, RegionBorder, 'inner')
dilation_circle (RegionBorder, RegionDilation, 6.5)
union1 (RegionDilation, ROIEdges)
dev_display (Image)
dev_set_color ('yellow')
dev_set_draw ('margin')
dev_display (ROIEdges)
disp_continue_message (WindowID, 'black', 'true')
stop ()
* ****
* step: extract edges
* ****
reduce_domain (Image, ROIEdges, ImageROI)
edges_sub_pix (ImageROI, Edges, 'lanser2', 0.3, 10, 30)
sort_contours_xld (Edges, SortedContours, 'upper_left', 'true', 'row')
dev_display (Image)
colored_display (SortedContours, ['cyan', 'white'])
disp_continue_message (WindowID, 'black', 'true')
stop ()
* ****
* step: process contours
* ****
fit_ellipse_contour_xld (Edges, 'ftukey', -1, 2, 0, 200, 3, 2, Row, Column, Phi, Ra, Rb, StartPhi, EndPhi, PointOrder)
NumHoles := |Ra|
gen_ellipse_contour_xld (ContEllipse, Row, Column, Phi, Ra, Rb, gen_tuple_const(NumHoles,0), gen_tuple_const(NumHoles,rad(360)), gen_tuple_const(NumHoles,'positive'), 1)
dev_display (Image)
sort_contours_xld (ContEllipse, SortedContEllipse, 'upper_left', 'true', 'row')
colored_display (SortedContEllipse, ['cyan', 'white'])
dev_set_color ('yellow')
for i := 0 to NumHoles - 1 by 1
sinPhi := sin(Phi[i])
cosPhi := cos(Phi[i])
disp_arrow (WindowID, Row[i], Column[i], Row[i] - sinPhi * Ra[i], Column[i] + cosPhi * Ra[i], 1)
disp_arrow (WindowID, Row[i], Column[i], Row[i] - cosPhi * Rb[i], Column[i] - sinPhi * Rb[i], 1)
disp_arrow (WindowID, Row[i], Column[i], Row[i] + sinPhi * Ra[i], Column[i] - cosPhi * Ra[i], 1)
disp_arrow (WindowID, Row[i], Column[i], Row[i] + cosPhi * Rb[i], Column[i] + sinPhi * Rb[i], 1)
set_tposition (WindowID, Row[i] - Rb[i] - 50, Column[i] - 85)
write_string (WindowID, 'D1=' + 2 * Ra[i])
set_tposition (WindowID, Row[i] - Rb[i] - 30, Column[i] - 85)
write_string (WindowID, 'D2=' + 2 * Rb[i])
endfor