The value of Zmotion is to bring customers more success!
此前,我们依次讲解了软硬件介绍及计数实例、相机的基本使用、基于形状匹配的视觉定位、BLOB有无检测、测量尺寸、机器视觉方案中使用到的标定功能、ZDevelop软件实现识别条形码和二维码,测量点/直线/圆以及划痕检测功能。
本期课程我们和大家一起使用ZDevelop软件实现机器视觉中使用到的OCR功能。
《视频教材:VPLC系列机器视觉运动控制一体机快速入门(十)》
一 OCR 检测原理
什么是OCR?
OCR全称是Optical Character Recognition,即光学字符识别。在机器视觉中OCR是指使用图像处理方法将获取到的图像上的黑白字符进行分割训练后,识别并输出对应的字符。
OCR的检测原理?
在机器视觉中进行OCR检测时,首先根据字符(前景)和背景的对比度以及字符的宽度和高度等参数将字符进行分割后提取字符图像,然后对提取的字符逐一进行训练学习,将所有可能出现的字符进行训练学习后存储到训练库中,在执行识别检测时,将当前提取到的字符和训练库中的字符进行对比,输出相似度满足条件的字符结果。
二 OCR 应用场景
1.验证字符准确性
可以验证待测产品上印刷字符的准确性。
2.获取产品字符信息
可以获取待测产品上的字符信息上传数据库或录入系统。
3.信息对比
可以将待测产品识别出的字符和条码输出的信息进行对比,判断字符印刷和条码信息是否一致。
三 软件实现
(一)流程图
(二)实例演示
1.打开ZDevelop软件:新建项目→新建HMI文件→新建main.bas文件,用于编写界面响应函数→新建global_variable.bas文件用于存放全局变量并开启HMI自动运行任务→新建InitParam.bas文件用于初始化测量参数→新建camera.bas文件用于实现相机采集功能→新建draw.bas文件用于更新绘制图形刷新界面→文件添加到项目。
2.设计HMI界面。
3.在global_variable.bas文件中定义全局变量,定义完成后运行Hmi.hmi文件。
'''''全局变量大部分使用数组结构'''''
''注:basic编程中很多函数会以TABLE(系统的数据结构)做为参数
''在这里table均是做为中间变量
''table 21-22,鼠标按键,控件坐标系
''table 31-35,旋转矩ROI参数,cx、cy、width、height、angle,控件坐标系
''table 41-45,旋转矩形控件坐标转换后对应的图像坐标,图像坐标系
'主任务状态
'0 - 未初始化
'1 - 停止
'2 - 运行中
'3 - 正在停止
GLOBAL DIM main_task_state
main_task_state = 1
'采集开关
'0 - 停止采集
'1 - 请求采集
GLOBAL DIM grab_switch
grab_switch = 0
'相机个数
GLOBAL cam_num
cam_num = 0
'相机种类,"zmotion;mvision;basler;mindvision;huaray"
GLOBAL DIM CAMERA_TYPE(16)
CAMERA_TYPE = "mvision"
' 定义主任务id - 10
GLOBAL DIM main_task_id
main_task_id = 10
'定义连续采集任务id - 9
GLOBAL DIM grab_task_id
grab_task_id = 9
'定义全局图像变量
GLOBAL ZVOBJECT grabImg '采集图像
'定义常用颜色变量
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 nameSample(32),result(32)
'旋转矩形ROI参数:cx、cy、width、height、angle
GLOBAL DIM d_roi_rect2(5)
'检测参数:阈值模式、阈值参数、最小字符面积、最大字符面积、最小字符宽度、最大字符宽度、最小字符高度、最大字符高度、字符极性、形态学类型、结构元宽、结构元高、字符最小间距
GLOBAL DIM d_detect_param(13)
'***********定义读取本地文件功能相关变量**************
''注意,该功能只在使用仿真器时有效
'定义是否使用本地图片标志
GLOBAL DIM d_use_imgfile
d_use_imgfile=1
'定义本地图片索引
GLOBAL DIM d_index
d_index=0
'定义读取图片的路径
GLOBAL DIM File_Name(100)
'***********结束定义读取本地文件功能相关变量**********
'运行HMI文件
RUN "Hmi1.hmi",1
4.在InitParam.bas文件中初始化测量参数。
end
GLOBAL SUB init_detect_param()'初始化测量参数
'初始化roi参数
d_roi_rect2(0) = 160.0 'roi中心x
d_roi_rect2(1) = 120.0 'roi中心y
d_roi_rect2(2) = 80.0 'roi宽
d_roi_rect2(3) = 60.0 'roi高
d_roi_rect2(4) = 0.0 'roi角度
TABLE(31) = d_roi_rect2(0) '将roi变量赋值给起始地址为31的table数组
TABLE(32) = d_roi_rect2(1)
TABLE(33) = d_roi_rect2(2)
TABLE(34) = d_roi_rect2(3)
TABLE(35) = d_roi_rect2(4)
'初始化检测参数:阈值模式、阈值参数、最小字符面积、最大字符面积、最小字符宽度、最大字符宽度、最小字符高度、最大字符高度、字符极性、形态学类型、结构元宽、结构元高、字符最小间距
d_detect_param(0) = 0 '手动阈值
d_detect_param(1) = 187 '低阈值
d_detect_param(2) = 500 '最小字符面积
d_detect_param(3) = 80000 '最大字符面积
d_detect_param(4) = 10 '最小字符宽度
d_detect_param(5) = 500 '最大字符宽度
d_detect_param(6) = 10 '最小字符高度
d_detect_param(7) = 500 '最大字符高度
d_detect_param(8) = 1 '白底黑字
d_detect_param(9) = 1 '开运算
d_detect_param(10) = 1 '结构元宽,正奇数
d_detect_param(11) = 10 '结构元高,正奇数
d_detect_param(12) = 1 '字符最小间距
nameSample="" '初始化训练字符样本为空字符串
result=" " '初始化字符结果变量为空字符串
END SUB
5.关联HMI界面值显示控件变量。
具体变量关联操作可参考视频中相关内容或获取程序代码进行参考。
6.在main.bas文件中添加HMI界面初始化函数并在Hmi系统设置中关联初始化函数。
'HMI界面初始化函数
GLOBAL SUB hmi_init()
grab_switch = 0 '初始化采集开关为停止采集
main_task_state = 1 '初始化主任务状态为停止状态
'初始化时依据图像分辨率设置区域的裁剪尺寸,此处图像分辨率为1280x960
ZV_RESETCLIPSIZE(1280, 960)
'设置锁存通道0的大小,以适应图片元件控件大小
ZV_LATCHSETSIZE(0, HMI_CONTROLSIZEX(10, 1), HMI_CONTROLSIZEY(10, 1))
ZV_SETSYSINT("LineWidth",5) '设置绘制画笔宽度为5个像素
init_detect_param() '初始化测量参数
ZV_LATCHCLEAR(0) '清空锁存通道0
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_SEL(0) '选择扫描到的第一个相机进行操作
CAM_SETEXPOSURE(5000) '设置相机曝光时间为5000us
CAM_SETMODE(0) '设置软件触发模式
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="\10\"+TOSTR(d_index,1,0)+".bmp" '.../flash/10/目录下的图片所在的路径名称
ZV_IMGREAD(grabImg,File_Name,1)
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.在draw.bas文件中添加检测ROI更新绘制函数,并在自定义元件属性窗口中关联刷新函数和绘制函数。
end
'和绘制(即选择ROI)有关的界面的刷新绘制函数放在这个bas文件里
DIM is_redraw '绘图标志,0表示未进行绘制,1表示正在进行绘制
is_redraw = 0
DIM hit_pos '定义调整操作标志
'根据鼠标操作更新ROI位置区域函数
GLOBAL SUB update_roi()
if mouse_scan(21) = 1 then '扫描鼠标按下操作
'只有按下时可以改变击中位置,获取鼠标点击位置对应的击中区域编号
hit_pos = ZV_HMIADJRECT2(table(21), table(22), 31, -1)
is_redraw = 1 '绘图标志置1
endif
if mouse_scan(21) = -1 then '扫描鼠标松开操作
'根据区域编号调整roi区域位置
ZV_HMIADJRECT2(table(21), table(22), 31, hit_pos)
is_redraw = 1 '绘图标志置1
endif
if (MOUSE_state(21)) then '鼠标按下时
'根据区域编号调整roi区域位置
ZV_HMIADJRECT2(table(21), table(22), 31, hit_pos)
is_redraw = 1 '绘图标志置1
endif
if (1 = is_redraw) then '如果绘制标志=1
is_redraw = 0 '绘图标志置0
'控件roi坐标转图像roi坐标,控件坐标存放在起始地址为111的数组,图像坐标存放在起始地址为50的数组
ZV_POSTOIMG(0, 1, 31, 41)
d_roi_rect2(0) = TABLE(41)'将图像坐标的数据赋值给ROI变量
d_roi_rect2(1) = TABLE(42)
d_roi_rect2(2) = ZV_LENTOIMG(0, TABLE(33))
d_roi_rect2(3) = ZV_LENTOIMG(0, TABLE(34))
d_roi_rect2(4) = TABLE(35)
SET_REDRAW '重新绘制全部区域
endif
END SUB
'ROI区域更新后实时绘制ROI区域
GLOBAL SUB draw_roi()
SET_COLOR(C_BLUE) '设置画笔颜色为蓝色
ZV_HMIRECT2(31, 300) '将旋转矩形roi分解为HMI支持的绘图图元并添加控制参数,便于HMI绘图显示
DRAWLINE(TABLE(300), TABLE(301), TABLE(302), TABLE(303)) '绘制外矩形
DRAWLINE(TABLE(302), TABLE(303), TABLE(304), TABLE(305))
DRAWLINE(TABLE(304), TABLE(305), TABLE(306), TABLE(307))
DRAWLINE(TABLE(306), TABLE(307), TABLE(300), TABLE(301))
DRAWLINE(TABLE(308), TABLE(309), TABLE(310), TABLE(311)) '绘制方向箭头
DRAWLINE(TABLE(312), TABLE(313), TABLE(310), TABLE(311))
DRAWLINE(TABLE(314), TABLE(315), TABLE(310), TABLE(311))
END SUB
9.在main.bas文件中添加HMI界面按下【提取训练】按钮时响应的函数并关联动作函数名。
'HMI界面按下提取字符按钮时响应的函数
GLOBAL SUB btn_segment()
'定义检测需要用到的变量:分割参数、样本图片库、训练样本库、OCR分类器、结果图片
GLOBAL ZVOBJECT param,sample,trainSample,ocr,colorImg
'根据界面输入参数生成分割参数
ZV_OCRSEGSETPARAM_(param,d_detect_param(0),d_detect_param(1),d_detect_param(2),d_detect_param(3),d_detect_param(4),d_detect_param(5),d_detect_param(6),d_detect_param(7),d_detect_param(8),d_detect_param(9),d_detect_param(10),d_detect_param(11),d_detect_param(12))
'根据分割参数和检测区域进行字符分割,分割的字符样本图片存放到sample变量中
ZV_OCRSEGCHAR_(grabImg,param,sample,d_roi_rect2(0),d_roi_rect2(1),d_roi_rect2(2),d_roi_rect2(3),d_roi_rect2(4))
'获取样本数量,存放到table(100)中
ZV_OCRSAMPLECNT_(sample,100)
'灰度图转换到RGB图,作为结果图像
ZV_GRAYTORGB(grabImg, colorImg)
if(TABLE(100)>0) then '如果识别到的字符数量大于0
for i=0 to TABLE(100)-1'根据识别到的字符次数进行循环
ZV_OCRSAMPLERECT2_(sample,i,150)'获取每个样本字符的最小外接矩阵,用于显示分割结果
'将样本的最小外接矩阵绘制到界面中
ZV_RECT2(colorImg,table(150),table(151),table(152),table(153),table(154),C_GREEN)
next
elseif (TABLE(100)=0) then '如果未分割提取到字符
?"未成功分割提取出字符!" '打印提示并退出执行
return
endif
ZV_LATCH(colorImg,0)'在锁存通道0中显示结果图像
if (STRCOMP(nameSample, "")=0)then '如果输入的训练字符样本是空的
?"请先输入训练字符样本再重新分割训练!" '打印提示
return '返回子函数,不往下执行
endif
'清空OCR分类器
ZV_CLEAR(ocr)
if(ZV_ISEMPTY(trainSample)=0) then '如果训练样本库不为空
ZV_OCRSAMPLECLR_(trainSample)'在训练字符之前先清空样本库
endif
'根据输入的训练库字符生成训练样本库
ZV_OCRSAMPLEAPP_(sample,trainSample,nameSample)
'使用训练样本库训练分类器,0.8为优化参数(惩罚系数),表示分类器的预测能力, 越小预测能力越强但越容易出错,范围[0,1],建议 0.8;
'0.15为径向基核参数,表示非线性映射能力,值越大非线 性映射能力就越强,范围[0,1],建议 0.15
ZV_OCRTRAINSVM_(trainSample,ocr,0.8,0.15)
if(ZV_ISEMPTY(ocr)=0) then '如果分类器训练成功
?"字符分割训练已完成!" '打印成功提示
else
?"训练字符和分割提取的字符不匹配!"
endif
END SUB
10.在main.bas文件中添加HMI界面按下【识别】按钮时响应的函数并关联动作函数名。
'HMI界面按下识别按钮时响应的函数
GLOBAL SUB btn_test()
result=" " '清空字符结果变量
'根据界面输入参数生成分割参数
ZV_OCRSEGSETPARAM_(param,d_detect_param(0),d_detect_param(1),d_detect_param(2),d_detect_param(3),d_detect_param(4),d_detect_param(5),d_detect_param(6),d_detect_param(7),d_detect_param(8),d_detect_param(9),d_detect_param(10),d_detect_param(11),d_detect_param(12))
'根据分割参数和检测区域进行字符分割,分割的字符样本存放到sample变量中
ZV_OCRSEGCHAR_(grabImg,param,sample,d_roi_rect2(0),d_roi_rect2(1),d_roi_rect2(2),d_roi_rect2(3),d_roi_rect2(4))
'获取分割到的字符数量,存放到table(100)中
ZV_OCRSAMPLECNT_(sample,100)
if(TABLE(100)=0) then '如果未分割提取到字符
?"未成功分割提取出字符!" '打印提示并退出执行
return
endif
'使用训练样本和分类器识别字符并输出字符结果到起始地址为50的变量中
ZV_OCRCLASSIFYSVM_(ocr,sample,32,50)
'获取识别到的字符数量,存放到table(100)中
ZV_OCRSAMPLECNT_(sample,100)
'灰度图转换到RGB图,作为结果图像
ZV_GRAYTORGB(grabImg, colorImg)
if(TABLE(100)>0) then '如果识别到的字符数量大于0
for i=0 to TABLE(100)-1'将字符结果循环赋值给结果变量显示到界面中
result(i)=TABLE(50+i)
ZV_OCRSAMPLERECT2_(sample,i,150)'获取每个样本字符的最小外接矩阵,用于显示分割结果
'将样本的最小外接矩阵绘制到界面中
ZV_RECT2(colorImg,table(150),table(151),table(152),table(153),table(154),C_GREEN)
next
endif
ZV_LATCH(colorImg,0)'在锁存通道0中显示结果图像
END SUB
11.在main.bas文件中添加【运行】按钮响应的函数并关联动作函数。
'HMI界面按下运行按钮时响应的函数
GLOBAL SUB btn_run()
if(2 = main_task_state) then '如果主任务处于运行中状态
?"已经开启连续运行任务,请勿重复操作!" '打印提示
return
endif
if (1 = main_task_state) then '如果主任务处于停止状态
if (0 = PROC_STATUS(main_task_id)) then'如果程序中任务未开启
main_task_state = 2 '将主任务状态设置为运行中状态
RUNTASK main_task_id, main_task'开启主任务
endif
endif
END SUB
'主任务执行的函数
main_task:
while(1)
'如果主任务状态处于正在停止状态即停止按钮按下时
if (3 = main_task_state) then
main_task_state = 1'将主任务状态设置为停止状态
exit while '退出循环
endif
'否则循环执行采集和识别函数
btn_grab()
btn_test()
wend
END
12.在main.bas文件中添加【停止】按钮响应的函数并关联动作函数。
'HMI界面按下停止按钮时响应的函数
GLOBAL SUB btn_stop()
if (2 = main_task_state) then '如果主任务状态处于2即任务运行中时
main_task_state = 3 '将主任务状态置为3,退出循环
endif
END SUB
(三)仿真演示效果
本次,正运动技术VPLC系列机器视觉运动控制一体机快速入门(十)——OCR功能,就分享到这里。
更多精彩内容请关注“正运动小助手”公众号,需要相关开发环境与例程代码,请咨询正运动技术销售工程师:400-089-8936。
本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。