在实际的机器视觉应用项目中,常常需要提取产品的轮廓信息进行进一步的加工处理。在本次的课程中,我们将使用ZDevelop软件来演示提取目标轮廓的功能。
《教学视频:机器视觉运动控制一体机应用例程(四)提取目标轮廓》
一 检测原理及应用场景
(一)检测原理
提取轮廓也是基于边缘轮廓的算法。它是在输入的单通道图像中,获取梯度值大于设置的梯度阈值、连接的轮廓长度大于最小轮廓长度的所有轮廓点并输出轮廓点对应的位置坐标数据。
(二)应用场景
1.提取产品的轮廓特征进行加工,如鞋底点胶,根据特定形状裁切布艺等。
2.提取产品的外轮廓作为检测区域对产品进行外观检测。
二 软件演示
(一)流程图
(二)实例演示
1.打开ZDevelop软件:新建项目→新建HMI文件→新建main.bas文件,用于编写界面响应函数→新建global_variable.bas文件用于定义和初始化全局变量并开启HMI自动运行任务→新建camera.bas文件用于实现相机采集功能→新建draw.bas文件用于更新绘制图形刷新界面→文件添加到项目。
2.设计HMI启动界面。
3.在global_variable.bas文件中定义并初始化全局变量,定义完成后运行Hmi.hmi文件。
'''''全局变量大部分使用数组结构'''''
''注:basic编程中很多函数会以TABLE(系统的数据结构)做为参数
''table 说明 table 说明
''11~12 鼠标操作时获取的坐标 15~18 提取轮廓ROI图像坐标数据
''25~28 提取轮廓ROI控件坐标数据 40 轮廓点数量
''50~50+轮廓数量*2 指定轮廓的轮廓点坐标
'***********定义程序任务相关变量**********************
'主任务状态
'0 - 未初始化
'1 - 停止
'2 - 运行中
'3 - 正在停止
GLOBAL DIM main_task_state
main_task_state = 1
'运行任务开关
GLOBAL DIM run_switch
run_switch = 0
'采集任务开关
'0 - 停止采集
'1 - 请求采集
GLOBAL DIM grab_switch
grab_switch = 0
'定位检测主任务id - 10
GLOBAL DIM main_task_id
main_task_id = 10
'相机连续采集线程id - 7
GLOBAL DIM grab_task_id
grab_task_id = 7
'***********结束定义程序任务相关变量******************
'***********定义相机采集相关变量**********************
'相机种类,此处使用海康相机-"mvision"
GLOBAL DIM CAMERA_TYPE(100)
'CAMERA_TYPE = "mindvision;basler;mvision;huaray;zmotion"
CAMERA_TYPE = "mvision"
'相机个数
GLOBAL cam_num
cam_num = 0
'相机模式,-1 连续采集,0-软件触发采集
GLOBAL cam_mode
cam_mode = 0
'***********结束定义相机采集相关变量******************
'定义使用ROI标志,1-使用ROI,0-使用全图像区域
GLOBAL DIM d_roi_arc_flag
d_roi_arc_flag = 0
'定义鼠标按下标志位,1-已按下,0-未按下
GLOBAL DIM is_set_roi_m_down
is_set_roi_m_down = 0
GLOBAL DIM d_detect_time '定义消耗的时间变量
d_detect_time = 0
'定义程序执行过程中采集的图像变量、二值化图像变量、显示图像变量、提取到的轮廓列表
GLOBAL ZVOBJECT grabImg,binImg,colorImg,contlist
'定义最小轮廓长度
GLOBAL DIM minLength
minLength=1700 '默认提取轮廓最小长度为1700像素
'定义提取到轮廓的数量
GLOBAL DIM count
count=0
'定义保存参数标志
GLOBAL DIM d_is_saved
'定义提取轮廓的ROI区域
GLOBAL DIM d_learn_roi(4)
d_learn_roi(0)=180
d_learn_roi(1)=110
d_learn_roi(2)=380
d_learn_roi(3)=310
TABLE(25) = d_learn_roi(0) '将矩形ROI数据存放到起始地址为25的table数组中
TABLE(26) = d_learn_roi(1)
TABLE(27) = d_learn_roi(2)
TABLE(28) = d_learn_roi(3)
'常用颜色变量
GLOBAL C_RED, C_GREEN, C_BLUE, C_YELLOW
C_RED = RGB(255, 0, 0)
C_GREEN = RGB( 0,255, 0)
C_BLUE = RGB( 0, 0,255)
C_YELLOW= RGB(255,255, 0)
'***********定义读取本地文件功能相关变量**************
''注意,该功能只在使用仿真器时有效
'定义是否使用本地图片标志
GLOBAL DIM d_use_imgfile
d_use_imgfile=1
'定义本地图片索引
GLOBAL DIM d_index
'定义读取图片的路径
GLOBAL DIM File_Name(100)
'***********结束定义读取本地文件功能相关变量**********
'初始化全局变量完成后开启HMI文件
RUN"Hmi1.hmi",1
4.关联HMI启动界面值控件变量。
5.在main.bas文件中添加HMI界面初始化函数并在Hmi系统设置中关联初始化函数。
end
'注:
'凡是要使用Region有关的算子在系统初始化时都要调用ZV_RESETCLIPSIZE(width, height)这个算子设置下图像尺寸,以满足相机分辨率,因为默认的是640*480尺寸
'HMI界面初始化函数
GLOBAL SUB hmi_init()
grab_switch = 0 '停止采集
main_task_state = 1 '主任务停止运行
if(VR(7)=1)then '如果已保存参数
btn_LoadParam()
endif
ZV_RESETCLIPSIZE(1280, 960)'初始化时依据图像分辨率设置区域的裁剪尺寸,此处图像分辨率为1280x960
ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(10, 1), HMI_CONTROLSIZEY(10, 1)) '设置锁存的大小
ZV_SETSYSDBL("CamGetTimeout", 1000) '设置采集超时
ZV_LATCHCLEAR(0) '清空锁存通道0
END SUB
'加载保存参数子程序
GLOBAL SUB btn_LoadParam()
'加载保存的参数
minLength=VR(0)
d_roi_arc_flag=VR(1)
d_learn_roi(0)=VR(2)
d_learn_roi(1)=VR(3)
d_learn_roi(2)=VR(4)
d_learn_roi(3)=VR(5)
d_use_imgfile =VR(6)
TABLE(25) = VR(9)
TABLE(26) = VR(10)
TABLE(27) = VR(11)
TABLE(28) = VR(12)
END SUB
6.在draw.bas文件中添加检测ROI更新绘制函数,并在自定义元件属性窗口中关联刷新函数和绘制函数。
end
'和绘制(即选择ROI)有关的界面的刷新绘制函数放在这个bas文件里
DIM is_redraw
is_redraw = 0
DIM hit_pos,sr_mpos_x ,sr_mpos_y
'根据鼠标操作更新训练颜色样本的有效区域
GLOBAL SUB update_roi()
if d_roi_arc_flag = 1 then '如果选择ROI类型为矩形
if mouse_scan(11) = 1 then '扫描鼠标按下操作
is_set_roi_m_down = 1 '鼠标按下标志置1
sr_mpos_x = table(11) '将当前鼠标按下位置的坐标赋值给变量
sr_mpos_y = table(12)
'只有按下时可以改变击中位置,获取鼠标点击位置对应的击中区域编号
hit_pos = ZV_HMIADJRECT(sr_mpos_x, sr_mpos_y, 25, -1)
is_redraw = 1 '绘图标志置1
endif
if mouse_scan(11) = -1 then '扫描鼠标松开操作
is_set_roi_m_down = 0 '鼠标按下标志置0
sr_mpos_x = table(11) '将当前鼠标松开位置的坐标赋值给变量
sr_mpos_y = table(12)
'根据区域编号调整定位器区域位置
ZV_HMIADJRECT(sr_mpos_x, sr_mpos_y, 25, hit_pos)
is_redraw = 1 '绘图标志置1
endif
'如果鼠标按下时
if (is_set_roi_m_down and MOUSE_state(11)) then
sr_mpos_x = table(11) '将当前鼠标按下位置的坐标赋值给变量
sr_mpos_y = table(12)
'根据区域编号调整定位器区域位置
ZV_HMIADJRECT(sr_mpos_x, sr_mpos_y, 25, hit_pos)
is_redraw = 1 '绘图标志置1
endif
if (1 = is_redraw) then '如果绘制标志=1
is_redraw = 0 '将绘制标志置0
'控件roi坐标转图像roi坐标,控件坐标存放在起始地址为25的数组,图像坐标存放在起始地址为15的数组
ZV_POSTOIMG(0, 2, 25, 15)
'将图像坐标的数据赋值给ROI变量中
d_learn_roi(0) = TABLE(15)
d_learn_roi(1) = TABLE(16)
d_learn_roi(2) = TABLE(17)
d_learn_roi(3) = TABLE(18)
SET_REDRAW '重新绘制全部区域
endif
else
SET_REDRAW
endif
END SUB
'根据更新的鼠标位置坐标绘制训练颜色样本区域
GLOBAL SUB draw_roi()
if d_roi_arc_flag = 1 then '如果ROI类型为矩形
SET_COLOR(C_BLUE) '设置绘制时画笔的颜色为蓝色
'根据控件坐标数据绘制矩形
DRAWRECT(TABLE(25), TABLE(26), TABLE(27), TABLE(28))
local cx,cy '定义局部变量
cx = (TABLE(25) + TABLE(27)) / 2 '计算矩形的中心坐标x、y
cy = (TABLE(26) + TABLE(28)) / 2
DRAWLINE(cx-5, cy, cx+5, cy) '绘制中心十字线
DRAWLINE(cx, cy-5, cx, cy+5)
endif
END SUB
7.在camera.bas文件中添加HMI界面中采集相关按钮响应的函数并关联动作函数。
end
'主界面按下扫描相机按钮时响应的函数
GLOBAL SUB cam_scan_all()
if(d_use_imgfile=1)then
?"请先按下使用本地图片按钮关闭该功能"
return
endif
ZV_SETSYSINT("LogLevel", 7) '设置控制器信息
ZV_SETSYSSTR("DataDir","")
CAM_SCAN(CAMERA_TYPE) '扫描相机,CAMERA_TYPE="mvision"
cam_num = CAM_COUNT() '获取扫描到的相机数量
if (0 = cam_num) then '如果相机数量=0,打印提示信息
? "未找到相机"
return '退出子函数,不往下执行
endif
?"cam_num = " cam_num '如果扫描到相机,打印相机数量
cam_mode = 0 '设置软触发采集
CAM_SEL(0) '选择扫描到的第一个相机进行操作
CAM_SETEXPOSURE(5000) '设置相机曝光时间为5000us
CAM_SETMODE(cam_mode) '设置软件触发模式
CAM_START(0) '开启相机
END SUB
'主界面按下单次采集按钮执行的函数
GLOBAL SUB btn_grab()
'如果d_use_imgfile=1时使用读取本地图片功能,使用控制器时请将此部分代码注释掉
if (d_use_imgfile=1) then
if(d_index=3) then
d_index=0
endif
File_Name="\提取轮廓\"+TOSTR(d_index,1,0)+".bmp" '.../flash/提取轮廓/目录下的图片所在的路径名称
ZV_IMGREAD(grabImg,File_Name,0)
ZV_LATCH(grabImg, 0)
d_index=d_index+1
return
endif
''读取本地图片功能结束
'如果相机数量为0,提示先扫描相机,并退出子函数不往下执行
if cam_num = 0 then
?"请先扫描相机!"
return
endif
CAM_SETPARAM("TriggerSoftware", 0) '发送触发指令
CAM_GET(grabImg, 0) '获取一帧图像存放到grabImg变量中
ZV_LATCH(grabImg, 0) '将图像显示到锁存通道0中
END SUB
'主界面按下连续采集按钮响应的函数
GLOBAL SUB btn_cgrab()
if grab_switch =1 then '如果已经处于连续执行状态,打印提示信息并退出函数
?"正在连续运行中,请勿重复操作!"
return
endif
if( d_use_imgfile =0) then '如果使用相机采集功能
if cam_num = 0 then '判断如果相机数量=0,打印提示信息并退出函数
?"请先扫描相机!"
return
endif
endif
grab_switch = 1 '采集任务开关置1
if (1 = grab_switch) then
if (0 = PROC_STATUS(grab_task_id)) then
RUNTASK grab_task_id, grab_task '开启连续采集任务
endif
endif
END SUB
'采集任务实现函数
grab_task:
while(1)
if (0 = grab_switch) then '如果采集任务开关=0即停止采集按钮按下时
exit while '退出循环
endif
'grab_switch=1时重复执行以下操作
btn_grab()'单次采集按钮响应的函数
wend
END
'主界面按下停止采集按钮响应的函数
GLOBAL SUB btn_stopCgrab()
if grab_switch =0 then '如果已经处于停止采集状态,打印提示信息并退出函数
?"未开启连续采集!"
return
endif
grab_switch = 0 '将采集任务开关置0
END SUB
8.在main.bas文件中添加HMI界面按下【保存】按钮时响应的函数并关联动作函数名。
'HMI界面按下保存按钮时响应的函数
GLOBAL SUB btn_SaveParam()
VR(0)=minLength
VR(1)=d_roi_arc_flag
VR(2)=d_learn_roi(0)
VR(3)=d_learn_roi(1)
VR(4)=d_learn_roi(2)
VR(5)=d_learn_roi(3)
VR(6)=d_use_imgfile
VR(9)=TABLE(25)
VR(10)= TABLE(26)
VR(11)=TABLE(27)
VR(12)=TABLE(28)
d_is_saved=1
VR(7)=d_is_saved
?"已成功保存参数"
END SUB
9.在main.bas文件中添加HMI界面按下【测试】按钮响应的函数并关联动作函数。
'定义HMI界面按下测试按钮时响应的函数
GLOBAL SUB btn_test()
TICKS=0
ZVOBJECT regionMask,contour,genList,fragment
'生成ROI区域
if d_roi_arc_flag = 1 then '如果选择的ROI类型是矩形
'根据ROI数据生成旋转矩形区域
ZV_REGENRECT(regionMask,d_learn_roi(0),d_learn_roi(1),d_learn_roi(2)-d_learn_roi(0)+1,d_learn_roi(3)-d_learn_roi(1)+1) '生成矩形测量区域
else
'生成全图像区域
ZV_REGENFULLIMG(grabImg,regionMask)
endif
ZV_CLEAR(contlist)
ZV_CONTGENSUBPIX(grabImg, regionMask,contlist,120,220,minLength) '从有效区域中提取最小轮廓长度为minLength的边缘轮廓,并将提取的结果存于 list 列表中
ZV_GRAYTORGB(grabImg,colorImg)'将灰度图转换到RGB图像,用于绘制检测结果图像
count = ZV_LISTCOUNT(contlist) '获取列表中的数量
for i=0 to count-1
ZV_LISTGET(contlist,contour,i) '获取列表中序号为i的元素
ZV_CONTCOUNT(contour,40) '获取轮廓点的数量
ZV_CONTOUR(colorImg,contour,zv_color(0,255,0))
' '获取轮廓点数据方法1,获取所有轮廓点数据
' for j=0 to TABLE(40)-1
' ZV_CONTGETPT(contour,j,50) '指定轮廓中第i个点的坐标放到 TABLE (50)中
' next
'获取轮廓点数据方法2,将轮廓分割成基元
ZV_CLEAR(genList)
ZV_CONTSEGMENT(contour,genList,0,1,1) '将轮廓分割成直线基元
DIM num
num=ZV_LISTCOUNT(genList)
for j=0 to num-1
ZV_LISTGET(genList,fragment,j) '获取列表中序号为j的元素
ZV_CONTGETPARAM(fragment,64,50)'获取轮廓基元类型和直线基元参数,包括基元类型,直线起始坐标x、y终点坐标x,y
ZV_LINE(colorImg,TABLE(51),TABLE(52),TABLE(53),TABLE(54),C_BLUE)'绘制轮廓分割后提取的效果
next
next
ZV_LATCH(colorImg,0)
d_detect_time=abs(TICKS)
END SUB
10.在main.bas文件中添加HMI界面按下【运行】按钮响应的函数并关联动作函数。
'主界面点击运行按钮时响应的函数
GLOBAL SUB btn_run()
if(run_switch = 1) then '如果已经开启连续运行
?"已开启连续运行,请勿重复操作!" '提示信息并退出子函数,不往下执行
return
endif
run_switch = 1 '主任务开关置1
if (1 = run_switch) then '如果主任务开关=1
if (0 = PROC_STATUS(main_task_id)) then '如果main_task_id任务未开启
RUNTASK main_task_id, main_task '开启main_task任务
endif
endif
END SUB
'主任务执行的内容
main_task:
while(1)
if (0 = run_switch) then '如果主任务开关=0即停止运行按钮按下时
exit while '退出循环
endif
'否则重复执行以下操作
'执行单次采集响应函数获取一帧图像
btn_grab()
'执行提取轮廓子程序
btn_test()
wend
END
11.在main.bas文件中添加HMI启动界面按下【停止】按钮响应的函数并关联动作函数。
'主界面点击停止按钮时响应的函数
GLOBAL SUB btn_stop()
if(run_switch = 0) then '如果主任务开关=0
?"未开启连续运行!" '提示未开启循环任务,并退出子函数不往下执行
return
endif
run_switch = 0 '主任务开关置0,退出循环
END SUB
本次,正运动技术机器视觉运动控制一体机应用例程(四)——提取目标轮廓,就分享到这里。