对应示例程序:
measuring_circles.hdev
目标:将圆拟合成圆形轮廓段,提取圆的位置和半径
思路为:
1.读取图像
2.通过二值化,连通域分割等Blob分析手段,定位到ROI区域
3.利用形态学的处理方法,提取出ROI的边缘,再膨胀,将ROI区域从原图中抠出来(形态学处理边缘:膨胀一个像素后的图 减去 原图 或者 原图 减去 腐蚀一个像素的图)
4.利用canny算法对ROI区域进行精确检测,再判断其中的圆弧部分,压入到事先生成的HObj中。(因为原图中的圆并不是单纯的圆,还有其他的边缘干扰,因此先把边缘拆开,再判断其中的圆弧部分)
5.合并是同一个圆的部分圆弧,然后对结果中的圆进行单独处理,拟合得到半径
图像:
代码:
dev_update_off ()
*
* step: acquire image
*
* 前期准备以及一些参数设置
read_image (Image, 'circle_plate')
get_image_size (Image, Width, Height)
dev_close_window ()
dev_open_window (0, 0, Width / 2, Height / 2, 'light gray', WindowID)
dev_set_part (0, 0, Height - 1, Width - 1)
dev_set_line_width (3)
dev_set_color ('white')
dev_set_draw ('margin')
dev_display (Image)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
disp_continue_message (WindowID, 'black', 'true')
stop ()
*
* step: segment image into regions
*
*分离图像 Blob分析
dev_set_colored (12)
dev_set_line_width (2)
dev_set_draw ('fill')
fast_threshold (Image, Region, 200, 255, 20) //快速阈值分割 图像差异很大不是黑就是白
connection (Region, ConnectedRegions) //连通域分割
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 70, 50000) //根据面积筛选出合适的区域
dev_display (Image)
dev_display (SelectedRegions)
disp_continue_message (WindowID, 'black', 'true')
stop ()
*
*这一步是先利用形态学求个初略轮廓,再膨胀一次,最后从原图中把ROI抠出来
* step: create ROI for contour processing
*
boundary (SelectedRegions, RegionBorder, 'inner_filled') //形态学处理边缘,获取最外边的边缘(膨胀再减去原图)
dilation_circle (RegionBorder, RegionDilation, 3.5) //圆形膨胀
union1 (RegionDilation, RegionUnion) //将全部分开的区域再合并起来
reduce_domain (Image, RegionUnion, ImageReduced) //将ROI区域从原图中抠出来
dev_clear_window ()
dev_display (ImageReduced)
disp_continue_message (WindowID, 'black', 'true')
stop ()
*
* step: create contours and fit circles to them
* 提取轮廓 并拟合
*这里就是精确的处理轮廓
edges_sub_pix (ImageReduced, Edges, 'canny', 1.5, 10, 40) //canny算法的亚像素边缘检测
//因为图像中的圆并不是完全独立的,因此先把每段轮廓分割开
segment_contours_xld (Edges, ContoursSplit, 'lines_circles', 5, 2, 2) //将提取出的轮廓进行分割,直线和圆
select_contours_xld (ContoursSplit, SelectedContours, 'contour_length', 25, 99999, -0.5, 0.5) //筛选符合要求的XLD轮廓
count_obj (SelectedContours, NumberContours) //计算筛选出的XLD轮廓的数量
gen_empty_obj (Circles) //一个空的Obj 用来存放
//对分割的每段轮廓进行处理,将是圆弧的放入设定好的Obj中
for i := 1 to NumberContours by 1
select_obj (SelectedContours, ObjectSelected, i) //挨个处理提取出的XLD轮廓
get_contour_global_attrib_xld (ObjectSelected, 'cont_approx', Attrib) //返回XLD轮廓的全局属性值 判断轮廓是否适合被划分为圆 //-1 适合直线 0适合圆弧 1 适合圆
if (Attrib == 1)
concat_obj (Circles, ObjectSelected, Circles) //如果是圆 就添加到之前新建的Obj中
endif
endfor
*合并是同一个圆的部分圆弧
union_cocircular_contours_xld (Circles, UnionContours, rad(60), rad(10), rad(30), 100, 50, 10, 'true', 1)
dev_clear_window ()
dev_set_color ('black')
dev_display (UnionContours)
disp_continue_message (WindowID, 'black', 'true')
stop ()
count_obj (UnionContours, NumberCircles) //统计圆的数目
CenterRow := [] //中心坐标
CenterColumn := []
dev_clear_window ()
dev_set_color ('black')
set_display_font (WindowID, 12, 'mono', 'true', 'false')
dev_display (SelectedContours)
for i := 1 to NumberCircles by 1
select_obj (UnionContours, ObjectSelected, i) //对每个圆XLD轮廓 进行拟合和计算
//拟合圆
fit_circle_contour_xld (ObjectSelected, 'algebraic', -1, 0, 0, 3, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)
gen_circle_contour_xld (ContCircle, Row, Column, Radius, 0, rad(360), 'positive', 1.5)
dev_set_color ('white')
//进行一些输出的坐标设计
dev_display (ContCircle)
if (i == 1)
Row2 := Row + Radius * sin(rad(-45))
Column2 := Column + Radius * cos(rad(-45))
set_tposition (WindowID, Row2 - 35, Column2 + 5)
endif
if (i > 1)
exist := 0
for j := 0 to i - 2 by 1
distance_pp (Row, Column, CenterRow[j], CenterColumn[j], DistanceCenters)
if (DistanceCenters < 20)
exist := 1
endif
endfor
if (exist == 1)
Row2 := Row + Radius * sin(rad(-135))
Column2 := Column + Radius * cos(rad(-135))
set_tposition (WindowID, Row2 - 40, Column2 - 30)
else
Row2 := Row + Radius * sin(rad(-45))
Column2 := Column + Radius * cos(rad(-45))
set_tposition (WindowID, Row2 - 35, Column2 + 5)
endif
endif
disp_arrow (WindowID, Row, Column, Row2, Column2, 2)
write_string (WindowID, i)
if (i < 8)
disp_message (WindowID, 'R' + i + ' = ' + Radius$'.4', 'window', i * 20, 130, 'black', 'false')
else
disp_message (WindowID, 'R' + i + ' = ' + Radius$'.4', 'window', (i - 7) * 20, 400, 'black', 'false')
endif
CenterRow := [CenterRow,Row]
CenterColumn := [CenterColumn,Column]
endfor
*
dev_update_window ('on')
用到的几个算子:
boundary --形态学处理边缘,获取最外边的轮廓。其实就是膨胀一个像素 再和原来的图像取差 或者腐蚀一个像素再和原来的图像取差。
segment_contours_xld-- 将亚像素轮廓分割为直线段、圆、或圆弧(三种方式:直线,直线和圆,直线和弧,根据实际图像进行选择)
get_contour_global_attrib_xld -返回XLD轮廓的全局属性值 判断轮廓是否适合被划分为圆 返回值:-1 适合直线 0适合圆弧 1 适合圆
union_cocircular_contours_xld–合并是同一个圆的部分圆弧
fit_circle_contour_xld–根据轮廓拟合圆
distance_pp–计算两点间的距离
参考资料:
[1].https://blog.csdn.net/ABC13222880223/article/details/98481651
[2].https://blog.csdn.net/touchych/article/details/44464045