当前位置:网站首页>基于halcon—缺陷检测常用方法与示例总结
基于halcon—缺陷检测常用方法与示例总结
2022-06-12 18:24:00 【NCUTer】
摘要
缺陷检测是视觉需求中难度最大一类需求,主要是其稳定性和精度的保证。首先常见缺陷:凹凸、污点瑕疵、划痕、裂缝、探伤等。 缺陷检测算法不同于尺寸、二维码、OCR等算法。后者应用场景比较单一,基本都是套用一些成熟的算子,所以门槛较低,比较容易做成标准化的工具。而缺陷检测极具行业特点,不同行业的缺陷算法迥然不同。随着缺陷检测要求的提高,机器学习和深度学习也成了缺陷领域一个不可或缺的技术难点。
- 传统算法检测缺陷:调试难度大,容易在检测不稳定情况下反复调参,且复杂缺陷误测多,兼容性差
- 机器学习检测缺陷:一般使用类似MLP的一些单层神经网络,对缺陷特征进行训练分类,该方法需要事先提取出缺陷部分,一般用来与传统分割法搭配使用,达到缺陷检测分类的效果。
- 深度学习检测缺陷(打标签):一般需要客户提供大量的缺陷样本,而且缺陷种类越多、特征越不明显,需要的缺陷样本就越大。其次,打标签过程很难做到自动,需要手动辅助框出缺陷位置,工作量非常大。总结就是训练周期久,训练样本大,如果客户可以提供大量样本,那该方法是首选(半导体行业一般不会出现大量缺陷样品)
- 深度学习检测缺陷(迁移学习法):该方法我感觉会成为后面工业领域检测瑕疵的一个大趋势,但是需要一些公司去收集各种行业的缺陷类型图片和训练的网络模型,并共享出来(突然感觉是个商机,就看谁能抓住了),然后我们可以使用迁移学习的方法学习别人训练好的模型。
总的来说,机器视觉中缺陷检测分为一下几种:
- blob分析+特征
- 模板匹配(定位)+差分
- 光度立体
- 特征训练
- 测量拟合
- 频域+空间域结合
- 深度学习
频域+空间域结合法
频域结合空间,其实频域就是用波动观点看世界,看问题角度变了,光经过镜头其实发生的是傅立叶变换,此思想在傅立叶光学上有所阐述,就像光经棱镜分光,而光进入计算机内部,进行了采样和量化,然后我们用函数f(x,y)来表示这些数据描述。图像处理应用傅里叶变换就是将空间域(图像本身)转换至频率域。傅里叶变换可以将一个信号函数,分解一个一个三角函数的线性组合。由于任何周期函数都可以由多个正弦函数构成,那么按照这个思想,图像由f(x,y)来表示,那么这时你就可以拆成多个正弦函数构成,这样每个正弦函数都有一个自己的频率。
关于傅里叶的讲解,可以参考此文章详看:傅里叶分析之掐死教程(完整版)更新于2014.06.06 - 知乎 (zhihu.com)
频率特征是图像的灰度变化特征,低频特征是灰度变化不明显,例如图像整体轮廓,高频特征是图像灰度变化剧烈,如图像边缘和噪声。一个重要的经验结论:低频代表图像整体轮廓,高频代表了图像噪声,中频代表图像边缘、纹理等细节。
那么什么时候使用傅里叶变换进行频域分析?
1)具有一定纹理特征的图像,纹理可以理解为条纹,如布匹、木板、纸张等材质容易出现。
2)需要提取对比度低或者信噪比低的特征。
3)图像尺寸较大或者需要与大尺寸滤波器进行计算,此时转换至频域计算,具有速度优势。因为空间域滤波为卷积过程(加权求和),频域计算直接相乘。
一般对于频域处理的方法有三种:
- 直接手画ROI区域,然后paint_regio(喷黑)
- 用滤波器(高通,低通,带通),然后进行滤波处理
- 调用power_real,对其进行blob分析。
halcon相关案例分析:
1,脏污检测(低通滤波,差分,线提取)
如下图,在塑料薄膜上有一些线条型的脏污,在空间域中向提取脏污的区域是比较困难的(方格会影响空间域的二值化),所以我们就想到了在频域中去处理它。
思路:
用calculate_lines_gauss_parameters函数计算Sigma和高低阈值(为后续lines_gauss提取脏污线痕做准备)
读入图片,将B空间转到频域
用高斯滤波器(低通滤波)进行滤波(即得到背景图像)
差分(原图——背景图),锐化图像
用lines_gauss提取脏污线痕

* 根据要提取的线的最大宽度和对比度,计算Sigma和高低阈值
calculate_lines_gauss_parameters (43.5, [25,5], Sigma, Low, High)
read_image (Image, 'D:/1.png')
* 这种脏污的提取可以考虑在频域中处理
* 让前景和背景分离,然后再提取脏污的区域
* 彩色图转灰度 这里拆通道更好,因为方格会影响脏污的提取
decompose3 (Image, R, G, B)
get_image_size(B, Width, Height)
* 空间域转频域
fft_generic (B, ImageFFT, 'to_freq', -1, 'none', 'dc_center', 'complex')
* 创建一个高斯滤波器/sigma越小滤波器越小,通过的信号更加的集中在低频,这样做的目的是得到背景
gen_gauss_filter (ImageGauss, 100, 100, 0, 'n', 'dc_center', Width, Height)
* 频域的乘法相当于空间域的卷积
convol_fft (ImageFFT, ImageGauss, ImageConvol)
* 频域转空间域
fft_generic (ImageConvol, ImageFFT1, 'from_freq', 1, 'none', 'dc_center', 'byte')
* 差分(原图 — 背景)
sub_image (B, ImageFFT1, ImageSub, 2, 100)
* 提取脏污的中心线
lines_gauss (ImageSub, Lines, Sigma, Low, High, 'dark', 'true', 'gaussian', 'true')
dev_display (B)
dev_display (Lines)
相关API参数:
- calculate_lines_gauss_parameters(根据线的最大宽度以及对比度计算出lines_gauss算子输入的Sigma、Low、High值)
calculate_lines_gauss_parameters( : : MaxLineWidth, Contrast : Sigma, Low, High)
MaxLineWidth (input_control) // lines_gauss要提取线条的最大宽度
Contrast (input_control) //lines_gauss要提取线的对比度。
Sigma (output_control) //获取用于lines_gauss输入的Sigma值
Low (output_control) //获取用于lines_gauss输入的Low 值
High (output_control) //获取用于lines_gauss输入的High 值关于Contrast 参数详解:
Contrast 值不仅可以一个,也可以为两个:
当只选择一个值时,最小对比度将会默认为最大对比度的1/3,最小对比度越小,线条将会延伸到对比度较低的区域,即线条越长。反之,值越高,线条越短,但越突出。
当值为两个时,数组中的第二个值是要提取线的最小对比度,并且其值不能大于第一个值。比如:[20,10]
lines_gauss(提取图像上的线条,提取的结果属于亚像素精度的XLD轮廓)
lines_gauss(Image , Lines ,Sigma, Low, High, LightDark, ExtractWidth, LineModel, CompleteJunctions )
Image (input_object) //输入图像
Lines (output_object) //检测线条(XLD)
Sigma (input_control) //高斯滤波值
Low (input_control) //滞后阈值分割的低阈值
High (input_control) //滞后阈值分割的高阈值
LightDark (input_control)//提取线条的类型,暗色还是亮色,(’dark’, ‘light’)
ExtractWidth (input_control) //是否提取线宽(‘false’,‘true’)
LineModel (input_control) //用来调整线条位置和宽度的线模型(‘bar-shaped’, ‘gaussian’, ‘none’, ‘parabolic’)
CompleteJunctions (input_control) //在断连的部分是否添加节点使线条连续(‘false’, ‘true’)2,检测表面微小凸起(高斯差分,灰度差,二值化)
如图,对于处理这种细微的缺陷,也可使用频域处理。
思路:
- 使用两个低通滤波器,进行相减后构造了一个带阻滤波器来提取缺陷分量
- 读入图像,灰度化,转频域,进行滤波,转回空间域
- 在空间域上blob分析
- 显示
关键点:该例程的关键就是使用两个低通滤波器,进行相减后构造了一个带阻滤波器来提取缺陷分量。通过带阻滤波后获得的频率成分对背景中的纹理要有明显的抑制,并且突出缺陷成分,在频域处理完成转会空间域之后,又用了一个能扩大亮点区域的函数:gray_range_rect 辅助后面的二值化,最终完成了缺陷的检测,这个函数可以说是点睛之笔。

完整代码如下:
dev_close_window ()
*1采集图像
read_image (Image, 'D:/1.png')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
dev_set_draw ('margin')
dev_set_line_width (3)
dev_set_color ('red')
* 根据具体宽高,优化该图像的傅里叶变换速度(有此函数)
optimize_rft_speed (Width, Height, 'standard')
Sigma1 := 10.0
Sigma2 := 3.0
*形成高斯滤波器
gen_gauss_filter (GaussFilter1, Sigma1, Sigma1, 0.0, 'none', 'dc_center', Width, Height)
gen_gauss_filter (GaussFilter2, Sigma2, Sigma2, 0.0, 'none', 'dc_center', Width, Height)
*第一个滤波器减去第二个滤波器(形成带通滤波器)
sub_image (GaussFilter1, GaussFilter2, Filter, 1, 0)
*2进行频域滤波
rgb1_to_gray (Image, Image)
* 转到频域
fft_generic (Image, ImageFFT, 'to_freq', -1, 'none', 'dc_center', 'complex')
*滤波
convol_fft (ImageFFT, Filter, ImageConvol)
*返回空间域(实部)
fft_generic (ImageConvol, ImageFiltered, 'from_freq',1, 'n', 'dc_center', 'real')
*3空间域上的blob图像分割
*原图矩形内的灰度值范围(max-min)作为输出图像像素值,扩大了亮的部分
gray_range_rect (ImageFiltered, ImageResult, 10, 10)
* 获得图像最大灰度值和最小灰度值
min_max_gray (ImageResult, ImageResult, 0, Min, Max, Range)
*二值化提取( 5.55是经验值,在调试中得到)
threshold (ImageResult, RegionDynThresh, max([5.55,Max * 0.8]), 255)
select_shape (RegionDynThresh, SelectedRegions, 'area', 'and', 1, 99999)
connection (SelectedRegions, ConnectedRegions)
dev_display (Image)
count_obj (ConnectedRegions, Number)
for Index1 := 1 to Number by 1
select_obj (ConnectedRegions, ObjectSelected, Index1)
area_center (ObjectSelected, Area, Row, Column)
gen_circle_contour_xld (ContCircle, Row, Column, 20, 0, 6.28318, 'positive', 1)
dev_display (ContCircle)
endfor
相关API参数:
gray_range_rect( Image , ImageResult , MaskHeight, MaskWidth: )
参数列表:
Image(in) //被计算灰度值的图像
ImageResult(out) //包含灰度值的图像
MaskHeight(in) //滤波器掩模的高度
MaskWidth(in) //滤波器掩模的宽度效果如图:在滤波后对图像进行 gray_range_rect (ImageFiltered, ImageResult, 10, 10)处理后(增强对比度,即亮部分):

