拟合直线 rake工具
摘自鸟叔教程。
1. 为什么要开发rake工具
- halcon提供给我们使用的测量矩形只能返回一个点,操作起来比较复杂,实际项目中不好使用;
- 为了提高工具的通用性,鲁棒性,也是为了提高项目的开发效率,基于halcon已有的算子进一步开发工具是比较不错的选择。
- rake工具是用来检测直线的,通过鼠标画一条线,然后工具根据已设定好的参数去附近区域寻找并拟合直线,返回这条直线。两条直线就可以计算交点了。
- rake工具封装了三个函数来完成,代码不少,下面我就来展示详细的代码和实际效果。
2. rake工具封装的三个函数
2.1 创建ROI的函数:draw_rake
【函数】
draw_rake( : Regions : WindowHandle, Elements, DetectHeight, DetectWidth : Row1, Column1, Row2, Column2)
【参数】
参数 | 描述 |
---|---|
Regions | (输出)输出边缘点检测区域及检测方向 |
WindowHandle | (输入)图形窗口句柄 |
Element | (输入)检测边缘数 |
DetectHeight | (输入)检测边缘的卡尺工具的高度 |
DetectWidth | (输入)检测边缘的卡尺工具的宽度 |
Row1 | (输出)检测区域起点Y |
Column1 | (输出)检测区域起点X |
Row2 | (输出)检测区域终点Y |
Column2 | (输出)检测区域终点X |
【函数实现】
*提示
disp_message (WindowHandle, '点击鼠标左键画一条直线,点击右键确认', 'window', 12, 12, 'red', 'false')
*产生一个空显示对象,用于显示
gen_empty_obj (Regions)
*画矢量检测直线
draw_line (WindowHandle, Row1, Column1, Row2, Column2)
*产生直线xld
gen_contour_polygon_xld (RegionLines, [Row1,Row2], [Column1,Column2])
*存储到显示对象
concat_obj (Regions, RegionLines, Regions)
*计算直线与x轴的夹角,逆时针方向为正
angle_lx(Row1,Column1,Row2,Column2,ATan)
*边缘检测方向垂直于检测直线:直线方向正向旋转90°为边缘检测方向
ATan := ATan+rad(90)
*根据检测直线按顺序产生测量区域矩形,并存储到显示对象
for i:=1 to Elements by 1
*如果只有一个测量矩形,作为卡尺工具,宽度为检测直线的长度
if(Elements==1)
RowC:=(Row1+Row2)*0.5
ColC:=(Column1+Column2)*0.5
distance_pp (Row1, Column1, Row2, Column2, Distance)
gen_rectangle2_contour_xld (Rectangle, RowC, ColC, ATan, DetectHeight/2, Distance/2)
else
*如果有多个测量矩形,产生该测量矩形xld
RowC:=Row1+(((Row2-Row1)*(i-1))/(Elements-1))
ColC:=Column1+(Column2-Column1)*(i-1)/(Elements-1)
gen_rectangle2_contour_xld (Rectangle, RowC, ColC, ATan, DetectHeight/2, DetectWidth/2)
endif
*把测量矩形xld存储到显示对象
concat_obj(Regions, Rectangle,Regions)
if(i==1)
*在第一个测量矩形绘制一个箭头xld,用于知识边缘检测方向
RowL2 := RowC+DetectHeight/2*sin(-ATan)
RowL1 := RowC-DetectHeight/2*sin(-ATan)
ColL2 := ColC+DetectHeight/2*cos(-ATan)
ColL1 := ColC-DetectHeight/2*cos(-ATan)
gen_arrow_contour_xld (Arrow, RowL1, ColL1, RowL2, ColL2, 25, 25)
endif
endfor
return ()
2.2 边缘测量的函数:rake
【函数】
rake(Image : Regions : Elements, DetectHeight, DetectWidth, Sigma, Threshold, Transition, Select, Row1, Column1, Row2, Column2 : ResultRow, ResultColumn)
【参数】
参数 | 描述 |
---|---|
Image | (输入)输入图像 |
Regions | (输出)输出边缘检测ROI及检测方向 |
Elements | (输入)检测边缘的数量 |
DetectHeight | (输入)卡尺工具的高度 |
DetectWidth | (输入)卡尺工具的宽度 |
Sigma | (输入)高斯滤波因子 |
Threshold | (输入)边缘幅度阈值。边缘幅度大于该值作为参考边缘,小于该值不是边缘 |
Transition | (输入)极性:positive表示由黑到白,negative表示由白到黑,all表示以上两种方向 |
Select | (输入)first表示选择第一点,last表示选择最后一点,max表示选择边缘幅度最强点 |
Row1 | (输入)直线ROI起点Y |
Column1 | (输入)直线ROI起点X |
Row2 | (输入)直线ROI终点Y |
Column2 | (输入)直线ROI终点X |
ResultRow | (输出)检测到的边缘的Y坐标数组 |
ResultColumn | (输出)检测到的边缘的X坐标数组 |
【函数实现】
SelectOut := Select
TransitionOut := Transition
*获取图像尺寸
get_image_size(Image,Width,Height)
*产生一个空显示对象,用于显示
gen_empty_obj (Regions)
*初始化边缘坐标数组
ResultRow:=[]
ResultColumn:=[]
*产生直线xld
gen_contour_polygon_xld (RegionLines, [Row1,Row2], [Column1,Column2])
*存储到显示对象
concat_obj (Regions, RegionLines, Regions)
*计算直线与x轴的夹角,逆时针方向为正向
angle_lx (Row1, Column1, Row2, Column2, ATan)
*边缘检测方向垂直由于检测直线:直线方向正向旋转90°为边缘检测方向
ATan:=ATan+rad(90)
*根据检测直线按顺序产生测量区域矩形,并存储到显示对象
for i:=1 to Elements by 1
*如果只有一个测量矩形,作为卡尺工具,宽度为检测直线的长度
if(Elements == 1)
RowC:=(Row1+Row2)*0.5
ColC:=(Column1+Column2)*0.5
*判断是否超出图像,超出不检测边缘
if(RowC>Height-1 or RowC<0 or ColC>Width-1 or ColC<0)
continue
endif
distance_pp(Row1, Column1, Row2, Column2, Distance)
DetectWidth:=Distance
gen_rectangle2_contour_xld (Rectangle, RowC, ColC, ATan, DetectHeight/2, Distance/2)
else
*如果有多个测量矩形,产生该测量矩形xld
RowC:=Row1+(((Row2-Row1)*(i-1))/(Elements-1))
ColC:=Column1+(Column2-Column1)*(i-1)/(Elements-1)
*判断是否超出图像,超出不检测边缘
if(RowC>Height-1 or RowC<0 or ColC>Width-1 or ColC<0)
continue
endif
gen_rectangle2_contour_xld (Rectangle, RowC, ColC, ATan, DetectHeight/2, DetectWidth/2)
endif
*把测量矩形xld存储到显示对象
concat_obj (Regions, Rectangle, Regions)
if(i==1)
*在第一个测量矩形绘制一个箭头xld,用于指示边缘检测方向
RowL2:=RowC+DetectHeight/2*sin(-ATan)
RowL1:=RowC-DetectHeight/2*sin(-ATan)
ColL2:=ColC+DetectHeight/2*cos(-ATan)
ColL1:=ColC-DetectHeight/2*cos(-ATan)
gen_arrow_contour_xld (Arrow1, RowL1, ColL1, RowL2, ColL2, 25, 25)
*把xld存储到显示对象
concat_obj (Regions, Arrow1, Regions)
endif
*产生测量对象句柄
gen_measure_rectangle2 (RowC, ColC, ATan, DetectHeight/2, DetectWidth/2, Width, Height, 'nearest_neighbor', MeasureHandle)
*设置极性
if (TransitionOut=='negative')
TransitionOut := 'negative'
elseif (TransitionOut=='positive')
TransitionOut := 'positive'
else
TransitionOut := 'all'
endif
*设置边缘位置。最强点是从所有边缘中选择幅度绝对值对大点,需要设置为'all'
if (SelectOut=='first')
SelectOut := 'first'
elseif (SelectOut=='last')
SelectOut := 'last'
else
SelectOut := 'all'
endif
*检测边缘
measure_pos (Image, MeasureHandle, Sigma, Threshold, TransitionOut, SelectOut, RowEdge, ColEdge, Amplitude, Distance)
*清除测量对象句柄
close_measure (MeasureHandle)
*临时变量初始化
*保存找到指定边缘的坐标
tRow:=0
tCol:=0
*保存边缘的幅度绝对值
t:=0
*找到的边缘必须至少为1个
tuple_length (RowEdge, Number)
if(Number<1)
continue
endif
*有多个边缘时,选择幅度绝对之后最大的边缘
for j:= 0 to Number-1 by 1
if(abs(Amplitude[j])>t)
tRow:=RowEdge[j]
tCol:=ColEdge[j]
t:=abs(Amplitude)
endif
endfor
*把找到的边缘保存在输出数组
if(t>0)
ResultRow:=[ResultRow,tRow]
ResultColumn:=[ResultColumn,tCol]
endif
endfor
return ()
2.3 拟合直线的函数:pts_to_best_line
【函数】
pts_to_best_line( : Line : Rows, Cols, ActiveNum : Row1, Column1, Row2, Column2)
【参数】
参数 | 描述 |
---|---|
Line | (输出)输出拟合直线的xld |
Rows | (输入)拟合直线的输入Y数组 |
Cols | (输入)拟合直线的输入X数组 |
ActiveNum | (输入)最小有效点数 |
Row1 | (输出)拟合的直线起点Y值 |
Column1 | (输出)拟合的直线起点X值 |
Row2 | (输出)拟合的直线终点Y值 |
Column2 | (输出)拟合的直线终点X值 |
【函数实现】
*初始化
Row1:=0
Column1:=0
Row2:=0
Column2:=0
*产生一个空的直线对象,用于保存拟合后的直线
gen_empty_obj (Line)
*计算边缘数量
tuple_length (Cols, Length)
*当边缘数量不小于有效点数时进行拟合
if(Length>=ActiveNum and ActiveNum>1)
*halcon的拟合是基于xld的,需要把边缘连接成xld
gen_contour_polygon_xld (Contour, Rows, Cols)
*拟合直线。使用的算法是'tukey',其他算法参考fit_line_contour_xld的描述
fit_line_contour_xld (Contour, 'tukey', -1, 0, 5, 2, Row1, Column1, Row2, Column2, Nr, Nc, Dist)
*判断拟合结果是否有效:如果拟合成功,数组中元素的数量大于0
tuple_length (Dist, Length1)
if(Length1 < 1)
return()
endif
*根据拟合结果,产生直线xld
gen_contour_polygon_xld (Line, [Row1,Row2], [Column1,Column2])
endif
return ()
3. rake工具使用示例
- 如下图就是使用上面的三条函数得到的效果,红色的线是鼠标画出的,蓝色直线是抓取到并返回的直线。
- Talk is cheap, Show you my code
read_image(Image, '矩形.png')
dev_get_window (WindowHandle)
dev_set_color ('dark olive green')
* 1. 创建ROI draw_rake函数
Elements:=20
DetectHeight:=50
DetectWidth:=5
draw_rake (Regions, WindowHandle, Elements, DetectHeight, DetectWidth, Row1, Column1, Row2, Column2)
* 2. 边缘测量 rake函数
Sigma:=3
Threshold:=30
Transition:='all'
Select:='all'
rake (Image, Regions, Elements, DetectHeight, DetectWidth, Sigma, Threshold, Transition, Select, Row1, Column1, Row2, Column2, ResultRow, ResultColumn)
* 3. 拟合直线 pts_to_best_line函数
ActiveNum:=5
pts_to_best_line (Line, ResultRow, ResultColumn, ActiveNum, Row1, Column1, Row2, Column2)
dev_set_color ('blue')
dev_set_line_width (3)
dev_display (Line)
4. 代码下载
- 不想自己写的,这里是代码和图片下载链接: