1. 简单聊一聊测量的那些事
- 测量为什么也能做缺陷检测的工作呢?仔细想一想,这不是废话吗?缺陷其中就包含了被测物的尺寸等问题,长短粗细、高矮胖瘦、间隙宽窄,包括我以前做过的平面度、平行度、轮廓度等测量项目,都可以归结为缺陷检测类项目。
- 就拿测量平面度项目来说吧,使用点激光按照图纸上标出的测量点位测量所有点的高度,结合轴坐标(或者直接使用工件坐标),我们就得到了一组三维点,轴坐标(或工件坐标)是X,Y,而点激光测到的值(高度值)作为Z,这就是空间中的一个点(X,Y,Z);
- 最小二乘法计算平面方程,当然建立基准平面(计算平面方程)的点要按照图纸要求来做;
- 然后计算所有测量点到平面的距离,其实这个距离是有正负号的,分布在平面上面的为正,在平面下的为负,Z轴的方向由点激光的数值变化决定。最高的点减去最低的点就是平面度;
- 假设我们得到平面的平面度是0.04mm,而工件要求的平面度是0.03mm±0.01mm,那这个也算是勉强达标了,这不正是缺陷检测——测量拟合的整个过程吗?
- 所以说缺陷检测的项目是很宽泛的,它的解决方案也绝不只有机器视觉一种,对不同的项目选择不同的方案,往往会起到事半功倍的效果。这也正是工程师需要修炼的内功。
2. 做几个测量判断缺陷的项目
案例1. 测量液体线高度
measure_fill_level.hdev
- 通过测量液体线的位置来判断液体是装多了还是装少了;
- 测量线的位置使用的是测量矩形;
- 测量矩形使用形状模板匹配定位跟随测量的;
- 这里使用了一个新的算子
Talk is cheap. Show me your code.
Halcon代码:
dev_get_window (WindowHandle)
set_display_font (WindowHandle, 15, 'mono', 'true', 'false')
list_files ('./', ['files','follow_links'], ImageFiles)
tuple_regexp_select (ImageFiles, ['\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$','ignore_case'], ImageFiles)
* 创建模板
read_image (Image, ImageFiles[0])
get_image_size (Image, Width, Height)
gen_rectangle1 (ModelRegion, 264, 54, 321, 100)
reduce_domain (Image, ModelRegion, TemplateImage)
create_shape_model (TemplateImage, 3, rad(-5), rad(10), 'auto', ['none','no_pregeneration'], 'use_polarity', [25,54,4], 4, ModelID)
get_shape_model_contours (ModelContours, ModelID, 1)
for T := 0 to |ImageFiles|-1 by 1
read_image (Image, ImageFiles[T])
* 寻找实例
find_shape_model (Image, ModelID, rad(-5), rad(10), 0.7, 0, 0.5, 'least_squares', [3,1], 0.75, Row, Column, Angle, Score)
MeanRows:=mean(Row)
Length1:=52
Length2:=20
gen_measure_rectangle2 (0, 0, rad(90), Length1, Length2,Width, Height, 'nearest_neighbor', MeasureHandle)
* 设置两条参考线
MeasureRow:=MeanRows-180
standard:=120//标准液线
offset:=20//允许液线偏移量
RefLineHigh:=standard-offset//高液线
RefLineLow:=standard+offset//低液线
dev_set_color ('cyan')
dev_set_line_width (1)
set_line_style (WindowHandle, 10)
gen_contour_polygon_xld (ContourLineHigh, [RefLineHigh,RefLineHigh], [0,Width])
gen_contour_polygon_xld (ContourLineLow, [RefLineLow,RefLineLow], [0,Width])
gen_contour_polygon_xld (ContourStand, [standard,standard], [0,Width])
for I := 0 to |Score| - 1 by 1
* 将测量矩形移动到测量位置
dev_set_line_width (3)
set_line_style (WindowHandle, 0)
* 完成了与仿射变换相同的功能,将测量矩形从左上角点平移到了目标测量位置
translate_measure (MeasureHandle, MeasureRow, Column[I])
measure_pos (Image, MeasureHandle, 2.6, 7, 'all', 'all', RowEdge, ColumnEdge, Amplitude, Distance)
if(|RowEdge|>0)
if(RowEdge<RefLineHigh)
dev_set_color ('red')
disp_message (WindowHandle, '超出'+(RefLineHigh-RowEdge), 'image', RowEdge, ColumnEdge-30, 'black', 'false')
elseif(RowEdge>RefLineLow)
dev_set_color ('red')
disp_message (WindowHandle, '低出'+(RowEdge-RefLineLow), 'image', RowEdge, ColumnEdge-30, 'black', 'false')
else
dev_set_color ('green')
endif
gen_contour_polygon_xld (Contour, [RowEdge,RowEdge], [ColumnEdge-24,ColumnEdge+24])
endif
endfor
stop()
endfor
案例2. 检测矩形通孔的缺陷
fit_rectangle2_contour_xld.hdev
halcon在方法-边缘提取(亚像素精度中)为我们提供了这样一个案例:
- 图中的矩形区域是工件上的矩形冲压通孔;
- 通过仔细观察发现显示NG的孔上边缘有缺陷,具体表现就是边缘不齐整,向下突出了一块,我们就是要把这样的边缘上的缺陷检查出来;
- 于是我们就自然想到了:提取矩形的实际轮廓xld,再拟合一个标准的轮廓xld,利用dist_rectangle2_contour_points_xld 这个算子求实际轮廓与理论轮廓点对点的距离,只要这个距离超过了我们的设定值,就认为边缘有缺陷了。而且还可以根据距离的大小和超出设定距离的点的数量来评价这个缺陷的严重程度;
- 但是需要注意的是,矩形孔的角不是绝对的直角,实际上它有一点圆角,但是这个并不认为是缺陷,所以再检测的时候才屏蔽了角点附近的几个点,屏蔽的点数与圆角的半径有关。
halcon代码:
dev_update_off ()
dev_set_draw ('margin')
dev_get_window (WindowHandle)
set_display_font (WindowHandle, 36, 'mono', 'true', 'false')
* 读图
read_image (Image, './punched_holes.png')
* 抠图
binary_threshold (Image, Region, 'max_separability', 'light', UsedThreshold)
boundary (Region, RegionBorder, 'inner')
dilation_circle (RegionBorder, RegionDilation, 5.5)
reduce_domain (Image, RegionDilation, ImageReduced)
* 边缘提取
edges_sub_pix (ImageReduced, Edges, 'canny', 2, 20, 40)
* 拟合
fit_rectangle2_contour_xld (Edges, 'regression', -1, 0, 0, 3, 2, Row, Column, Phi, Length1, Length2, PointOrder)
gen_rectangle2_contour_xld (Rectangles, Row, Column, Phi, Length1, Length2)
* 缺陷检测
count_obj (Rectangles, Number)
dev_display (Image)
for Index := 1 to Number by 1
select_obj (Edges, ObjectSelected, Index)
get_contour_xld (ObjectSelected, Rows, Cols)//获得xld坐标
select_obj (Rectangles, FitRectangle, Index)
get_contour_xld (FitRectangle, RowC, ColC)
* 分析
D1 := sqrt((Rows - RowC[0]) * (Rows - RowC[0]) + (Cols - ColC[0]) * (Cols - ColC[0]))
D2 := sqrt((Rows - RowC[1]) * (Rows - RowC[1]) + (Cols - ColC[1]) * (Cols - ColC[1]))
D3 := sqrt((Rows - RowC[2]) * (Rows - RowC[2]) + (Cols - ColC[2]) * (Cols - ColC[2]))
D4 := sqrt((Rows - RowC[3]) * (Rows - RowC[3]) + (Cols - ColC[3]) * (Cols - ColC[3]))
DistCorner := min2(min2(D1,D2),min2(D3,D4))
* 求轮廓到拟合轮廓的对点距离
dist_rectangle2_contour_points_xld (ObjectSelected, 0, Row[Index-1], Column[Index-1], Phi[Index-1], Length1[Index-1], Length2[Index-1], Distances)
* 判断
dev_display (FitRectangle)
Result:=[]
for J := 0 to |Distances|-1 by 1
if(DistCorner[J]>7 and Distances[J]>1)//排除了角点附近的点
Result:=[Result,0]
else
Result:=[Result,1]
endif
endfor
if(min(Result)==0)
disp_message (WindowHandle, 'NG', 'image', Row[Index-1], Column[Index-1], 'red', 'false')
else
disp_message (WindowHandle, 'OK', 'image', Row[Index-1], Column[Index-1], 'green', 'false')
endif
endfor
dev_update_on ()