- min_max_gray(得到区域的最小值最大值及灰度值范围
- gen_circle_contour_xld(创建圆或圆弧的XLD轮廓)
gen_circle_contour_xld(ContCircle ,Row, Column, Radius, StartPhi, EndPhi, PointOrder, Resolution) 参数列表: ContCircle(out) //输出轮廓 Row, Column(in) //圆弧或圆的中心坐标 Radius(in) //圆弧或圆的半径 StartPhi(in) //圆或圆弧的起始角度 EndPhi(in) //圆或圆弧的终止角度 PointOrder(in) //输入沿边界的点序( 'negative'负序, 'positive'正序) Resolution(in) //相邻轮廓点之间的距离(Resolution >= 0.00001)
- gen_gauss_filter(形成高斯滤波器(低通))
gen_gauss_filter( GaussFilter,Sigma1, Sigma2, Phi, Norm, Mode, Width, Height ) 参数列表: GaussFilter(in/out)//生成的高斯滤波器的句柄 Sigma1(in) //空域中高斯在主方向上的标准差 Sigma2(in) //空域中高斯在垂直于主方向的方向上的标准差 Phi(in) //滤波器主方向的角度(0.0) Norm(in) //滤波器的规范(’none’) Mode(in) // 直流项在频域的位置(’rft’) Width, Height (in) // 图片的宽高
该例程通过两个高斯滤波器相减,构建一个带通滤波器,其函数GenGaussFilter为:,常用于纹理缺陷检测
构建函数:GenGaussFilter(ImageFilter, Sigma1, Sigma2, Width, Height) gen_gauss_filter (GaussFilter1, Sigma1, Sigma1, 0.0, 'none', 'rft', Width, Height) gen_gauss_filter (GaussFilter2, Sigma2, Sigma2, 0.0, 'none', 'rft', Width, Height) sub_image (GaussFilter1, GaussFilter2, ImageFilter, 1, 0) return ()
GenGaussFilter (ImageFilter, 2, 10, Width, Height)则是一个带通滤波器(或者说“带阻滤波器”)——先通过高反差保留让中高频通过,然后通过高斯模糊抑制高频,最终的结果是让中频通过。
3,检测磨砂表面的缺陷(高斯滤波差分,分水岭,灰度共生矩阵)
由于磨砂表面粗糙(噪点很多,影响二值化) 因此该例程使用了频域高斯滤波差分后,在空间域的blob分析用了分水岭域分割滤波后的图像,计算每个区域灰度共生矩阵,通过能量筛选缺陷。

dev_close_window ()
dev_update_off ()
read_image (Image, 'D:/1.png')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, 640, 480, 'black', WindowHandle)
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
dev_set_draw ('margin')
dev_set_line_width (3)
dev_set_color ('red')
decompose3 (Image, R, G, B)
fft_generic (B, ImageFFT, 'to_freq', -1, 'sqrt', 'dc_center', 'complex')
gen_gauss_filter (ImageGauss, 50, 50, 0, 'none', 'dc_center', Width, Height)
convol_fft (ImageFFT, ImageGauss, ImageConvol)
fft_generic (ImageConvol, ImageBackground, 'from_freq', 1, 'sqrt', 'dc_center', 'byte')
* 图像减去背景,增加特征与背景对比度
sub_image (B, ImageBackground, ImageSub, 2, 100)
* 中值滤波,为分水岭域做准备
median_image (ImageSub, ImageMedian, 'circle', 9, 'mirrored')
watersheds_threshold (ImageMedian, Basins, 20)
* 缺陷部分是黑色的,灰度值小能量就小,所以根据能量可以将缺陷的区域筛选出来
cooc_feature_image (Basins, ImageMedian, 6, 0, Energy, Correlation, Homogeneity, Contrast)
Mask := Energy [<=] 0.05
select_mask_obj (Basins, Defects, Mask)
dev_display (Image)
dev_display (Defects)
count_obj (Defects, NDefects)
disp_message (WindowHandle, NDefects + ' \'mura\' defects detected', 'window', 12, 12, 'red', 'true')
相关API参数:
- cooc_feature_image(计算图像的灰度共生矩阵)
cooc_feature_image(Regions, Image ,LdGray, Direction ,Energy, Correlation, Homogeneity, Contrast)
参数列表:
Regions(in) //要检查的区域。
Image (in) //灰度图像。
LdGray(in) //要区分的灰度值的数量。(默认6)
Direction (in) //矩阵的计算方向('0','45','90','130','mean‘)
Energy(out) //能量
Correlation(out)//相关性
Homogeneity(out) //局部均匀性(熵)
Contrast(out) //对比度(反差)输出参数详解:
能量(Energy):是对图像纹理的灰度变化稳定程度的度量,反应了图像灰度分布均匀程度和纹理粗细度。能量越大,表示灰度变化比较稳定,反映了纹理变化的均匀程度。。对于灰度图来说,能量低说明灰度值低,对于彩色图来说,能量低说明光强低。
相关性(Correlation):表示纹理在行或者列方向的相似程度。相关性越大,相似性越高。
(熵)局部均匀性(Homogeneity):反映图像局部纹理的变化量(即复杂程度),熵值越大图像越复杂。
(反差)对比度(Contrast):表示矩阵的值的差异程度,也间接表现了图像的局部灰度变化幅度。反差值越大,图像中的纹理深浅越明显,表示图像越清晰;反之,则表示图像越模糊。
- watersheds_threshold(阈值分水岭图像分割)
watersheds_threshold(Image ,Basins ,Threshold ) 参数列表: Image(in)//输入图像(最好先用中值滤波处理) Basins(out)//输出二值图像(盆地) Threshold(in)//阈值
算子描述:
第一步:计算出分水岭(不使用该参数Threshold ),分割的盆地和调用算子watersheds得到的盆地是相同的
第二步:如果被一个分水岭分割的相邻盆地与对应分水岭的高度差小于Threshold ,盆地依次合并。假设B1和B2分别是两个相邻盆地的最小灰度值,W是盆地对应分水岭的最小灰度值。当满足以下条件时,两个盆地合并:max{W-B1,W-B2}<Threshold 。由此得到的盆地存储在Basins 变量中。
4,检测工件孔洞毛刺缺陷 - 局部变形匹配(inspect_gasket_local_deformable.hdev)
在日常工程应用中,我们通常通过halcon的形状匹配(shape-based matching)进行各种定位,正如上篇例程,当待匹配物体有轻微变形时,并不影响得到的匹配结果,然后当待匹配物体有较大变形时,如塑料产品在成形时变形、纺织产品的花纹因为褶皱变形等,要想得到精确的定位结果就显得捉襟见肘,如下图所示,工件如果有较大变形,在用形状匹配时,定位结果就不尽如人意,因为形状匹配本身得到的匹配结果只是一个点(row,col)。
因此本篇例程使用了局部变形匹配(local deformable matching),匹配结果可以根据待匹配物体自动进行变形。而且在这个案例中,create_variation_model (Width, Height, ‘byte’, ‘direct’, VariationModelID) 使用的方法是’direct’,因此是不需要训练差异模型而可以直接使用的。
1️⃣读入标准图像,创建差异模型以及匹配模板
*1.读入图像
dev_update_off ()
dev_get_window (WindowHandle)
set_display_font (WindowHandle, 36, 'mono', 'true', 'false')
dev_set_draw ('margin')
read_image (ModelImage, 'gasket/gasket_model')
get_image_size (ModelImage, Width, Height)
read_image (Image, 'gasket/gasket_01')
*2.创建差异模型
create_variation_model (Width, Height, 'byte', 'direct', VariationModelID)
sobel_amp (ModelImage, EdgeAmplitude, 'sum_abs', 3)
*3.直接设参数+标准图像+边缘幅度图像
prepare_direct_variation_model (ModelImage, EdgeAmplitude, VariationModelID, 20, 2)
*4.创建局部变形匹配模板
create_local_deformable_model (ModelImage, 'auto', [], [], 'auto', 0.9, [], 'auto', 0.9, [], 'auto', 'none', 'use_polarity', 'auto', 'auto', [], [], ModelID)
get_deformable_model_contours (ModelContours, ModelID, 1)
area_center (ModelImage, Area, Row, Column)标准图像:

这里由于是用单幅图像创建的差异模型,因此参数Mode设置的’direct’,故不需要再去训练,而是直接使用prepare_direct_variation_model (ModelImage, EdgeAmplitude, VariationModelID, 20, 2)得到差异模型。
2️⃣通过匹配模板将待检测工件定位矫正
for Index := 1 to 7 by 1
read_image (Image, 'gasket/gasket_' + Index$'02')
get_image_size (Image, Width1, Height1)
*5.查找
find_local_deformable_model (Image, ImageRectified, VectorField, DeformedContours, ModelID, rad(-10), rad(20), 1, 1, 1, 1, 0.93, 1, 0.7, 0, 0.4, ['image_rectified','vector_field','deformed_contours'], ['deformation_smoothness','expand_border','subpixel'], [25,0,1], Score, Row, Column)
if (|Score| > 0)
gen_warped_mesh_region (VectorField, MeshRegion, 25)
gen_region_contour_xld (DeformedContours, EdgeRegion, 'margin')
dilation_circle (EdgeRegion, RegionDilation, 2 * 25)
intersection (RegionDilation, MeshRegion, RegionIntersection)
dev_set_line_width (1)
dev_set_color ('yellow')
dev_display (Image)
dev_display (RegionIntersection)
Found[Index] := |Score|
dev_set_line_width (2)
dev_set_color ('green')
dev_display (DeformedContours)
* 7.注意:这里显示的是修正过的图像
dev_display (ImageRectified)
endif
待检测图像:

