这段 Halcon 代码的主要功能是从一幅图像中提取外矩形和内矩形,并获取它们的顶点坐标,最后根据这些顶点坐标生成线段区域。具体来说,它会先读取图像,将其转换为灰度图,然后通过强调、阈值分割、连通区域分析、形状选择等操作分别提取外矩形和内矩形的轮廓,接着获取这些矩形的最小外接矩形的参数,生成矩形轮廓,再对矩形轮廓进行排序和顶点坐标提取,最后根据顶点坐标生成线段区域。
代码详细解释
1. 图像读取与窗口设置
read_image (Image, 'D:/动态重复/聚焦在底部/线宽识别动态重复测试二维图(聚焦在底部)/picture138.png')
dev_clear_window ()
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width/2, Height/2, 'black', WindowHandle)
read_image
:从指定路径读取图像并存储在Image
变量中。dev_clear_window
:清空当前图形窗口。get_image_size
:获取图像的宽度和高度,分别存储在Width
和Height
变量中。dev_open_window
:打开一个新的图形窗口,窗口大小为图像宽度和高度的一半,背景颜色为黑色。
2. 图像预处理与外矩形提取
rgb1_to_gray (Image, GrayImage)
emphasize (GrayImage, ImageEmphasize, 7, 7, 10)
threshold (ImageEmphasize, Region, 80, 255)
connection (Region, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, 'holes_num', 'and', 0, 10)
gen_contour_region_xld (SelectedRegions, Contours, 'border')
select_shape_xld (Contours, SelectedXLD, 'area', 'and', 1000, 30000)
smallest_rectangle2_xld (SelectedXLD, Row, Column, Phi, Length1, Length2)
gen_rectangle2_contour_xld (Rectangle, Row, Column, Phi, Length1, Length2)
rgb1_to_gray
:将彩色图像转换为灰度图像,存储在GrayImage
变量中。emphasize
:对灰度图像进行强调处理,增强图像中的边缘信息,结果存储在ImageEmphasize
变量中。threshold
:对强调后的图像进行阈值分割,将灰度值在 80 到 255 之间的像素分割出来,存储在Region
变量中。connection
:将分割后的区域进行连通区域分析,将相邻的像素合并为一个连通区域,存储在ConnectedRegions
变量中。select_shape
:根据形状特征选择符合条件的连通区域,这里选择孔洞数量在 0 到 10 之间的区域,存储在SelectedRegions
变量中。gen_contour_region_xld
:将选择的区域转换为轮廓,存储在Contours
变量中。select_shape_xld
:根据形状特征选择符合条件的轮廓,这里选择面积在 1000 到 30000 之间的轮廓,存储在SelectedXLD
变量中。smallest_rectangle2_xld
:计算选择的轮廓的最小外接矩形的参数,包括矩形的中心行坐标Row
、列坐标Column
、旋转角度Phi
、长边长度Length1
和短边长度Length2
。gen_rectangle2_contour_xld
:根据最小外接矩形的参数生成矩形轮廓,存储在Rectangle
变量中。
3. 内矩形提取
threshold (GrayImage, Region1, 144, 255)
connection (Region1, ConnectedRegions1)
gen_contour_region_xld (ConnectedRegions1, Contours1, 'border')
select_shape_xld (Contours1, SelectedXLD1, 'area', 'and', 15000, 20000)
smallest_rectangle2_xld (SelectedXLD1, Row1, Column1, Phi1, Length11, Length21)
gen_rectangle2_contour_xld (Rectangle1, Row1, Column1, Phi1, Length11, Length21)
- 这部分代码与外矩形提取的步骤类似,只是阈值分割的阈值范围不同,形状选择的面积范围也不同,用于提取内矩形。
4. 外矩形坐标提取与线段生成
sort_contours_xld (Rectangle, SortedContours, 'upper_left', 'true', 'row')
count_obj (SortedContours, Number)
for j:=1 to Number by 1
select_obj (SortedContours, ObjectSelected, j)
get_contour_xld (ObjectSelected, Row5, Col5)
for i:=0 to 4 by 1
if((Row5[i]-Row[j-1])<0 and Col5[i]-Column[j-1]<0)
LT_X:=Row5[i]
LT_Y:=Col5[i]
elseif ((Row5[i]-Row[j-1])>0 and Col5[i]-Column[j-1]<0)
LB_X:=Row5[i]
LB_Y:=Col5[i]
elseif ((Row5[i]-Row[j-1])<0 and Col5[i]-Column[j-1]>0)
RT_X:=Row5[i]
RT_Y:=Col5[i]
elseif ((Row5[i]-Row[j-1])>0 and Col5[i]-Column[j-1]>0)
RB_X:=Row5[i]
RB_Y:=Col5[i]
endif
endfor
ROW2[0]:=LT_X
COL2[0]:=LT_Y
ROW2[1]:=LB_X
COL2[1]:=LB_Y
ROW2[2]:=RB_X
COL2[2]:=RB_Y
ROW2[3]:=RT_X
COL2[3]:=RT_Y
ROW2[4]:=LT_X
COL2[4]:=LT_Y
ROW.at(j-1):=ROW2
COL.at(j-1):=COL2
endfor
for i:=0 to Number-1 by 1
for j:=1 to 4 by 1
gen_region_line (RegionLines, ROW.at(i)[j],COL.at(i)[j],ROW.at(i)[j-1],COL.at(i)[j-1])
endfor
endfor
sort_contours_xld
:对矩形轮廓进行排序,按照左上角的位置进行排序,结果存储在SortedContours
变量中。count_obj
:统计排序后的轮廓数量,存储在Number
变量中。- 外层
for
循环遍历每个轮廓,通过select_obj
选择当前轮廓,get_contour_xld
获取当前轮廓的顶点坐标。 - 内层
for
循环根据顶点坐标与矩形中心坐标的相对位置,判断顶点是左上角、左下角、右上角还是右下角,并将其坐标存储在相应的变量中。 - 将四个顶点的坐标依次存储在
ROW2
和COL2
数组中,并将这两个数组存储在ROW
和COL
数组中。 - 最后,通过
gen_region_line
根据顶点坐标生成线段区域。
5. 内矩形坐标提取与线段生成
sort_contours_xld (Rectangle1, SortedContours2, 'upper_left', 'true', 'row')
count_obj (SortedContours2, Number2)
for j:=1 to Number2 by 1
select_obj (SortedContours2, ObjectSelected2, j)
get_contour_xld (ObjectSelected2, Row2, Col2)
for i:=0 to 4 by 1
if((Row2[i]-Row1[j-1])<0 and Col2[i]-Column1[j-1]<0)
LT_X2:=Row2[i]
LT_Y2:=Col2[i]
elseif ((Row2[i]-Row1[j-1])>0 and Col2[i]-Column1[j-1]<0)
LB_X2:=Row2[i]
LB_Y2:=Col2[i]
elseif ((Row2[i]-Row1[j-1])<0 and Col2[i]-Column1[j-1]>0)
RT_X2:=Row2[i]
RT_Y2:=Col2[i]
elseif ((Row2[i]-Row1[j-1])>0 and Col2[i]-Column1[j-1]>0)
RB_X2:=Row2[i]
RB_Y2:=Col2[i]
endif
endfor
ROW3[0]:=LT_X2
COL3[0]:=LT_Y2
ROW3[1]:=LB_X2
COL3[1]:=LB_Y2
ROW3[2]:=RB_X2
COL3[2]:=RB_Y2
ROW3[3]:=RT_X2
COL3[3]:=RT_Y2
ROW3[4]:=LT_X2
COL3[4]:=LT_Y2
ROW4.at(j-1):=ROW3
COL4.at(j-1):=COL3
endfor
for i:=0 to Number2-1 by 1
for j:=1 to 4 by 1
gen_region_line (RegionLines2, ROW4.at(i)[j],COL4.at(i)[j],ROW4.at(i)[j-1],COL4.at(i)[j-1])
endfor
endfor
- 这部分代码与外矩形坐标提取和线段生成的步骤类似,用于处理内矩形。
代码延申应用
1. 尺寸测量
在工业生产中,常常需要对零件的尺寸进行精确测量。通过提取外矩形和内矩形的顶点坐标,可以计算出矩形的长、宽、对角线长度等尺寸信息。例如,可以使用以下代码计算外矩形的长和宽:
for i:=0 to Number-1 by 1
width := sqrt((ROW.at(i)[0] - ROW.at(i)[1]) * (ROW.at(i)[0] - ROW.at(i)[1]) + (COL.at(i)[0] - COL.at(i)[1]) * (COL.at(i)[0] - COL.at(i)[1]))
height := sqrt((ROW.at(i)[0] - ROW.at(i)[3]) * (ROW.at(i)[0] - ROW.at(i)[3]) + (COL.at(i)[0] - COL.at(i)[3]) * (COL.at(i)[0] - COL.at(i)[3]))
disp_message (WindowHandle, 'Width: ' + width + ', Height: ' + height, 'window', 12, 12, 'black', 'true')
endfor
这段代码通过计算相邻顶点之间的距离,得到矩形的长和宽,并在窗口中显示测量结果。
2. 位置检测
在自动化生产线上,需要检测零件的位置是否正确。通过比较外矩形和内矩形的中心坐标,可以判断零件的位置是否偏移。例如,可以使用以下代码计算外矩形和内矩形的中心坐标,并判断它们的偏移量是否在允许范围内:
for i:=0 to Number-1 by 1
outer_center_row := (ROW.at(i)[0] + ROW.at(i)[2]) / 2
outer_center_col := (COL.at(i)[0] + COL.at(i)[2]) / 2
inner_center_row := (ROW4.at(i)[0] + ROW4.at(i)[2]) / 2
inner_center_col := (COL4.at(i)[0] + COL4.at(i)[2]) / 2
row_offset := abs(outer_center_row - inner_center_row)
col_offset := abs(outer_center_col - inner_center_col)
if (row_offset > 10 or col_offset > 10)
disp_message (WindowHandle, 'Position offset detected!', 'window', 12, 12, 'red', 'true')
endif
endfor
这段代码通过计算外矩形和内矩形的中心坐标,得到它们的行和列偏移量,并判断偏移量是否超过允许范围。如果超过允许范围,则在窗口中显示位置偏移的提示信息。
3. 缺陷检测
在产品质量检测中,常常需要检测产品表面是否存在缺陷。通过比较外矩形和内矩形的形状和尺寸,可以判断产品是否存在缺陷。例如,如果内矩形的面积明显小于正常范围,可能表示产品存在破损或缺失。可以使用以下代码计算内矩形的面积,并判断是否存在缺陷:
for i:=0 to Number2-1 by 1
area := abs((ROW4.at(i)[0] - ROW4.at(i)[1]) * (COL4.at(i)[0] - COL4.at(i)[3]) - (COL4.at(i)[0] - COL4.at(i)[1]) * (ROW4.at(i)[0] - ROW4.at(i)[3]))
if (area < 12000)
disp_message (WindowHandle, 'Defect detected!', 'window', 12, 12, 'red', 'true')
endif
endfor
这段代码通过计算内矩形的面积,并与正常范围进行比较。如果面积小于正常范围,则在窗口中显示缺陷检测的提示信息。
4. 目标跟踪
在视频监控或机器人视觉中,需要对目标进行跟踪。通过在连续的图像帧中提取外矩形和内矩形的顶点坐标,可以实现目标的跟踪。例如,可以使用以下代码在连续的图像帧中跟踪目标的位置:
while (true)
read_image (Image, 'path/to/next/image.png')
* 重复前面的图像预处理和矩形提取步骤
* ...
for i:=0 to Number-1 by 1
current_center_row := (ROW.at(i)[0] + ROW.at(i)[2]) / 2
current_center_col := (COL.at(i)[0] + COL.at(i)[2]) / 2
if (i > 0)
prev_center_row := (ROW.at(i-1)[0] + ROW.at(i-1)[2]) / 2
prev_center_col := (COL.at(i-1)[0] + COL.at(i-1)[2]) / 2
displacement_row := current_center_row - prev_center_row
displacement_col := current_center_col - prev_center_col
disp_message (WindowHandle, 'Displacement: (' + displacement_row + ', ' + displacement_col + ')', 'window', 12, 12, 'black', 'true')
endif
endfor
endwhile
这段代码在一个无限循环中不断读取下一帧图像,并重复前面的图像预处理和矩形提取步骤。然后,计算当前帧中目标的中心坐标,并与上一帧的中心坐标进行比较,得到目标的位移信息,并在窗口中显示。
5. 图像拼接
在全景图像拼接或多相机图像融合中,需要将多个图像拼接成一个大的图像。通过提取外矩形和内矩形的顶点坐标,可以确定图像之间的重叠区域,从而实现图像的拼接。例如,可以使用以下代码将两个图像进行拼接:
read_image (Image1, 'path/to/image1.png')
read_image (Image2, 'path/to/image2.png')
* 分别对两个图像进行矩形提取和坐标提取
* ...
* 找到重叠区域
overlap_start_row := max(ROW.at(0)[0], ROW4.at(0)[0])
overlap_end_row := min(ROW.at(0)[2], ROW4.at(0)[2])
overlap_start_col := max(COL.at(0)[0], COL4.at(0)[0])
overlap_end_col := min(COL.at(0)[2], COL4.at(0)[2])
* 拼接图像
gen_image_const (MergedImage, 'byte', Width1 + Width2 - (overlap_end_col - overlap_start_col), Height1)
paste_image (Image1, MergedImage, 0, 0)
paste_image (Image2, MergedImage, Width1 - (overlap_end_col - overlap_start_col), 0)
dev_display (MergedImage)
这段代码首先读取两个图像,并分别对它们进行矩形提取和坐标提取。然后,找到两个图像的重叠区域,并根据重叠区域的位置将两个图像拼接成一个大的图像。最后,在窗口中显示拼接后的图像。
6. 姿态估计
在机器人操作或虚拟现实中,需要对物体的姿态进行估计。通过提取外矩形和内矩形的顶点坐标,可以计算物体的旋转角度和位置,从而实现物体的姿态估计。例如,可以使用以下代码计算外矩形的旋转角度:
for i:=0 to Number-1 by 1
dx := COL.at(i)[1] - COL.at(i)[0]
dy := ROW.at(i)[1] - ROW.at(i)[0]
angle := atan2(dy, dx)
disp_message (WindowHandle, 'Rotation angle: ' + angle, 'window', 12, 12, 'black', 'true')
endfor
这段代码通过计算外矩形相邻顶点之间的水平和垂直距离,得到矩形的斜率,进而计算出矩形的旋转角度,并在窗口中显示。