(五)Halcon缺陷检测实例

一、所需软件环境

1、资源分享

(1)Halcon软件

  Halcon的安装包可以看百度网盘链接:链接:https://pan.baidu.com/s/1bqi7XebbIB_SeqoyKNXqbA
提取码:3opf
 每个月初都需要更新licence,licence可以参考这位博主的博文【这位博主是位大好人,要向他学习】:

https://blog.csdn.net/qq_18620653/article/details/120033443?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170079289416800192261338%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170079289416800192261338&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-5-120033443-null-null.142v96pc_search_result_base1&utm_term=halcon%20licence&spm=1018.2226.3001.4187

  这个是安装好之后的效果。
在这里插入图片描述

(2)原图和代码分享

资源链接:https://pan.baidu.com/s/1u63SRK-hQlUWejbgLyvzIw
提取码:403v

                有问题可以随时沟通

二、缺陷细分

1、原图

在这里插入图片描述
  这个就是我们这篇文章中想要处理的图片,我们的目的是取提取中间黑色圆形边缘的缺口和毛刺部分。

2、毛刺

 中间圆形是空的,外围是金属区域,而如下图所示就是突出毛刺的展示图片。
在这里插入图片描述

3、缺口

 毛刺就是凸出来的,那么反之,凹进去的自然就是缺口,如下图所示是缺口的展示图片:
在这里插入图片描述

三、Halcon检测

1、思路引导

 检测的流程其实非常简单,先得到一个阈值分割中心黑色圆的结果,然后拟合一个标准圆。用标准圆与黑色圆作差,就可以得到毛刺或者缺口了。
 逻辑是这样的:
  毛刺=拟合圆-黑色圆
  缺口=黑色圆-拟合圆
  这样的计算结果虽然不算严谨,但作为一个初方案还是可以的。

 理清楚逻辑,我们就要思考,拟合圆该怎么得到了。直接用黑色圆进行gen_circle吗,那如果黑色圆阈值分割不太好呢?那么标准圆就变得不太标准了。
 所以我采用精定位的方法,精定位一般用在尺寸测量上,需要建立模板,再添加模板。
 当然在编写算法之前,要先理清楚顺序。
(1)添加参数
(2)阈值分割,得到黑色圆 
(2)根据黑色圆的边缘,建立精定位模板,调节参数,得到标准圆,
(4)根据两个圆和输入图片,得到毛刺
(5)同理得到缺口
(6)根据所需的筛选条件,对毛刺和缺口进行筛选。
(7)输出缺陷,

2 、第一步:导入参数

  每个公司应该都会有自己封装好的联系算子和参数,这些算子的作用主要是用来与后续的函数联系。
  这家公司所用到的算子都是自己封装的,全部以fs开头的,有些还是与自己公司所用到的软件进行联系的,我就不多说了。

简单列出我所导入的参数:
在这里插入图片描述
 这里我就简单地说一下就行,因为这些函数都需要导入三级算子,就算给你们也用不了。

3、main函数中的内容

*需要仿真的图片路径
Path:='E:/工作------------------------------------working/11-09日工作内容/注液孔'
Path:='Z:/工作working————————工作/11-12日工作内容/的撒的撒多撒多撒/注液孔'
Path:='Z:/工作working————————工作/11-12日工作内容/的撒的撒多撒多撒/nn'
Path:='Z:/工作working————————工作/注液孔模型全部最新/注液孔缺陷测试1'
* Path:='E:/工作------------------------------------working/11-09日工作内容/demo'
* Path:='C:/Users/haopengli/Desktop/demo'
list_files (Path, ['files','follow_links'], ImageFiles)
tuple_regexp_select (ImageFiles, ['\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$','ignore_case'], ImageFiles)

***针对模板图像或者数据的预处理,预处理exec_preprocess在加载模板时执行,得到中间变量可以传递到exec_inspect检测函数中执行
*Halcon与FSWing进行对接的标准函数,每个算法都需要提供这样定义的函数,FSWing自动构建该函数的调用
*preProcessHandleList是Halcon自有句柄的集合,用于在exec_inspect中使用,不允许删除
preProcessHandleList:=[]
CDataHandle:=[]
*预处理函数接口
exec_preprocess (CDataHandle, FSDataHandle, preProcessHandleList)
fs_datahandle_setsimulatestatus (FSDataHandle, 1)

gen_empty_obj (DefectRegions)
for Index := 0 to |ImageFiles| - 1 by 1
    read_image (SrcImage, ImageFiles[Index])
    
    * 缺陷图显示设置
    dev_close_window ()
    get_image_size (SrcImage, WidthI, HeightI)
    Width:=500
    Height:=Width*HeightI/WidthI
    dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
    dev_set_draw ('margin')
    dev_display (SrcImage)
    **************

    ***针对每一张图像的检测
    *Halcon与FSWing进行对接的标准函数,
    *每个手机算法都需要提供这样定义的函数,
    *FSWing自动构建该函数的调用
    exec_inspect (SrcImage, DefectRegions, CDataHandle, FSDataHandle)
    
    
    *********************************************
    * 以下函数可能有问题,可以自行删除
    * *********************************************
     dev_display (DefectRegions)
     disp_message (WindowHandle, ImageFiles[Index], 'window', 12, 12, 'black', 'true')
     count_obj (DefectRegions, Number)
     if(Number > 0)
         area_center(DefectRegions, Area, Row, Column)
         dev_display (DefectRegions)
         for Index1 := 1 to Number by 1
             gen_circle(Circle, Row[Index1-1], Column[Index1-1], 200.5)
         endfor
         disp_message (WindowHandle,Index +'/'+|ImageFiles|+': '+ 'NG:'+Number, 'window', 32, 12, 'black', 'true')
         stop ()
     endif
     disp_message (WindowHandle,Index +'/'+|ImageFiles|+': '+  'OK', 'window', 32, 12, 'black', 'true')
     if (Index == |ImageFiles|)
         disp_message (WindowHandle, '已跑完所有', 'window', 52, 12, 'black', 'true')
     endif
    
endfor

  主函数中的内容其实非常简单,主要分为以下几点。
 (1)导入参数,使用fs开头的封装算子
 (2)写图片文件夹,因为Halcon检测缺陷一定要让算法具有一定的泛化性,所以仅针对一张图像的缺陷检测是不行的。
 (3)遍历每张图像,通过exec_inspect函数进行缺陷检测。
 (4)显示缺陷。
  那么我们的主要研究的内容就集中在exec函数中了。

4、exec_inspect函数

  在此检测函数中,我们要输出最终的缺陷,所以大部分实现的内容就要在这个函数中完全体现了。
再次理一下之前的思路,我们要在exec_inspect函数中实现的功能有:
(0)导入参数
(1)圆度检测,不够圆的产品视为缺陷。
(2)毛刺检测
(3)缺口检测
(4)缺陷筛选

  我们在这个函数中主要实现这四个功能。
那么我们就可以这样写:

* 系统属性设置
dev_set_draw ('margin')
* *********
fs_productXML_getpara (FSDataHandle, 'burr_area_thr', burr_area_thr, ProductXMLParaType)
fs_productXML_getpara(FSDataHandle, 'burr_length_thr', burr_length_thr, ProductXMLParaType)
fs_productXML_getpara (FSDataHandle, 'gap_area_thr', gap_area_thr, ProductXMLParaType)
fs_productXML_getpara(FSDataHandle, 'gap_length_thr', gap_length_thr, ProductXMLParaType)

* 变量初始化设置
Alg_RunningFlag := 0
try
    fs_reportLog_addLog_tuple (FSDataHandle, 0, ['算法开始执行!'])
    *****************************
    * 一、检测圆度
    *****************************
    exec_circlue_COPY_1 (SrcImage, RegionFillUp, FSDataHandle)
    
    *****************************
    * 二、检测毛刺
    *****************************
    exec_maoci_COPY_1 (RegionFillUp, SrcImage, Region_maoci, Circle, FSDataHandle)
    fs_show_region (Circle, FSDataHandle, 1, '注液孔精定位区域')
    
    *****************************
    * 三、检测缺口
    *****************************
    exec_quekou_COPY_1 (RegionFillUp, Circle, SrcImage, Region_quekou, FSDataHandle)
    
    *****************************
    * 四、缺陷筛选
    *****************************
    fs_reportLog_addLog_tuple (FSDataHandle, 0, ['缺陷筛选开始执行!'])
     DefectionScreen_COPY_1 (Region_maoci, Region_quekou, SrcImage, DefectRegions, FSDataHandle)
    * 缺口筛选
    
    fs_reportLog_addLog_tuple (FSDataHandle, 0, ['算法执行结束!'])
    
    return()
    