局部变形匹配定位:

由于局部变形模板匹配在寻找到图像(find_local_deformable_model)后是自动矫正的,因此我们可以省掉仿射变换的步骤了。
3️⃣通过差异模型差分得到缺陷工件
*6.差分
compare_variation_model (ImageRectified, Region, VariationModelID)
connection (Region, ConnectedRegions)
*缺陷提取(特征选择,即面积大于60的定义为缺陷)
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 60, 99999)
count_obj (SelectedRegions, Number)
*显示
if(Number>0)
disp_message (WindowHandle, 'NG', 'image', 12, 12, 'red', 'false')
else
disp_message (WindowHandle, 'OK', 'window', 12, 12, 'magenta', 'false')
endif
dev_set_color ('red')
dev_display (SelectedRegions)
stop()
endfor
dev_update_on ()
边栏推荐
- 面试题总结
- Resttemplateconfig configuration print request response log under soringboot
- Explanation of core interrupt of Godson processor
- Extracting strings with grep awk
- 同花顺能开户吗,同花顺在APP上可以直接开通券商安全吗
- Write a select based concurrent server
- JS dichotomy
- GD32F4xx控制DGUS触控按键
- 用grep awk提取字符串
- Problems that the sap Spartacus e-commerce cloud UI shipping method does not display in the unit test environment
猜你喜欢

Remote gadget putty (Alibaba cloud mirror station address sharing)

VirtualLab基础实验教程-6.闪耀光栅

General differences between SQL server versions released by Microsoft in different periods so far, for reference

在思科模拟器Cisco Packet Tracer实现自反ACL

Review of MySQL (IX): index

深圳3月14日起全市停工停业7天居家办公心得|社区征文

Problems that the sap Spartacus e-commerce cloud UI shipping method does not display in the unit test environment

Solution to the problem that the anaconda navigator card logo cannot be opened and the card will flash back - replace the alicloud image source

PHP:Fatal error: Allowed memory size of 262144 bytes exhausted (tried to allocat

快速复制浏览器F12中的请求到Postman/或者生成相关语言的对应代码
随机推荐
"Big fat • small lesson" - talk about big file segmentation and breakpoint sequel
Is it safe to open an account in flush
Mise en œuvre de l'ACL réflexe dans le simulateur Cisco Cisco Packet Tracer
USB转串口那些事儿—串口驱动类型
torch.where的新用法(很老但是大家忽略的用法)
2022.6.12-----leetcode.890
TypeScript常用类型(一)
js判断回文数
Comparison of disk mapping tools for network disk and object cloud storage management
Typescript advanced type (2)
GD32F4xx 与符合DLT645的电能表通信_2
About datasets
Gospel of audio and video developers, rapid integration of AI dubbing capability
JS中的栈(含leetcode例题)<持续更新~>
Review of MySQL (VII): use of tables
C#的变量
VirtualLab基础实验教程-5.泊松亮斑
General differences between SQL server versions released by Microsoft in different periods so far, for reference
Gospel of audio and video developers, rapid integration of AI dubbing capability
GD32F4xx控制DGUS触控按键