Halcon视觉入门芯片识别

需求

有如下图的一个摆盘,摆盘的方格中摆放芯片,一个格子中只放一个,我们需要知道每个方格中是否有芯片去指导我们将芯片放到空的方格中。

分析

通过图片分析得出

  1. 我们感兴趣的区域在中间,每个方格大小类似

  2. 芯片大小相同

  3. 方格是白色的

  4. 方格有100个,方格外的不识别

解决问题的思路

  1. 建立ROI关注每一个方格

  2. 需要提取方格的特征

  3. 单独分析每一个方格,如果方格中存在芯片(通过特征分析芯片是否存在),则标注出来,如果不存在,标注为红色,代表是空的,可以摆放芯片。

在编写代码之前,大体思路出来了,需要的是尝试各种参数得到我们想要的结果。

代码如下:


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视觉入门芯片识别的更多相关文章

  1. halcon视觉入门钢珠识别

    halcon视觉入门钢珠识别 经过入门篇,我们有了基础的视觉识别知识.现在加以应用. 有如下图片: 我们需要识别图片中比较明亮的中间区域,有黑色的钢珠,我们需要知道他的位置和面积. 分析如何识别 编写 ...

  2. halcon视觉入门扫盲篇

    halcon视觉入门扫盲篇 前言     在公司让我研究视觉的时候,我是两眼一抹黑的.之前完全没有接触过视觉.综合权衡后选择了Halcon,使用的是HDevelop 13 (64-bit).      ...

  3. 使用Python+OpenCV进行图像处理(二)| 视觉入门

    [前言]图像预处理对于整个图像处理任务来讲特别重要.如果我们没有进行恰当的预处理,无论我们有多么好的数据也很难得到理想的结果. 本篇是视觉入门系列教程的第二篇.整个视觉入门系列内容如下: 理解颜色模型 ...

  4. 没事别想不开做Halcon视觉工程师 halcon机器视觉如何学习?

    今天我们来听听看来自一个机器视觉工程师的唠叨和吐槽,在这之后,你还想学人工智能,还想学机器视觉?恭喜你,你对人工智能机器视觉是真爱了! 既然自己选择了这条路,那么无论前进路上有多坎坷,跪着也要走完. ...

  5. HALCON之喷码OCR识别案例

    一个喷码识别的案例 1 read_image (Image, 'D:/用户目录/Desktop/2.png') 2 3 rgb1_to_gray(Image, Image) 4 5 get_image ...

  6. HALCON视觉算子相关函数中文说明System(2)

    16.6  Parameters get_system_ 功能:根据HALCON系统参数获取关于当前的信息. set_system 功能:HALCON系统参数的设置. 16.7  Serial cle ...

  7. 3 TensorFlow入门之识别手写数字

    ------------------------------------ 写在开头:此文参照莫烦python教程(墙裂推荐!!!) ---------------------------------- ...

  8. Tesseract-ocr视觉学习-验证码识别及python import pytesseract使用

    Tesseract-OCR的简单使用与训练 最近看到某个网站提交数据要提交验证码,用tesseract自带的识别, 识别出来是什么鬼,0-9识别成了什么玩意! so决定自己训练下tesseract.. ...

  9. appium入门元素识别参考

    https://www.cnblogs.com/miniren/p/7365885.html#top

随机推荐

  1. ZOJ 3785:What day is that day?(数论)

    What day is that day? Time Limit: 2 Seconds Memory Limit: 65536 KB It's Saturday today, what day is ...

  2. Spring企业级程序设计 • 【第2章 Spring Bean管理进阶】

    全部章节   >>>> 本章目录 2.1 bean标签和import标签 2.1.1 标签中的id属性和name属性 2.1.2 Bean的作用范围和生命周期 2.1.2 Be ...

  3. Android开发布局 案例一

    权重:就是在布局界面中所占的比例 实践案例: <?xml version="1.0" encoding="utf-8"?> <LinearLa ...

  4. 【】Apache Ranger剖析:Hadoop生态圈的安全管家

    前言 2016年,Hadoop迎来了自己十周岁生日.过去的十年,Hadoop雄霸武林盟主之位,号令天下,引领大数据技术生态不断发展壮大,一时间百家争鸣,百花齐放.然而,兄弟多了不好管,为了抢占企业级市 ...

  5. vuex从后台数据后页面已完成渲染无法显示数据的解决办法

    一.在store中state定义一个变量 来控制是否显示 二.在完成数据获取后把isShow设为true 三.把state状态映射到页面的computed中 四.在模板中使用v-if来判断是否显示 来 ...

  6. webSocket 前端 js 加入 心跳机制 的基本写法

    1前言 websocket 一般 每隔 90 秒无操作则会自动断开  ,需要加入一个心跳机制 来防止 自断 2. 实验过程 (1)设定一个jsp 或html 文件都行 ,加入元素 (2)js 源码 , ...

  7. vue2.0中实现echarts图片下载-----书写中

    由于各个版本浏览器兼容性不一,所以,我们需要一个判断浏览器类型的函数来对不同的浏览器做不同的处理. 获取浏览器版本的函数 // 判断浏览器类型 IEVersion () { let userAgent ...

  8. 信息收集&Fuzz

    本文译自https://0xjoyghosh.medium.com/information-gathering-scanning-for-sensitive-information-reloaded- ...

  9. 原生JS获取网页宽高

    网页可见区域宽: document.body.clientWidth 网页可见区域高: document.body.clientHeight 网页可见区域宽: document.body.offset ...

  10. ADD software version display

    ADD software version display ADD software version display1. Problem Description2. Analysis3. Solutio ...