catch (Exception)
    *执行失败
    fs_datahandle_settuple (FSDataHandle, 'FS_Exec_State', 0)
    fs_productXML_getinspmethod (FSDataHandle, ProductXMLInspMethod_catch)
    fs_productXML_getinspobjID (FSDataHandle, ProductXMLInspObjID_catch)
    fs_datahandle_gettrayid (FSDataHandle,TrayID_catch, bOK)
    fs_reportLog_addLog_tuple (FSDataHandle, 2, ['Error,'+ProductXMLInspMethod_catch+'算法崩溃,检测对象ID:',ProductXMLInspObjID_catch,',TrayID:',TrayID_catch,Exception])
endtry
return ()

 在这个函数中,我们又用几个函数来实现相对应的功能。
对应关系如下:
在这里插入图片描述

  那么之后的所有工作就是去拆解以上的这些函数了。

5.圆度检测

  圆度检测的步骤其实也非常简单,不过在这个函数中,我们需要将要检测的圆的区域threhold出来,输出的圆的区域我用RegionFillUp来表示,写的函数如下:

fs_productXML_getpara (FSDataHandle, 'ZYK_circularity', ZYK_circularity, ProductXMLParaType)
fs_productXML_getpara (FSDataHandle, 'ZYK_thr', ZYK_thr, ProductXMLParaType)
*******************************************************
* 一、阈值分割,提取区域RegionFillUp
* *******************************************************
threshold (SrcImage, Regions, 0, 120)
connection (Regions, ConnectedRegions1)
select_shape_std (ConnectedRegions1, SelectedRegions, 'max_area', 10)
fill_up (SelectedRegions, RegionFillUp)
opening_circle (RegionFillUp, RegionOpening, 3.5)
connection (RegionOpening, ConnectedRegions)
select_shape_std (ConnectedRegions, RegionFillUp, 'max_area', 10)
region_features (RegionFillUp, 'circularity', Value_cir)
*******************************************************
* 二、检测圆度
*******************************************************
reduce_domain (SrcImage, RegionFillUp, ImageReduced)
threshold (ImageReduced, Regions, ZYK_thr[0], ZYK_thr[1])
closing_circle (Regions, RegionClosing, 20)
opening_circle (RegionClosing, RegionOpening, 50)
closing_circle (RegionOpening, RegionClosing1, 200)
circularity (RegionClosing1, Circularity)
if (|Circularity| > 0)
    if (Circularity<ZYK_circularity)
        fs_control_addID (FSDataHandle, 1, 1, ControlCountID)
        fs_control_setparavaluelist (FSDataHandle, ['注液孔圆度:'+Circularity])
    endif
else
    fs_control_addID (FSDataHandle, 1, 1, ControlCountID)
    fs_control_setparavaluelist (FSDataHandle, ['注液孔形状异常'])
endif
return()

  第一段代码用来阈值分割,选取的阈值范围是0-120,这里可以自行设置,如果想要图像边缘的信息尽可能多的提取出来,那么就可以把右边的阈值设置的大一点,比如200,都是可以的。
  阈值分割之后,执行打散筛选操作,因为图像比较简单,所以直接选取打散之后最大的区域。
在这里插入图片描述
  得到的区域如上图所示。
  随后在第二部分就可以计算得到区域的圆度。
使用

circularity (RegionClosing1, Circularity)

这个算子,就可以计算得到的区域的圆度。
  随后就可以设定一个判断条件,看得到的圆度是否满足我们的需求。
这里我设定的阈值是0.9。

  我设定了两个判断语句。
  先对圆度求模,如果圆度小于0.5,那么它的模就小于0,反之则大于零。
  在对大于0.5的圆度判断,看是否大于0.9,如果小于0.9,也属于异常,那么就可以按照自己的需求,输出错误信息。

6、毛刺检测函数exec_maoci_COPY_1

  在这个函数中,还是有一些工作量的,首先要对阈值分割的圆进行精定位,这里有一个精定位函数jingdiwei()
  我先把代码贴出来:

********************
* 精定位函数 
********************
jingdiwei (RegionFillUp, SrcImage, Circle_jingdingwei, FSDataHandle)
Circle := Circle_jingdingwei
difference (Circle, RegionFillUp, RegionDifference)
********************
* 减法得到毛刺缺陷
********************
* 得到减出区域灰度
gray_features (RegionFillUp, SrcImage, 'mean', gray_circlue)
connection (RegionDifference, ConnectedRegions2)
gray_features (ConnectedRegions2, SrcImage, 'mean', gray_inter)
regular_select_gray := gray_inter[>]170
select_mask_obj (ConnectedRegions2, SelectObject, regular_select_gray)
select_shape (SelectObject, SelectedRegions1, 'area', 'and', 0, 99999)

