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. Notepad++汉化教程

    Notepad++汉化方法总结 Notepad++系统只带了中文语言包,不需要像其他软件一样破解 打开Notepad++(通过文本文件右键选择以Notepad++打开或者找到Notepad++的快捷方 ...

  2. 计算机图形学——梁友栋-Barsky算法

    梁算法是计算机图形学上最经典的几个算法,也是目前唯一一个以中国人命名的出现在国内外计算机图形学课本的算法,我之前在介绍裁剪算法的时候介绍过这个算法 https://www.cnblogs.com/wk ...

  3. 物联网大赛 - Android学习笔记(二)Andriod 应用界面编程

    学习目标: Android的程序界面和View组件 View组件和ViewGroup组件 常见的布局管理器 文本框组件TextView和EditView 按钮组件Button 和ImageButton ...

  4. 射频FEM介绍

    FEM介绍 1. 什么是FEM 1.1 FEM简介 FEM,Front-end Modules,即就是前端模块.硬件电路中的前端模块完成射频信号的发送放大以及接收放大(with bypass).滤波, ...

  5. Unity——ShaderLab基础

    1.格式 Shader "Custom/MyShader" //命名+右键创建shader路径 { //属性必须在代码里声明才能使用 Properties{ //属性,会出现在in ...

  6. Java基础(八)——IO流5_其他流

    一.其他 1.System.in.System.out(标准输入.输出流) System.in:标准的输入流,默认从键盘输入. System.out:标准的输出流,默认从控制台输出. 改变标准输入输出 ...

  7. pod运行到指定node节点

    利用labels 1.一般来说都每个节点有自己特有的labels  比如 2.利用nodeSelector nodeSelector: kubernetes.io/hostname: master3 ...

  8. Eclipse提速优化方法

    Eclipse提速优化方法 Eclipse运行太慢了,尤其是导入大型maven项目的时候经常动不动就down掉,于是总结了一下Eclipse提速的方法. 1.关闭不常用的工程 打开的项目太多,会严重影 ...

  9. 来自MyBatis不一样收获结果的探索之旅-v3.5.9

    概述 定义 MyBatis官网 https://mybatis.org/mybatis-3/ 最新版本为3.5.9 MyBatis是一个的ORM框架,支持自定义SQL.存储过程和高级映射.MyBati ...

  10. unittest_认识unittest(1)

    unittest是python内置的单元测试框架,具备编写用例.组织用例.执行用例.输出报告等自动化框架的条件. 使用unittest前需要了解该框架的五个概念: 即test case,test su ...