缺陷检测分类
1.1 标准缺陷检测
所谓标准,就是不针对行业特点,这里基本分为如下几类:
标准预处理功能:图像增强、腐蚀、膨胀、开运算、闭运算、滤波、傅立叶变换(频域空间域转换)、距离变换、差分、等
面积检测:阈值后计算ROI内面积
Blob(阈值分割+特征提取)检测:阈值联通后计算Blob
浓度差检测:计算ROI范围内的最大浓度、最小浓度、浓度差
直线/曲线上的毛刺/缺陷:拟合直线/曲线,计算边缘点到直线/曲线距离
标准的做法一般都是拿标准的算法块进行组合,以达到缺陷检测的效果,缺陷检测标准流程一般是:
1设置基准图模板——>2当前图模板定位——>3生成仿射变换矩阵——>4旋转平移图像或者区域——>5预处理差分——>6预处理滤波/腐蚀/膨胀——>7Blob检测——>8面积检测
1.2 非标缺陷检测(针对行业特性)
相对与标准做法,非标的做法就非常多,有些非标的目的是为了减少操作步骤,例如将上面的组合流程变成一个工具,这个我们称之为业务逻辑非标。
还有一些非标主要是做图像预处理部分,例如修改一些标准预处理的算子和预处理流程,将瑕疵提取出来。当然,对数学理论掌握程度较高的朋友,会推导理论公式,然后直接实现数学公式达到检测效果。
2. 行业难点
传统算法检测缺陷:调试难度大,容易在检测不稳定情况下反复调参,且复杂缺陷误测多,兼容性差
机器学习检测缺陷:一般使用类似MLP的一些单层神经网络,对缺陷特征进行训练分类,该方法需要事先提取出缺陷部分,一般用来与传统分割法搭配使用,达到缺陷检测分类的效果。
深度学习检测缺陷(打标签):一般需要客户提供大量的缺陷样本,而且缺陷种类越多、特征越不明显,需要的缺陷样本就越大。其次,打标签过程很难做到自动,需要手动辅助框出缺陷位置,工作量非常大。总结就是训练周期久,训练样本大,如果客户可以提供大量样本,那该方法是首选(半导体行业一般不会出现大量缺陷样品)
深度学习检测缺陷(迁移学习法):该方法我感觉会成为后面工业领域检测瑕疵的一个大趋势,但是需要一些公司去收集各种行业的缺陷类型图片和训练的网络模型,并共享出来(突然感觉是个商机,就看谁能抓住了),然后我们可以使用迁移学习的方法学习别人训练好的模型。
3. 常规缺陷检测算法(Halcon)
总的来说,缺陷检测的算法包括:
Blob分析+特征提取(常用,较简单)
定位(Blob定位、模板匹配定位)+ 差分(常用)
光度立体
特征训练
测量拟合(常用)
频域+空间域结合(常用)
深度学习
3.1 差分法
标准缺陷检测我觉得用差分法还是蛮多的。顾名思义,差分就是通过对两幅图像或和两个区域作差,来找出其中有差异的区域。处理流程基本就是定位Blob分析+差分或模板匹配+差分的方式,主要用来检测物品损坏,凸起,破洞,缺失,以及质量检测等。两种方式的具体流程如下:
3.1.1 blob分析+差分
检测流程如下:
读取图像
对图像进行Blob分析,提取图像上的Roi检测区域
在对Roi区域直接进行差分处理或者与没有缺陷的图像进行差分处理
ps:这里差分包含区域差分和图像差分两种方式。
最后求差集,根据差集部分的面积判断该物品是否有缺陷
流程解析:以标准图像中的灰度值为模板,计算处检测图像的灰度值,并与标准图像作差,灰度值差异越大,则证明检测图像中存在与标准图比有明显灰度变化的区域,即这部分区域就是我们所要筛选出的缺陷区域。
示例分析:提取灰度值明显的缺陷区域
*1.使用`intensity()`算子计算出模板图(即标准图,图1)的检测区域图像的灰度平均值`OriginalMean`
intensity(OriginalRegion, ImageReduced1, OriginalMean, Deviation1)
*2.再`intensity()`算子计算出待测图(图2)的检测区域图像的灰度平均值`DetectMean`,计算出两幅图像灰度均值的差值
intensity (DetectRegion, ImageReduced2, DetectMean, Deviation2)
tuple_abs (OriginalMean-DetectMean, Abs)
*3.
*如果两个区域的灰度值差值大于10(if(Abs>10)),则生成一副图像(图3),其灰度值为模板图中计算得到的的平均灰度值;
*如果两个区域的灰度值差值小于10(if(Abs<10)),则生成一副图像(图3),其灰度值为待测图中计算得到的的平均灰度值。
*ps:这里计算结果是差值小于10,也就是检测图的灰度值差异和模板图差异不大,直接生成一个后者的灰度均值图像
if(Abs>10)
gen_image_proto (ImageReduced2, ImageCleared, OriginalMean)
else
gen_image_proto (ImageReduced2, ImageCleared, DetectMean)
endif
reduce_domain (ImageCleared, RegionDifference, ImageReduced1)
*4.将待测图与新生成的灰度值图像做差值(图4),可以找到待测图和模板图灰度值有差异的区域
abs_diff_image (ImageReduced2, ImageReduced1, ImageAbsDiff, 1)
invert_image (ImageAbsDiff, ImageInvert)
threshold (ImageInvert, Region1, 0, 30)
opening_circle (Region1, RegionOpening, 1.5)
connection (RegionOpening, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 10, 99999)
检测效果如下:
没理解的,再列举一些Halcon中的差分例程练练手:
1.检测毛刺(Blob+差分法)—— fin.hdev
2.电路板线路缺陷检测(Blob+差分法)—— pcb_inspection.hdev
3.瓶口破损缺陷检测(Blob+差分法)—— inspect_bottle_mouth.hdev(注意直角坐标系和极坐标系之间的转换)
3.1.2 模板匹配+差分
流程如下:
先定位模板区域后,求得模板区域的坐标,创建物品的形状模板create_shape_model,注意把模板的旋转角度改为rad(0)和rad(360)
匹配模板find_shape_model时,由于物品的缺陷使形状有局部的改变,所以要把MinScore设置小一点,否则匹配不到模板。并求得匹配项的坐标
关键的一步,将模板区域仿射变换到匹配成功的区域。由于差集运算是在相同的区域内作用的,所以必须把模板区域转换到匹配项的区域
最后求差集,根据差集部分的面积判断该物品是否有缺陷
示例分析:印刷质量缺陷检测(可变现模板匹配+差分法)—— print_check.hdev
ps:里面没用difference做差分,而是用了Halcon为变形模板提供的专门的差分算子:compare_variation_model();
3.1.3 两种检测方式对比
Blob分析适用于需要整张图都是Roi区域或图像某处Roi区域通过预处理很容易提取出来的情况。而Blob分析没办法定位到图像R区域的时候就需要用到模板匹配了,通过模板匹配(形状匹配或局部变形匹配)定位到图像Roi区域,然后用差异模型去检测缺陷,可以理解为模板匹配 + 差分是Blob分析 + 差分的进阶版,比较容易处理的可以交给儿子Blob分析处理,难的话爸爸模板匹配上。
3.2 频域+空间结合法
3.2.1 傅里叶变换理论
傅里叶变换是一种函数在空间域和频率域的变换,从空间域到频率域的变换是傅里叶变换,而从频率域到空间域是傅里叶的反变换。
时域与频域:
频域(frequency domain)
是指在对函数或信号进行分析时,分析其和频率有关部份,而不是和时间有关的部份,和时域一词相对。
时域(空间域)
是描述数学函数或物理信号对时间的关系。例如一个信号的时域波形可以表达信号随着时间的变化。若考虑离散时间,时域中的函数或信号,在各个离散时间点的数值均为已知。若考虑连续时间,则函数或信号在任意时间的数值均为已知。在研究时域的信号时,常会用示波器将信号转换为其时域的波形。
两者相互间的变换
时域(信号对时间的函数)和频域(信号对频率的函数)的变换在数学上是通过积分变换实现。对周期信号可以直接使用傅立叶变换,对非周期信号则要进行周期扩展,使用拉普拉斯变换。
信号在频率域的表现:
在频域中,频率越大说明原始信号 变化速度越快;频率越小说明原始信号越平缓。当频率为0时,表示直流信号,没有变化。因此,频率的大小反应了信号的变化快慢。高频分量解释信号的突变部分,而低频分量决定信号的整体形象。
在图像处理中,频率反应了图像在空域灰度变化剧烈程度,也就是图像灰度的变化速度,也就是图像的梯度大小。对图像而言,图像的边缘部分是突变部分,变化较快,因此反应在频域上是高频分量;图像的噪声大部分情况下是高频部分;图像平缓变化部分则为低频分量。也就是说,傅立叶变换提供另外一个角度来观察图像, 可以将图像从灰度分布转化到频率分布上来观察图像的特征。书面一点说就是,傅里叶变换提供了一条从空域到频率自由转换的途径。对图像处理而言,以下概念非常的重要。
由布布丶全权冠名的“ 第一届图像杯拳王挑战锦标赛 "`正式开始,有请:
高频代表队参赛选手:噪声、细节和边缘
图像高频分量:图像突变部分,在某些情况下指图像边缘信息,某些情况下指噪声,更多是两者的混合。
低频代表队参赛选手:图像整体轮廓
图像低频分量:图像(亮度/灰度)变化平缓的部分,代表着那是连续渐变的一块区域,这部分就是低频。对于一幅图像来说,除去高频的就是低频了,也就是边缘以内的内容为低频,而边缘内的内容就是图像的大部分信息,即图像的大致概貌和轮廓,是图像的近似信息。
亲高频派裁判代表:高通滤波器 —— 让图像高频分量通过,抑制低频分量。
亲低频派裁判代表:低通滤波器 —— 与高通相反,让图像低频分量通过,抑制高频分量。
铁面无私裁判代表:带通滤波器 —— 使图像在某一部分的频率信息通过,其他过低或过高都抑制。
左右逢源裁判代表:带阻滤波器,是带通的反。
加强理解:图像噪声一般是白点或者黑点,因为它与正常的点颜色不一样了,也就是说该像素点灰度值明显不一样了,也就是灰度有快速地变化了,所以是高频部分;图像细节处也是属于灰度值急剧变化的区域,正是因为灰度值的急剧变化,才会出现细节,也属于高频部分;所以一般会对信号先进行低通滤波处理,即过滤掉图像中的高频部分(噪声/细节/边缘),留下低频(图像轮廓),结果就是图像模糊了。
ps:图像处理中,有书上说低频反应轮廓,高频反应细节;有的文章里面说低频反应的是背景,高频反应的是边缘;低频反应轮廓,这里的轮廓指的不是边缘(很多人会搞混觉得轮廓是指的就是边缘),打个比方,近视眼的人摘了眼镜,人们通常会说:“我什么也看不清,仅仅能看到一个大致轮廓。”就是类似的意思。所以图像的边缘提取仍是提起的边缘的高频信息,这两种说法并不矛盾。
总结:低频代表图像整体轮廓,高频代表了图像噪声、边缘和细节,中频代表图像纹理等。
3.2.2 应用场景
使用傅里叶变换进行频域分析的应用场景:
具有一定纹理特征的图像,纹理可以理解为条纹,如布匹、木板、纸张等材质容易出现。
需要提取对比度低或者信噪比低的特征。
图像尺寸较大或者需要与大尺寸滤波器进行计算,此时转换至频域计算,具有速度优势。因为空间域滤波为卷积过程(加权求和),频域计算直接相乘。
3.2.3 核心检测算子
在Halcon中,使用频域进行检测的思路是先从空间域到频域,在频域中进行适当滤波,选择自己想要的频段,然后再返回到空间域中去,其中有两个步骤是比较关键的:
1. 生成合适的滤波器
对应的关键算子:
gen_std_bandpass
gen_sin_bandpass
*创建一个高斯滤波器,sigma越小滤波器越小,通过的信号更加的集中在低频,这样做的目的是得到背景
gen_gauss_filter( : ImageGauss : Sigma1, Sigma2, Phi, Norm, Mode, Width, Height : )(常用)
gen_mean_filter
gen_derivative_filter
gen_bandpass
gen_bandfilter
gen_highpass
gen_lowpass
2. 快速傅里叶变换(空间域和频域之间的相互转换)
对应的关键算子:
fft_generic(Image : ImageFFT : Direction, Exponent, Norm, Mode, ResultType : )
rft_generic(Image : ImageFFT : Direction, Norm, ResultType, Width : )
两个算子共同点:
这两个算子都可以进行空间域—>频域,频域—>空间域的变换,只需要针对参数Direction分别进行选择,参数'to_freq'是进行的是空间域—>频域的变换,'from_freq'是频域—>空间域的变换
针对参数ResultType,如果是to_freq',那么ResultType一般选择'complex';如果是'from_freq',ResultType一般选择’byte’(灰度图像)。
两个算子不同点:
fft_generic:DC项在频域中的位置可选在左上角(Mode:'dc_edge')或者原点平移到中心(Mode:'dc_center')
rft_generic:没有设置项Mode,原点在默认在左上角。
除此之外,fft_image:也可进行快速傅里叶变化(空间域到频域),等价于fft_generic(Image,ImageFFT,‘to_freq’,-1,‘sqrt’,‘dc_center’,‘complex’)
3.2.4 相关实际检测案例
塑料制品的表面进行缺陷检测 —— detect_indent_fft.hdev
* 1.对指定大小的图片的fft速度进行优化
optimize_rft_speed (Width, Height, 'standard')
Sigma1 := 10.0
Sigma2 := 3.0
* 2.构造两个高斯滤波器,ps:Sigma参数的选取很重要
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, Filter, 1.025, 0)
NumImages := 16
for Index := 1 to NumImages by 1
read_image (Image, 'plastics/plastics_' + Index$'02')
rgb1_to_gray (Image, Image)
* 3.计算一个图像的实值快速傅里叶变换(空间域转至频域)
rft_generic (Image, ImageFFT, 'to_freq', 'none', 'complex', Width)
* 4.用在频域内的滤波器使一个图像卷积。
convol_fft (ImageFFT, Filter, ImageConvol)
* 5.卷积后的频域图像转至空间域
rft_generic (ImageConvol, ImageFiltered, 'from_freq', 'n', 'real', Width)
* 6.滤波之后的图像交给形态学来分析
* 空间域上的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)
对于处理这种细微的缺陷,也可使用频域处理。该例程的关键就是使用两个低通滤波器,进行相减后构造了一个带阻滤波器(先通过高反差保留让中高频通过,然后通过高斯模糊抑制高频,最终的结果是让中频通过)来提取缺陷分量。
此外,Halcon中关于傅里叶变换的例程还有:detect_mura_defects_texture.hdev
检测布料表面划痕
*《Halcon机器视觉算法原理与编程实战》16-1
* 将测试图像转化为单通道的灰度图像
rgb1_to_gray (Image, ImageGray)
* 1.创建一个高斯滤波器,用于将傅里叶转换后的图像进行滤波
gen_gauss_filter (GaussFilter, 3.0, 3.0, 0.0, 'none', 'rft', Width, Height)
* 对灰度图像进行颜色反转
invert_image (ImageGray, ImageInvert)
* 2.对反转后的图像进行傅里叶变换
rft_generic (ImageInvert, ImageFFT, 'to_freq', 'none', 'complex', Width)
* 3.对傅里叶图像做卷积,使用之前创建的高斯滤波器作为卷积核
convol_fft (ImageFFT, GaussFilter, ImageConvol)
* 4.将卷积后的傅里叶图像还原为空间域图像。可见图像的突变部分得到了增强
rft_generic (ImageConvol, ImageFiltered, 'from_freq', 'n', 'real', Width)
* 5.设置提取线条的参数,将图像中的有灰度差异的线条提取出来
calculate_lines_gauss_parameters (17, [25,3], Sigma, Low, High)
lines_gauss (ImageFiltered, Lines, Sigma, Low, High, 'dark', 'true', 'gaussian', 'true')
木板划痕检测
dev_update_off ()
dev_close_window ()
read_image (Image, '缺陷检测木板划痕提取.jpg')
* 1.彩色转灰度图
count_channels (Image, Channels)
if (Channels == 3 or Channels == 4)
rgb1_to_gray (Image, Image)
endif
get_image_size (Image, Width, Height)
dev_open_window_fit_size (0, 0, Width, Height, -1, -1, WindowHandle)
dev_display (Image)
* 2.傅里叶变换去背景
fft_generic (Image, ImageFFT, 'to_freq', -1, 'sqrt', 'dc_center', 'complex')
gen_rectangle2 (Rectangle1, 308.5, 176.56, rad(-0), 179.4, 7.725)
gen_rectangle2 (Rectangle2, 306.955, 175, rad(-90), 180.765, 4.68)
union2 (Rectangle1, Rectangle2, UnionRectangle)
paint_region (UnionRectangle, ImageFFT, ImageResult, 0, 'fill')
fft_generic (ImageResult, ImageFFT1, 'from_freq', 1, 'sqrt', 'dc_center', 'byte')
* 3.提取划痕
threshold (ImageFFT1, Regions, 5, 240)
connection (Regions, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 20, 99999)
union1 (SelectedRegions, RegionUnion)
dilation_rectangle1 (RegionUnion, RegionDilation, 5, 5)
connection (RegionDilation, ConnectedRegions1)
select_shape (ConnectedRegions1, SelectedRegions1, ['width','height'], 'and', [30,15], [150,100])
dilation_rectangle1 (SelectedRegions1, RegionDilation1, 11, 11)
union1 (RegionDilation1, RegionUnion1)
skeleton (RegionUnion1, Skeleton)
* 4.显示
dev_set_color ('red')
dev_display (Image)
dev_display (Skeleton)
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !