功能介绍
这个程序演示了用于1D测量的测量对象的方向的自动细化。如果用户输入的测量ROI不完全垂直于被测量的结构,则程序估计正确的方向并相应地旋转ROI。如下图:
原图结果图
应用(个人想法)
应用在二次开发当中(上位机),例如用户随机画了一个检测区想抓边,那么可在抓边按钮中加入以下程序 自动的去根据区域内容调整roi,去适当地放大缩小或者旋转区域去抓边。相当于是提高易用性。
官方程序及注释和理解
* Display initializations
* 窗口显示初始化,变量初始化
Interactive := 0
dev_close_window ()
dev_update_window ('off')
read_image (Image, 'fuse')
* 这里获得的宽高是为了后面的gen_measure_rectangle2()
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
dev_set_draw ('margin')
dev_display (Image)
*
if (Interactive == 0)
* 生成一个测量矩形,下一步将会纠正错误的旋转
disp_message (WindowHandle, 'Measure rectangle generated', 'window', 12, 12, 'black', 'true')
disp_message (WindowHandle, 'Wrong rotation will be corrected in the next step', 'window', 50, 12, 'black', 'true')
* 设置线的颜色和宽度
dev_set_color ('blue')
dev_set_line_width (2)
* 任意生成一个带角度的矩形(85°)长80宽5,等下把它旋转纠正
gen_rectangle2 (Rectangle, 292, 540, rad(85), 80, 5)
dev_display (Rectangle)
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* 任意方向地去包围某个区域的最小矩形,相当于把绘制的矩形的中心坐标和角度长宽存到变量当中
smallest_rectangle2 (Rectangle, Row, Column, Phi, Length1, Length2)
else
* User interaction: input measure rectangle
disp_message (WindowHandle, 'Modify measure rectangle (arrow up, confirm with right mouse)', 'window', 12, 12, 'black', 'true')
disp_message (WindowHandle, 'Eventually wrong rotations will be corrected', 'window', 50, 12, 'black', 'true')
dev_set_color ('blue')
dev_set_line_width (2)
* 任意定向矩形的交互绘图
draw_rectangle2_mod (WindowHandle, 292, 540, rad(85), 80, 5, Row, Column, Phi, Length1, Length2)
gen_rectangle2 (Rectangle, Row, Column, Phi, Length1, Length2)
endif
*
* Apply a first measurement
* 准备提取垂直于矩形长轴的直边
* Width:随后要处理的图像宽度; Heigth:随后要处理的图形高度
gen_measure_rectangle2 (Row, Column, Phi, Length1, Length2, Width, Height, 'bicubic', MeasureHandle)
* 提取与矩形或者环状弧垂直的直线边缘
* Transition='all': 返回具有亮暗亮或者暗亮暗的边缘对,适用于不同背景具有不同亮度的物体
* 如果Transition='positive': First返回沿着矩形长轴方向的暗到亮过渡的边缘点,Second返回亮到暗的边缘点;'negative'反之
* RowEdgeFirst: 边缘到第一条边的中心的row坐标
* ColumnEdgeFirst: 边缘到第一条边的中心的col坐标
* RowEdgeSecond: 边缘到第二条边的中心的row坐标
* ColumnEdgeSecond: 边缘到第二条边的中心的col坐标
* IntraDistance: 边缘对之间的距离
* InterDistance: 连续边缘之间的距离
measure_pairs (Image, MeasureHandle, 1, 30, 'all', 'all', RowEdgeFirst, ColumnEdgeFirst, AmplitudeFirst, RowEdgeSecond, ColumnEdgeSecond, AmplitudeSecond, IntraDistance, InterDistance)
*
* Extract edges and edge directions within a slightly extended ROI
gen_rectangle2 (RectangleROI, Row, Column, Phi, Length1, 4 * Length2)
reduce_domain (Image, RectangleROI, ImageReduced)
* 使用Deriche,Shen,或Canny等滤波器去提取子像素的精确边缘
* edges_sub_pix( , , 滤波器, 平滑强度, 对比度最小阈值, 对比度最大阈值)
edges_sub_pix (ImageReduced, Edges, 'canny', 1, 20, 40)
* 为每个轮廓计算一个xld轮廓方向
get_contour_angle_xld (Edges, 'abs', 'regress', 3, Angles)
*
* Use the mean edge direction to estimate the correct orientation
* of the measure rectangle.
* 使用平均边缘方向去估计这个测量矩形的正确方向
* rad()是把角度换成弧度; deg()是把弧度换成角度
MeanAngle := mean(Angles) - rad(90)
MeanAngleDeg := deg(MeanAngle)
*
* Correct the orientation of the measure rectangle
gen_measure_rectangle2 (Row, Column, MeanAngle, Length1, Length2, Width, Height, 'bicubic', MeasureHandle)
gen_rectangle2 (Rectangle2, Row, Column, MeanAngle, Length1, Length2)
measure_pairs (Image, MeasureHandle, 1, 30, 'all', 'all', RowEdgeFirst2, ColumnEdgeFirst2, AmplitudeFirst2, RowEdgeSecond2, ColumnEdgeSecond2, AmplitudeSecond2, IntraDistance2, InterDistance2)
*
* Display results
dev_display (Image)
dev_set_draw ('margin')
p_disp_edge_marker (RowEdgeFirst, ColumnEdgeFirst, Phi, Length2, 'red', 2)
p_disp_edge_marker (RowEdgeSecond, ColumnEdgeSecond, Phi, Length2, 'red', 2)
dev_set_color ('red')
dev_display (Rectangle)
p_disp_edge_marker (RowEdgeFirst2, ColumnEdgeFirst2, MeanAngle, Length2, 'green', 2)
p_disp_edge_marker (RowEdgeSecond2, ColumnEdgeSecond2, MeanAngle, Length2, 'green', 2)
dev_set_color ('green')
dev_display (Rectangle2)
disp_message (WindowHandle, 'Measure direction corrected from ' + deg(Phi)$'.3' + ' deg to ' + MeanAngleDeg$'.3' + ' deg', 'window', 12, 12, 'black', 'true')
LastCommonIndex := min([|RowEdgeFirst|,|RowEdgeSecond|,|RowEdgeFirst2|,|RowEdgeSecond2|]) - 1
disp_message (WindowHandle, 'Width [px]: ' + IntraDistance[0:LastCommonIndex]$'.3' + ', corrected: ' + IntraDistance2[0:LastCommonIndex]$'.3', 'window', (RowEdgeFirst[0:LastCommonIndex] + RowEdgeSecond[0:LastCommonIndex]) / 2 - 10, 150, 'black', 'false')
* 子函数:p_disp_edge_marker()内容如下:
*
* Determine the number of edges
NumRows := |Rows|
NumCols := |Cols|
Num := min([NumRows,NumCols])
*
* Loop over the edges
for i := 0 to Num - 1 by 1
Row := Rows[i]
Col := Cols[i]
*
* Determine start and end point of the edge marker.
* Length边是最开始的smartest_rectangle2得到的矩形短边,然后通过三角函数计算
RowStart := Row + Length * cos(Phi)
RowEnd := Row - Length * cos(Phi)
ColStart := Col + Length * sin(Phi)
ColEnd := Col - Length * sin(Phi)
*
* Generate a contour that connects the start and end point.
* 根据一个多边形(以元组形式给出)创建一个xld
gen_contour_polygon_xld (Marker, [RowStart,RowEnd], [ColStart,ColEnd])
*
* Display the contour with the specified style.
dev_set_color (Color)
dev_set_line_width (LineWidth)
dev_display (Marker)
endfor
return ()
理解后编写的
流程步骤:
第①步:初始化窗口和变量
第②步:随机生成一个矩形,并记录长宽角度坐标
第③步:在原图上提取矩形区域内的边缘过渡线上的中心边缘点
第④步:使用canny直接提取ROI内的边缘过渡线,并求出边缘线的角
度(针对矩形长边),针对这个角度绘制矩形
第⑤步:在原图中提取新的矫正后的矩形的边缘点
第⑥步:分别绘制原来的矩形和纠正后的矩形的边缘线,把重复部分封装为一个函数
第⑦步:总结结果并展示
* 第一步:初始化窗口和变量
Interactive := 0
dev_close_window ()
dev_update_off ()
read_image (Image, 'D:/halcon/官方例程练习/correct_measure_direction/fuse.png')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
dev_set_draw ('margin')
dev_set_color ('blue')
dev_set_line_width (2)
dev_display (Image)
* 第二步:随机生成一个矩形,并记录长宽角度坐标
gen_rectangle2 (Rectangle, 292, 540, rad(80), 80, 5)
smallest_rectangle2 (Rectangle, Row, Column, Phi, Length1, Length2)
gen_cross_contour_xld (Cross, Row, Column, 6, Phi)
* 第三步:在原图上提取矩形区域内的边缘过渡线上的中心边缘点
gen_measure_rectangle2 (Row, Column, Phi, Length1, Length2, Width, Height, 'bicubic', MeasureHandle)
measure_pairs (Image, MeasureHandle, 1, 30, 'all', 'all', RowEdgeFirst, ColumnEdgeFirst, AmplitudeFirst, RowEdgeSecond, ColumnEdgeSecond, AmplitudeSecond, IntraDistance, InterDistance)
* 把提取到的点显示在图上
dev_set_color ('green')
gen_cross_contour_xld (Cross1, RowEdgeFirst, ColumnEdgeFirst, 4, Phi)
dev_set_color ('forest green')
gen_cross_contour_xld (Cross2, RowEdgeSecond, ColumnEdgeSecond, 4, Phi)
* 第四步:使用canny直接提取ROI内的边缘过渡线,并求出边缘线的角度(针对矩形长边),针对这个角度绘制矩形
dev_set_color ('blue')
* 扩大矩形宽度目的:为了找很多的Edges,免得后面纠正回去的矩形与Edges没有交集,就看着尴尬
gen_rectangle2 (Rectangle1, Row, Column, Phi, Length1, 4 * Length2)
reduce_domain (Image, Rectangle1, ImageReduced)
* 灰度差在20-40之间都看作为边缘线
edges_sub_pix (ImageReduced, Edges, 'canny', 1, 20, 40)
get_contour_angle_xld (Edges, 'abs', 'range', 3, Angles)
* 获取每条边缘线的平均角度,去作为正确的角度
* 这个度数相对于矩形长轴来说,确实是接近90度
MeanAngle := mean(Angles) - rad(90)
MeanAngleDeg := deg(MeanAngle)
gen_rectangle2 (Rectangle2, Row, Column, MeanAngle, Length1, Length2)
* 第五步:在原图中提取新的矫正后的矩形的边缘点
gen_measure_rectangle2 (Row, Column, MeanAngle, Length1, Length2, Width, Height, 'bicubic', MeasureHandle1)
measure_pairs (Image, MeasureHandle1, 1, 30, 'all', 'all', RowEdgeFirst1, ColumnEdgeFirst1, AmplitudeFirst1, RowEdgeSecond1, ColumnEdgeSecond1, AmplitudeSecond1, IntraDistance1, InterDistance1)
dev_set_color ('red')
gen_cross_contour_xld (Cross5, Row, Column, 6, Phi)
dev_set_color ('green')
gen_cross_contour_xld (Cross3, RowEdgeFirst1, ColumnEdgeFirst1, 4, Phi)
dev_set_color ('forest green')
gen_cross_contour_xld (Cross4, RowEdgeSecond1, ColumnEdgeSecond1, 4, Phi)
* 第六步:分别绘制原来的矩形和纠正后的矩形的边缘线,把重复部分封装为一个函数
dev_clear_window ()
dev_set_color ('blue')
dev_display (Image)
dev_display (Rectangle)
dev_set_color ('red')
*gen_cross_contour_xld (Cross6, RowEdgeFirst, ColumnEdgeFirst, 6, Phi)
stop ()
p_dis_edges_maker (RowEdgeFirst,ColumnEdgeFirst,Phi,Length2,'red',2)
p_dis_edges_maker (RowEdgeSecond, ColumnEdgeSecond, Phi, Length2, 'red', 2)
dev_set_color ('pink')
dev_display (Rectangle2)
p_dis_edges_maker (RowEdgeFirst1,ColumnEdgeFirst1,MeanAngle,Length2,'green',2)
p_dis_edges_maker (RowEdgeSecond1, ColumnEdgeSecond1, MeanAngle, Length2, 'green', 2)
* 第七步:总结结果并展示
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
disp_message (WindowHandle, 'Measure direction corrected from ' + deg(Phi)$'.3' + ' deg to ' + MeanAngleDeg$'.3' + ' deg', 'window', 12, 12, 'black', 'true')
NumberRow := |Rows|
NumberCol := |Columns|
num := min2(NumberRow, NumberCol)
for i := 0 to num - 1 by 1
row1 := Rows[i] + Length * cos(Phi)
row2 := Rows[i] - Length * cos(Phi)
col1 := Columns[i] + Length * sin(Phi)
col2 := Columns[i] - Length * sin(Phi)
dev_set_color (Color)
dev_set_draw ('margin')
dev_set_line_width (LineWidth)
gen_contour_polygon_xld (Maker, [row1, row2], [col1, col2])
dev_display (Maker)
endfor
return ()
结果图
学习心得
①学习算子gen_measure_rectangle2():
- 准备提取垂直于矩形长轴的直边
- Width:随后要处理的图像宽度; Heigth:随后要处理的图形高度
gen_measure_rectangle2 (Row, Column, Phi, Length1, Length2, Width, Height, 'bicubic', MeasureHandle)
②学习算子measure_pairs()
- 提取与矩形或者环状弧垂直的直线边缘
- Transition=‘all’: 返回具有亮暗亮或者暗亮暗的边缘对,适用于不同背景具有不同亮度的物体
- 如果Transition=‘positive’: First返回沿着矩形长轴方向的暗到亮过渡的边缘点,Second返回亮到暗的边缘点;'negative’反之
- RowEdgeFirst: 边缘到第一条边的中心的row坐标
- ColumnEdgeFirst: 边缘到第一条边的中心的col坐标
- RowEdgeSecond: 边缘到第二条边的中心的row坐标
- ColumnEdgeSecond: 边缘到第二条边的中心的col坐标
- IntraDistance: 边缘对之间的距离
- InterDistance: 连续边缘之间的距离
measure_pairs (Image, MeasureHandle, 1, 30, 'all', 'all', RowEdgeFirst, ColumnEdgeFirst, AmplitudeFirst, RowEdgeSecond, ColumnEdgeSecond, AmplitudeSecond, IntraDistance, InterDistance)
③子函数的创建和调用
相对而言,还是很easy的:
根据传入函数的类型去设置,image\region\tuple。函数调用的话,直接在main函数中或者其他子函数当中调用即可
分析有不到位的,请各位点评指正~