halcon软件自带了关于金属表面划痕缺陷检测的例程find_scratches_bandpass_fft.hdev
先附上代码截图并作了初步注释,本篇博客将讲解一下该例程的过程各个算子的使用是为了啥
/*首先*/
有趣的是,前面写了invert_image (Image, ImageInverted),后面做变换时都用转换后的图像,估计人们也是尝试发现Invert一下的图像更适合rft变换?
检测不均匀表面光照的表面缺陷思路就是:生成一个带通滤波器(卷积核),然后用它对在频域中的图像进行滤波[1](卷积,将卷积后的傅里叶图像还原为空间域图像,可见图像的突变部分得到了增强)以加强高频部分[2],然后变回时域做形态学处理。那么生成一个合适的滤波器和空间域⇿频域之间的变换成了关键步骤!
- 卷积(convolution): 两个信号的卷积的傅里叶变换等于它们的傅里叶变换的乘积。
频域结合空间,其实频域就是用波动观点看世界,看问题角度变了,光经过镜头其实发生的是傅立叶变换,此思想在傅立叶光学上有所阐述,就像光经棱镜分光,而光进入计算机内部,进行了采样和量化,然后我们用函数f(x,y)来表示这些数据描述。图像处理应用傅里叶变换就是将空间域(图像本身)转换至频率域。傅里叶变换可以将一个信号函数,分解一个一个三角函数的线性组合。由于任何周期函数都可以由多个正弦函数构成,那么按照这个思想,图像由f(x,y)来表示,那么这时你就可以拆成多个正弦函数构成,这样每个正弦函数都有一个自己的频率。
那什么时候需要使用傅里叶变换进行频域分析?
- 具有一定纹理特征的图像,纹理可以理解为条纹,如布匹、木板、纸张等材质容易出现。
- 需要提取对比度低或者信噪比低的特征。
- 图像尺寸较大或者需要与大尺寸滤波器进行计算,此时转换至频域计算,具有速度优势。因为空间域滤波为卷积过程(加权求和),频域计算直接相乘。
///***而图像在空间域和频域之间的转换主要有以下两个关键算子:
- rft_generic
- fft_generic
这两个算子的异同点:
- 都是对图像进行快速傅里叶变换的算子,都可以实现图像在空间域⇿频域之间变换;
- 算子参数'to_freq'是进行的是空间域→频域的变换,'from_freq'是频域→空间域的变换;
- 针对参数ResultType,如果是'to_freq',那么ResultType一般选择'complex';如果是'from_freq',ResultType一般选择'byte'(灰度图像)。
- rft_generic算子的输入图像是实值函数,fft_generic的输入图像是复数函数;从输出的结果来看,rft_generic只需要计算和存储了左半边的复数图像信息就可以了,因为右半边是共轭对称[3]的。因此从最终的输出我们可以看到,只有左上和左下有DC成分[4]。而fft_generic如果设定的是原点在左上角,那么就会在四个角上有DC成分。
- fft_generic算子可以通过参数Mode设置原点的位置:如果设置的是'dc_edge',那么原点在左上角;如果设置的是'dc_center',那么就会将原点平移到中心位置。fft_generic算子一般会设置为'dc_center'。对于rft_generic算子,因为没有设置项,所以默认原点位置为左上角。
注意:在对同一个图像进行空间域和频域的相互转换时,不要交叉使用这两个算子。
***生成滤波器主要有如下算子:
- gen_std_bandpass,
- gen_sin_bandpass,
- gen_gauss_filter,
- gen_mean_filter,
- gen_derivative_filter,
- gen_bandpass,
- gen_bandfilter,
- gen_highpass,
- gen_lowpass
注意:滤波器的参数选取还是难点,估计得有空看看数字信号处理的书,上面有各种滤波器说明。
/*其次*/
threshold(Image : Region : MinGray, MaxGray : ) 全局阈值分割
Image
是输入图像Region
是分割后的结果MinGray
和MaxGray
是输入的控制参数,分割的最小灰度值与最大灰度值
选择满足输入控制参数的灰度值之内的灰度值,当所有的像素点都满足这个区域的话,就有一个区域,当有多个像素段满足这个限制,每个像素段对应一个区域。
筛选掉杂点之后提取出保留区域所对应的图像。用lines_gauss算子将图像中的有灰度差异的线条提取出来(进行亚像素处理,先提取出亚像素轮廓,然后按照轮廓总长度进行轮廓筛选,从而得到划痕的亚像素轮廓。至此,已经得到划痕了)。然后再根据形态特征筛选一下,最后进行划痕区域的处理,先将亚像素轮廓转为区域,使用的算子是gen_region_contour_xld;得到划痕轮廓之后就可以将划痕标记出来了。
- Image:输入图像
- Lines:提取出的亚像素精度线条
- Sigma:应用的高斯平滑的系数
- Low:后滞阈值分割的低值
- High:后滞阈值分割的高值
- LightDark:提取图像中的亮色或者暗色线条,其实就是让你选择你要提取线条的类型,暗色还是亮色,选’dark’、 ‘light’
- ExtractWidth:是否提取线条的宽度(是否提取每条XLD轮廓线的线宽,true提取,false不提取)
- LineModel:提取线条的模式,有'none', 'bar-shaped', 'parabolic', 'gaussian' 四种
- CompleteJunctions:是否添加能够提取的接合点(junction)
当LineModel被设置为除'none'以外的其他值时,lines_gauss算子会补偿非对称线条(即在线条的中心两侧有不同对比度的线条),来校正提取出的线条的位置和宽度。对大多数应用来说,LineModel的'bar-shaped'参数是正确的选择,'parabolic'参数常用来提取边缘比较锐利的线条(比如背光照明的图像中的线条),'gaussian'则在线条边缘不那么锐利的时候使用。参数LineModel仅在参数ExtractWidth被设置为'true'时才有意义。
因为几何算法的原因,线条提取器(即lines_gauss算子)不可能提取出所有确定的接合点,当CompleteJunctions被设置为'true'时,算子会试图通过不同的 算法提取出那些能够提取出来的接合点。
This is done by immediately accepting line points that have a second derivative larger than High. Points that have a second derivative smaller than Low are rejected.
思路如图,也就说选取sigma和low high是还要看看处理图片的灰度大概情况。
算法通过计算带通滤波(高斯滤波核掩膜)与图像卷积的偏导数来确定图像上的每个像素点在x、y方向上的二次多项式参数。参数Sigma的值决定对图像平滑程度,高斯掩膜越大对图像平滑程度越深,这样会导致对线的定位失准。通常,在参数设置类似情况下,lines_gauss对线条定位要比lines_facet表现的好。二次多项式参数被用来计算每个像素的线方向。在垂直于线方向的二阶偏导数中的局部最大像素值被标记为线上的点,这些点组成了XLD轮廓。如果被标记点的二阶偏导数大于High设置的值,那么这个点直接成为XLD轮廓上的点;如果小于Low设置的值,直接被舍弃;如果介于Low、和High之间 …这里我没研究明白,直接上超链接:https://blog.csdn.net/qq_18620653/article/details/105467210
在选择高低阈值的时候,值得注意的是,二阶偏导数取决于线的振幅、宽度以及Sigma值的选择。二阶偏导数与振幅成线性关系,即振幅越大,二阶偏导数越大;对于线的宽度,呈现反指数依赖关系:线越宽,二阶偏导数越小,这和Sigma类似,即Sigma越大,二阶偏导数越小,这意味着选择的Sigma值大,对应选择的高低阈值就小。两个例子说明这个问题,对于一个线宽为5pixel、振幅大于100的线条,如果Sigma值为1.5,那么High的值应该大于14;而对于一个线宽为10pixel、振幅大于100的线条,如果Sigma值为3,那么High的值应该大于3.5,对于Low的值应该选在介于0.25High-0.5High之间较合适。参数Low和High可以根据要提取线条的灰度高低对比度(低对比度和高对比度)以及Sigma值分别计算得出,公式如下:
///***
对新手来说,使用lines_gauss算子最大的障碍是对参数调节无从下手,真的很痛苦。Halcon开发者在开发过程中感知到了这件事情(哈哈哈),所以配套开发了halcon calculate_lines_gauss_parameters
算子,用来确定lines_gauss输入参数,问题迎刃而解,傻瓜式用lines_gauss。halcon calculate_lines_gauss_parameters详细说明链接:https://blog.csdn.net/qq_18620653/article/details/105465852
***///
提取的线可以理解成是一个数据结构(线段和节点组成),也就是说可以根据节点将线分成一个个线段。
如果ExtractWidth设置为“false”,通过lines_gauss提取到的线上的每个点具有以下属性:
‘angle’:垂直于线条方向的角度
‘response’:二阶偏导数的幅度值
如果ExtractWidth设置为“true,除了’angle’、‘response’属性,下面的属性也被定义:
‘width_left’:线条左侧的宽度
‘width_right’:线条右侧的宽度
如果ExtractWidth 设置为’true’ ,并且LineModel 不选择’none’,那么除了以上四个属性外,下面两个属性也会被定义:
‘asymmetry’:线条两侧的不对称性
‘contrast’:线条的对比度
这里,如果非对称部分,即梯度较弱的部分在直线的右侧,则不对称性为正,而如果非对称部分在直线的左侧,则不对称性为负。对比度是由线条的灰度值和背景的灰度值之间的差异造成的。如果提取亮线,对比度为正;如果提取暗线,对比度为负。
以上属性值可以通过get_contour_attrib_xld[5]获取到
注意:
一般来说,特别是在需要提取线条宽度的时候,参数Sigma的选择应满足Sigma >= w/sqrt(3),w表示图像中线条的宽(线条直径的一半),最小允许值是Sigma >= w/2.5。例如,如果要提取的线宽为4pix(直径为8pix),Sigma >= 2.3比较合适。
另外需要注意的一点是,如果Sigma设置的太低,‘width_left’, ‘width_right’, ‘asymmetry’, ‘contrast’ 属性将设置为零。
注释
-
一般在频域里就是选择你想要的频段而已,被称为滤波,这个手法在halcon中有三个;一个是直接手画,然后paint_region;一个是涉及滤波器,然后进行滤波,这个对于初学者有点难度,先掌握常见滤波器;第三个就是调用power_real,对其进行blob解析。
- 一个重要的经验结论:低频代表图像整体轮廓,高频代表了图像噪声,中频代表图像边缘、纹理等细节。频率特征是图像的灰度变化特征,低频特征是灰度变化不明显,例如图像整体轮廓,高频特征是图像灰度变化剧烈,如图像边缘和噪声。
- 当一个函数f其实部为偶函数,虚部为奇函数时,此函数就为共轭对称函数,即f(x)的共轭等于f(-x)。
- 直流分量DC。
-
在提取线条的同时,lines_guass算子还提取线条上每个点的以下属性:
如果ExtractWidth='false'
(1)'angle':垂直于线条的方向的角度
(2)'response':二阶偏导数的幅度值
如果ExtractWidth='true',下面的属性也会被提取出来
(3)'width_left':线条左侧的宽度
(4)'width_right':线条右侧的宽度
如果LineModel没有被设置为'none',那么下面的属性也会被提取出来
(5)'asymmetry':线条的不对称度
(6)'contrast':线条的对比度
上面这些参数能够通过get_contour_attrib_xld算子得到。获得一些参数的函数。