Halcon视觉入门芯片识别
Halcon视觉入门芯片识别
需求
有如下图的一个摆盘,摆盘的方格中摆放芯片,一个格子中只放一个,我们需要知道每个方格中是否有芯片去指导我们将芯片放到空的方格中。
分析
通过图片分析得出
我们感兴趣的区域在中间,每个方格大小类似
芯片大小相同
方格是白色的
方格有100个,方格外的不识别
解决问题的思路
建立ROI关注每一个方格
需要提取方格的特征
单独分析每一个方格,如果方格中存在芯片(通过特征分析芯片是否存在),则标注出来,如果不存在,标注为红色,代表是空的,可以摆放芯片。
在编写代码之前,大体思路出来了,需要的是尝试各种参数得到我们想要的结果。
代码如下:
dev_close_window ()
dev_open_window (0, 0, 1200, 850, 'black', WindowHandle)
dev_clear_window()
* read_image (Image, 'D:/temp/chips/20220110112001.jpg')
* read_image (Image, 'D:/temp/chips/20220110112121.jpg')
* read_image (Image, 'D:/temp/chips/20220110112137.jpg')
read_image (Image, 'D:/temp/chips/Image_20211227154621354.bmp')
*使用canny算子提取亚像素边界
edges_sub_pix (Image, Edges, 'canny', 10, 20, 40)
*根据边长和面积提取xld
select_shape_xld (Edges, SelectedXLD, ['rect2_len1','area'], 'and', [70,18000], [100,33000])
*根据xld绘出区域
gen_region_contour_xld (SelectedXLD, Region, 'margin')
*根据区域面积提取芯片盒子
select_shape (Region, ChipsImage, ['width','height'], \
'and', [155,155], [170,170])
*合并区域
union1 (ChipsImage, RegionUnion)
*填充区域
fill_up (RegionUnion, ROI_0)
*根据联合区域裁剪感兴趣区域
reduce_domain (Image, ROI_0, ImageReduced)
*联通分割区域
connection (ImageReduced, ConnectedRegions)
*将每个芯片方格转换成标准矩形
shape_trans (ConnectedRegions, RegionTransRect, 'rectangle1')
*排序
sort_region (RegionTransRect, SortedRegions, 'character', 'true', 'row')
*总孔位数量
count_obj (SortedRegions, Number)
*结果数组,0 代表没有 1代表有芯片
result:= []
*空位的数量
EmptyCount :=0
dev_display (Image)
for Index := 1 to Number by 1
* 找到索引对应的区域
select_obj (SortedRegions, ALL_ROI_OF_SINGLES, Index)
*裁剪区域
reduce_domain (Image, ALL_ROI_OF_SINGLES, ChipBoxRegion)
*动态阈值分割
binary_threshold (ChipBoxRegion, ChipRegion, 'max_separability', 'dark', UsedThreshold)
*反向选择
difference (ChipBoxRegion, ChipRegion, ChipWithLine)
*联通区域
connection (ChipWithLine, ConnectedRegions1)
*根据面积特征筛选区域
select_shape (ConnectedRegions1, SelectedRegions, 'area', 'and', 1000.59, 3001.78)
*腐蚀
erosion_circle (SelectedRegions, ChipErosion, 2)
*再次通过面积筛选
select_shape (ChipErosion, ChipInside, 'area', 'and', 1000, 3000)
*腐蚀
erosion_circle (ChipInside, ChipErosion1, 2)
*计算赛选个数
count_obj (ChipErosion1, chipExists)
if (chipExists == 0)
*判断不存在芯片的逻辑
EmptyCount := EmptyCount +1
*显示用
dev_update_on()
dev_set_color ('red')
dev_set_draw ('fill')
dev_display (ALL_ROI_OF_SINGLES)
dev_update_off()
endif
if(chipExists == 1)
*判断有芯片的逻辑
*显示用
dev_update_on()
dev_set_color ('green')
dev_set_draw ('fill')
shape_trans (ChipErosion1, RegionTrans, 'rectangle2')
dev_display (RegionTrans)
dev_update_off()
endif
result[Index-1] := chipExists
endfor
disp_message (WindowHandle, ' Holes Number:'+Number + ' , Exists Count:' + (Number - EmptyCount) , 'window', 12, 12, 'black', 'true')
下面开始分析代码
dev_close_window ()
dev_open_window (0, 0, 1200, 850, 'black', WindowHandle)
dev_clear_window()
* read_image (Image, 'D:/temp/chips/20220110112001.jpg')
* read_image (Image, 'D:/temp/chips/20220110112121.jpg')
* read_image (Image, 'D:/temp/chips/20220110112137.jpg')
read_image (Image, 'D:/temp/chips/Image_20211227154621354.bmp')
这段代码,看算子名称就知道意思。关闭窗口;打开窗口,然后清空窗口;最后读取一张图片。读取的图片就是上图展示的图片。
*使用canny算子提取亚像素边界
edges_sub_pix (Image, Edges, 'canny', 10, 20, 40)
*根据边长和面积提取xld
select_shape_xld (Edges, SelectedXLD, ['rect2_len1','area'], 'and', [70,18000], [100,33000])
这里是有两个概念 亚像素 和 xld
亚像素 因为图片的最小单位是像素,亚像素是比像素还小的显示方式,但只是在显示上做的区分,并不是可以精确到亚像素。
xld 可以理解成轮廓
edges_sub_pix (Image, Edges, 'canny', 10, 20, 40)
Image 是输入;Edges 是输出, canny,10,20,40 是控制参数
为什么是这4个控制参数,可以参考F1
这里 只有 10 是 经过调整的参数,其他参数是默认值,数值越小精细度越高,细节也就越多,需要处理的数据也就越多。当它是1 的时候如图:
当它是10的时候如图:
当参数是10的时候,我们需要的轮廓已经出来了,然后通过边长(rect2_len1)和面积(area)过滤出我们需要的区域,如图:
可以看到,干扰不多了,接下来我们要将xld 绘制成区域,用到算子 gen_region_contour_xld
*根据xld绘出区域
gen_region_contour_xld (SelectedXLD, Region, 'margin')
这里可以看到,上一步的结果,是这一步的输入,这是Halcon编程的基本套路。第二个参数是输出参数,也就是我们得到的区域,第三个参数是绘制方式,这里用到了margin 当然也可以用fill,得到的结果是这样的:
然后再根据面积过滤掉不要的对象,说的是面积,实际上用到的是 宽(width),高(height)
*根据区域面积提取芯片盒子
select_shape (Region, ChipsImage, ['width','height'], \
'and', [155,155], [170,170])
这里的参数 155-170 是通过特征窗口得到的,如图:
然后就是这样的结果了,如图:
*合并区域
union1 (ChipsImage, RegionUnion)
*填充区域
fill_up (RegionUnion, ROI_0)
*根据联合区域裁剪感兴趣区域
reduce_domain (Image, ROI_0, ImageReduced)
*联通分割区域
connection (ImageReduced, ConnectedRegions)
*将每个芯片方格转换成标准矩形
shape_trans (ConnectedRegions, RegionTransRect, 'rectangle1')
*排序
sort_region (RegionTransRect, SortedRegions, 'character', 'true', 'row')
*总孔位数量
count_obj (SortedRegions, Number)
这段代码就很好理解了,合并区域(union1),为什么要合并呢,因为这些区域是分散的,我们需要建立一个感兴趣区域,然后对它进行裁剪,所以需要将他们合并在一起。合并后我做了一个填充也就是 (fill_up),填充后是这样的:
裁剪( reduce_domain (Image, ROI_0, ImageReduced) )后是这样的:
接下来就是连通区域(connection),将他们分割,然后 转成标准矩形(shape_trans),排序(sort_region),统计个数(count_obj),这样我们就得到了每个方格。
这里说一下排序,排序是为了让它从左到右从上到下方便我们遍历。
*结果数组,0 代表没有 1代表有芯片
result:= []
*空位的数量
EmptyCount :=0
这里定义了一个数组存放结果,定义了一个遍历存放空位。接下来就是开始遍历分析每一个方格对象。
这里说一下,这个语法跟pascal语法很像,之前有做过Delphi项目,所以这个语法我也没有专门学过,做到哪里查到哪里,有不足的地方还请指正。
for Index := 1 to Number by 1
* 找到索引对应的区域
select_obj (SortedRegions, ALL_ROI_OF_SINGLES, Index)
*裁剪区域
reduce_domain (Image, ALL_ROI_OF_SINGLES, ChipBoxRegion)
*动态阈值分割
binary_threshold (ChipBoxRegion, ChipRegion, 'max_separability', 'dark', UsedThreshold)
*反向选择
difference (ChipBoxRegion, ChipRegion, ChipWithLine)
*联通区域
connection (ChipWithLine, ConnectedRegions1)
*根据面积特征筛选区域
select_shape (ConnectedRegions1, SelectedRegions, 'area', 'and', 1000.59, 3001.78)
*腐蚀
erosion_circle (SelectedRegions, ChipErosion, 2)
*再次通过面积筛选
select_shape (ChipErosion, ChipInside, 'area', 'and', 1000, 3000)
*腐蚀
erosion_circle (ChipInside, ChipErosion1, 2)
*计算赛选个数
count_obj (ChipErosion1, chipExists)
if (chipExists == 0)
*判断不存在芯片的逻辑
EmptyCount := EmptyCount +1
endif
if(chipExists == 1)
*判断有芯片的逻辑
endif
result[Index-1] := chipExists
endfor
这里说几个算子 select_obj 可以选择某个区域,select_obj (SortedRegions, ALL_ROI_OF_SINGLES, Index)
第一个参数是所有区域的集合,第二个参数是输出的区域,也就是我们通过索引得到的区域,第三个参数是索引
这里是索引是从1开始的,要格外注意
接下来就继续裁剪,根据每个小方格,继续裁剪,如图:
因为动态阈值分割的结果是这样的,如图:
它选择了我们不要的内容,所以需要求反。用到了 difference (ChipBoxRegion, ChipRegion, ChipWithLine) 。这里第一个参数是整体,第二个参数是阈值分割的结果,第三个参数是求差之后的结果,也就是我们需要的结果,如图:
此时,按照惯例,处理边缘的干扰,如果得到的区域是有内容的,说明是有芯片的,如果没有内容,说明没有放芯片。
*联通区域
connection (ChipWithLine, ConnectedRegions1)
*根据面积特征筛选区域
select_shape (ConnectedRegions1, SelectedRegions, 'area', 'and', 1000.59, 3001.78)
*腐蚀
erosion_circle (SelectedRegions, ChipErosion, 2)
*再次通过面积筛选
select_shape (ChipErosion, ChipInside, 'area', 'and', 1000, 3000)
*腐蚀
erosion_circle (ChipInside, ChipErosion1, 2)
*计算赛选个数
count_obj (ChipErosion1, chipExists)
连通区域,通过面积过滤,然后 腐蚀,腐蚀的目的是去掉周边的干扰。这个数值 2 也是经过多次尝试得到的。
然后再次通过面积筛选,去掉干扰。继续腐蚀,也是为了去掉干扰。这里的逻辑会根据实际情况发生变化。
最后计数( count_obj (ChipErosion1, chipExists) )。如果chipExists ==1 说明有芯片,否则就是没有芯片。
然后根据 chipExists的值进行判断处理即可。我这里的处理方式是标注,如图:
红色代表是空的,如果不是空的,我们将芯片的区域绘制成绿色。
总结
在编程的过程中,感受到视觉识别是一门多方面配合的工作,如果光线情况比较好,精度高很多时候可以让我们节省很多处理步骤。所以,视觉识别不单单是编程的问题。如果能在前期将光打好,后期可以节省很多事情,提高处理效率。
最后如果本文有不对的地方,欢迎指正。
Halcon视觉入门芯片识别的更多相关文章
- halcon视觉入门钢珠识别
halcon视觉入门钢珠识别 经过入门篇,我们有了基础的视觉识别知识.现在加以应用. 有如下图片: 我们需要识别图片中比较明亮的中间区域,有黑色的钢珠,我们需要知道他的位置和面积. 分析如何识别 编写 ...
- halcon视觉入门扫盲篇
halcon视觉入门扫盲篇 前言 在公司让我研究视觉的时候,我是两眼一抹黑的.之前完全没有接触过视觉.综合权衡后选择了Halcon,使用的是HDevelop 13 (64-bit). ...
- 使用Python+OpenCV进行图像处理(二)| 视觉入门
[前言]图像预处理对于整个图像处理任务来讲特别重要.如果我们没有进行恰当的预处理,无论我们有多么好的数据也很难得到理想的结果. 本篇是视觉入门系列教程的第二篇.整个视觉入门系列内容如下: 理解颜色模型 ...
- 没事别想不开做Halcon视觉工程师 halcon机器视觉如何学习?
今天我们来听听看来自一个机器视觉工程师的唠叨和吐槽,在这之后,你还想学人工智能,还想学机器视觉?恭喜你,你对人工智能机器视觉是真爱了! 既然自己选择了这条路,那么无论前进路上有多坎坷,跪着也要走完. ...
- HALCON之喷码OCR识别案例
一个喷码识别的案例 1 read_image (Image, 'D:/用户目录/Desktop/2.png') 2 3 rgb1_to_gray(Image, Image) 4 5 get_image ...
- HALCON视觉算子相关函数中文说明System(2)
16.6 Parameters get_system_ 功能:根据HALCON系统参数获取关于当前的信息. set_system 功能:HALCON系统参数的设置. 16.7 Serial cle ...
- 3 TensorFlow入门之识别手写数字
------------------------------------ 写在开头:此文参照莫烦python教程(墙裂推荐!!!) ---------------------------------- ...
- Tesseract-ocr视觉学习-验证码识别及python import pytesseract使用
Tesseract-OCR的简单使用与训练 最近看到某个网站提交数据要提交验证码,用tesseract自带的识别, 识别出来是什么鬼,0-9识别成了什么玩意! so决定自己训练下tesseract.. ...
- appium入门元素识别参考
https://www.cnblogs.com/miniren/p/7365885.html#top
随机推荐
- POJ 1986:Distance Queries
Distance Queries Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 18139 Accepted: 6248 ...
- uniapp以及微信小程序中scroll-view隐藏滚动条 自定义滚动条
隐藏滚动条 1.全局隐藏滚动条,在app.vue中 ::-webkit-scrollbar{ display: none; } 2.局部隐藏藏滚动条 样式没有使用scoped属性时, 否则无效. .u ...
- 读书笔记markdown模板
读书笔记 书名 作者 出版社 阅读日期 书籍背景 书摘/ 笔记 批注 总结& 收获 读完每一本书,把书中的知识转化为「自己的智慧」,才是最扎实的收获- 他山之石 摘录相关精彩书评-
- IM2603 Type-C扩展坞电源管理 IC
IM2603 概述 用于带有集成降压转换器的 Type-C 外围应用的电源管理 IC IM2603 是一款主要用于 Type-C 外围应用的电源管理 IC. 它集成了一个带有内置高侧 MOSFET 的 ...
- BUUCTF [极客大挑战 2019]Not Bad
总的来说这是一个64位orw的题 开头先在主函数里分配了一个很大的空间 1 __int64 __fastcall main(int a1, char **a2, char **a3) 2 { 3 mm ...
- 【MySQL作业】MySQL函数——美和易思数学函数和控制流函数应用习题
点击打开所使用到的数据库>>> 1.添加一条商品记录. 商品编码 goodsCode 商品名 goodsName 种类 category 单价 unitPrice 02005 夏 ...
- MySQL创建数据库 easyShopping,包括area表、goods表、customer表、orders表、ordersdetall表、test表
MySQL创建数据库 easyShopping,包括area表.goods表.customer表.orders表.ordersdetall表.test表 商品表表结构: 字段名 说 明 类 型 长 度 ...
- SQL Server 添加字段,修改字段类型,修改字段名,删除字段
-- 1.添加字段-- 基本语法alter table 表 add 列名 字段类型 null-- 例:给学生表添加Telephone字段并指定类型为vachar,长度为50,可空alter table ...
- Java链式调用
疯狂的类构造器Builder模式,链式调用 以swagger配置类为例 比如需要返回一个ApiInfo 的类,可以直接new一个ApiInfo,这样一般需要把参数都填上 或者,可以使用他的builde ...
- SpringBoot集成MyBatis-Plus自定义SQL
1.说明 本文介绍Spring Boot集成MyBatis-Plus框架后, 基于已经创建好的Spring Boot工程, 添加自定义的SQL实现复杂查询等操作. 自定义SQL主要有两种方式, 一种是 ...