union1 (SelectedRegions1, Region_maoci)
* 初步筛选,这一步有点问题
connection (Region_maoci, ConnectedRegions4)
select_shape (ConnectedRegions4, SelectedRegions, 'area', 'and', 50, 99999)
opening_circle (Region_maoci, Region_maoci1, 2.5)
dilation_circle (Region_maoci1, RegionDilation, 3.5)
intersection (Region_maoci, RegionDilation, RegionIntersection)
union1 (RegionIntersection, Region_maoci)
return ()

  这里比较关键的就是精定位的函数了,以下是精定位函数中的关键代码。

* 创建精定位模板,加载参数
create_metrology_model (MetrologyHandle)
get_image_size (SrcImage, Width, Height)
set_metrology_model_image_size (MetrologyHandle, Width, Height)
* Add the metrology rectangle objects to the model
* as defined above25
Region_jz_acc_circle := RegionOpening1
* 加载参数
smallest_circle (Region_jz_acc_circle, Row2, Column2, Radius)
add_metrology_object_circle_measure (MetrologyHandle, Row2, Column2, Radius, min_length1, min_length2, .5, measure_threshold, [], [], MetrologyCircleIndices)
set_metrology_object_param (MetrologyHandle, MetrologyCircleIndices, 'measure_transition', measure_transition)
set_metrology_object_param (MetrologyHandle, MetrologyCircleIndices, 'measure_select', measure_select)
set_metrology_object_param (MetrologyHandle, MetrologyCircleIndices, 'min_score', min_score)

apply_metrology_model (SrcImage, MetrologyHandle)

get_metrology_object_result (MetrologyHandle, MetrologyCircleIndices, 'all', 'result_type', 'all_param', CircleParameter)
* 精定位过程
get_metrology_object_measures (Contour, MetrologyHandle, 'all', 'all', Row1, Column1)
gen_cross_contour_xld (Cross, Row1, Column1, 6, 0.785398)
if (|CircleParameter| = 3)
    CircleRow := CircleParameter[0]
    CircleColumn := CircleParameter[0 + 1]
    CircleRadius := CircleParameter[0 + 2]
    gen_circle (Circle_jingdingwei, CircleRow, CircleColumn, CircleRadius)
    fs_reportLog_addLog_tuple (FSDataHandle, 0, ['注液孔圆形定位成功'])


    * Perform the measurement

    if (SimulateStatus)
        fs_productXML_getpara (FSDataHandle, 'enable_show_measure', enable_show_measure, ProductXMLParaType1)
        if (enable_show_measure)
            fs_show_xld (Cross, FSDataHandle, 1, '测量矩形')
            fs_show_xld (Contour, FSDataHandle, 1, '测量点')
        endif
    endif

else
    fs_reportLog_addLog_tuple (FSDataHandle, 1, ['极柱区域测量定位失败'])
endif
count_obj (Circle_jingdingwei, Number)
if (Number=0)
    fs_reportLog_addLog_tuple (FSDataHandle, 2, ['极柱区域测量定位失败'])
    return ()
endif

return ()

  在这个过程之后,可以得到两个区域,分别是标准圆和阈值分割的圆。
标准圆Circle_jingdingwei:
在这里插入图片描述
阈值分割圆RegionFillUp:
在这里插入图片描述
  一般情况下,标准圆是要比阈值分割圆更大的,因此,就可以通过作差的方式得到毛刺。
  最终可以得到毛刺:
在这里插入图片描述

7、缺口检测函数exec_quekou_COPY_1

  因为之前已经得到标准圆了,那么缺口就非常好检测了,直接用RegionFillUp减去标准圆Circle就可以了。

* 检测缺口(1)阈值分割检测
difference (RegionFillUp, Circle, RegionDifference3)
connection (RegionDifference3, ConnectedRegions5)
opening_circle (ConnectedRegions5, RegionOpening, 1.5)
dilation_circle (RegionOpening, RegionOpening, 1)
count_obj (RegionOpening, Number2)
gen_empty_obj (Region_quekou_1)
for i := 1 to Number2 by 1
    select_obj (RegionOpening, ObjectSelected1, i)
    reduce_domain (SrcImage, ObjectSelected1, ImageReduced1)
    threshold (ImageReduced1, RegionDynThresh1, 0, 200)
    union2 (Region_quekou_1, RegionDynThresh1, Region_quekou_1)
endfor
* intersection (Region_quekou_1, RegionDifference3, Region_quekou_1)
count_obj (Region_quekou_1, Number1)
if (Number1=0)
    message := '没有缺口'
endif
union1 (Region_quekou_1, Region_quekou)
return ()

  得到的缺口区域如下所示:
在这里插入图片描述

8、筛选函数 DefectionScreen_COPY_1

  很显然,在缺口检测中,哪些缺口缺陷不是我们想要的,那么就需要在随后的筛选函数中,把它们全部筛掉。

ProductXMLResolutionH := 0.01123367
ProductXMLResolutionV := 0.01123367

burr_area_thr := burr_area_thr/ProductXMLResolutionH/ProductXMLResolutionH
burr_length_thr := burr_length_thr/ProductXMLResolutionV
gap_area_thr := gap_area_thr/ProductXMLResolutionH/ProductXMLResolutionH
gap_length_thr := gap_length_thr/ProductXMLResolutionV

* ************************
* 一、筛选毛丝
* ************************
gen_empty_obj (Region_maosi)
gen_empty_region (NGRegion)
count_obj (Region_maoci, Number)
NGTuple := []
NGInfo := []
if (Number)
    connection (Region_maoci, ConnectedRegions)
    select_shape (ConnectedRegions, SelectedRegions1, 'area', 'and', 60, 99999)
    region_features (SelectedRegions1, 'area', Value_maosi_area)
    region_features (SelectedRegions1, 'ra', Value_maosi_ra)
    region_features (SelectedRegions1, 'rb', Value_maosi_rb)
    if(|Value_maosi_area|)
        select_a := Value_maosi_ra [<] Value_maosi_rb*20                                // 筛选非常长条的缺陷
        select_b := Value_maosi_ra[>] burr_length_thr[1]                                   // 长边
        select_c := Value_maosi_area[>]burr_area_thr[1]
        select_d := Value_maosi_rb[>] burr_length_thr[1]
        select_area := Value_maosi_area [>] burr_area_thr[0]
        select_ab:=select_d or select_b                                                                    // 筛选长短边
        select_maosi := select_a and select_ab and select_area or select_c
        select_mask_obj (SelectedRegions1, Region_maosi, select_maosi)
    endif
endif

gen_empty_region (NGRegion1)
select_shape (NGRegion1, NGRegion1, 'area', 'and', 0, 99999)
NGTuple1 := []
NGInfo1 := []
* ************************
* 二、筛选缺口
* ************************
count_obj (Region_quekou, Number1)
if (Number1)
    connection (Region_quekou, ConnectedRegions1)
    select_shape (ConnectedRegions1, SelectedRegions, 'area', 'and', 40, 99999)
    region_features (SelectedRegions, 'area', Value_quekou_area)
    region_features (SelectedRegions, 'ra', Value_quekou_ra)
    region_features (SelectedRegions, 'rb', Value_quekou_rb)
    if (|Value_quekou_area|)
        select_d := Value_quekou_ra [<] Value_quekou_rb*3
        select_a := Value_quekou_ra[>] gap_length_thr[1]
        select_b := Value_quekou_area[>]gap_area_thr[1]
        select_all := select_a  and select_d
        select_all:=  select_all or select_b
        select_mask_obj (SelectedRegions, Region_quekou, select_all)
    endif
endif
union2 (Region_maosi, Region_quekou, DefectRegions)
return ()

  在这个筛选过程中,由于筛选参数不明确,所以筛选条件都是自己设定的,后续可以根据客户的需求来客制化筛选参数。

9、总结

  其实算法的整个步骤非常简单,其中大部分东西还不太完善,不过大致的缺陷提取思想就是这样的。当然在做一个项目的时候,不可能这么简单完成,还需要与软件相互配合,不过这就设计到QT或者MFC的一些知识了。待博主学习完了之后,将这所有的东西再串一下,分享给大家。
    
  
  
  
  
加油各位,勇往直前,
    
  
总有一天可以成为一个大佬……!

猜你喜欢

转载自blog.csdn.net/weixin_44463519/article/details/134592817
今日推荐