理论基础
一、频率特征是图像的灰度变化特征,低频特征是灰度变化不明显,例如图像整体轮廓,高频特征是图像灰度变化剧烈,如图像边缘和噪声。一个重要的经验结论:低频代表图像整体轮廓,高频代表了图像噪声,中频代表图像边缘、纹理等细节。什么时候使用傅里叶变换进行频域分析?
具有一定纹理特征的图像,纹理可以理解为条纹,如布匹、木板、纸张等材质容易出现。
需要提取对比度低或者信噪比低的特征。
图像尺寸较大或者需要与大尺寸滤波器进行计算,此时转换至频域计算,具有速度优势。因为空间域滤波为卷积过程(加权求和),频域计算直接相乘。
二、在halcon中,使用频域进行检测,有两个步骤是比较关键的:
一个是生成合适的滤波器;
一个是空间域和频域之间的转换。
生成滤波器主要有如下算子:
gen_std_bandpass,
gen_sin_bandpass,
gen_gauss_filter,
gen_mean_filter,
gen_derivative_filter,
gen_bandpass,
gen_bandfilter,
gen_highpass,
gen_lowpass
空间域和频域之间的转换,主要有如下两个关键算子:
rft_generic
fft_generic
这两个算子的共同点:
这两个算子都是进行快速傅里叶变换的算子
这两个算子都可以进行空间域-》频域和频域-》空间域的变换,只需要针对参数Direction分别进行选择,'to_freq'是进行的是空间域-》频域的变换,'from_freq'是频域-》空间域的变换。
针对参数ResultType,如果是'to_freq',那么ResultType一般选择'complex';如果是'from_freq',ResultType一般选择'byte'(灰度图像)。
这两个算子的不同点:
rft_generic算子的输入图像是实值函数,fft_generic的输入图像是复数函数;从输出的结果来看,rft_generic只需要计算和存储了左半边的复数图像信息就可以了,因为右半边是共轭对称的。因此从最终的输出我们可以看到,只有左上和左下有DC成分。而fft_generic如果设定的是原点在左上角,那么就会在四个角上有DC成分。
fft_generic算子可以通过参数Mode设置原点的位置:如果设置的是'dc_edge',那么原点在左上角;如果设置的是'dc_center',那么就会将原点平移到中心位置。fft_generic算子一般会设置为'dc_center'。对于rft_generic算子,因为没有设置项,所以默认原点位置为左上角。
针对同一个图像,进行空间域-》频域、频域-》空间域的转换的时候,如果使用的是rft_generic算子,那么两个转换就都使用该算子;如果使用的是fft_generic算子,那么两个转换也都使用该算子,在对同一个图像进行空间域和频域的相互转换时,不要交叉使用这两个算子。
当然,从空间域到频域的转换,也可以使用算子fft_image,这个算子也是快速傅里叶变换,其实际效果相当于:
fft_generic(Image,ImageFFT,'to_freq',-1,'sqrt','dc_center','complex')
举例说明
1、检测布料表面划痕
*《Halcon机器视觉算法原理与编程实战》16-1 *清空当前窗口 dev_close_window () *读取测试图像 read_image (Image, 'data/cloth1') *获取图像的宽 get_image_size (Image, Width, Height) *创建显示窗口,并设置窗口及绘制参数 dev_open_window_fit_size (0, 0, Width, Height, -1, -1, WindowHandle) dev_display (Image) dev_set_draw ('margin') dev_set_line_width (3) dev_set_color ('red') *创建一个高斯滤波器,用于将傅里叶转换后的图像进行滤波 gen_gauss_filter (GaussFilter, 3.0, 3.0, 0.0, 'none', 'rft', Width, Height) *开始检测 *将测试图像转化为单通道的灰度图像 rgb1_to_gray (Image, ImageGray) *对灰度图像进行颜色反转 invert_image (ImageGray, ImageInvert) *对反转后的图像进行傅里叶变换 rft_generic (ImageInvert, ImageFFT, 'to_freq', 'none', 'complex', Width) *对傅里叶图像做卷积,使用之前创建的高斯滤波器作为卷积核 convol_fft (ImageFFT, GaussFilter, ImageConvol) *将卷积后的傅里叶图像还原为空间域图像。可见图像的突变部分得到了增强 rft_generic (ImageConvol, ImageFiltered, 'from_freq', 'n', 'real', Width) *设置提取线条的参数 calculate_lines_gauss_parameters (17, [25,3], Sigma, Low, High) *将图像中的有灰度差异的线条提取出来 lines_gauss (ImageFiltered, Lines, Sigma, Low, High, 'dark', 'true', 'gaussian', 'true') *将提取出的结果显示出来 dev_display (Image) dev_display (Lines)
2、傅里叶变换之划痕检测
*微信公众号--机器视觉那些事儿 *检测-FFT傅里叶变换之划痕检测 *对于检测表面是具有一定纹理的比如:布匹、皮革、塑料等,针对这一类表面的检测就不能单纯依靠帧差或者背景差来完成, *这种情况下,通过将图像变换到频域进行处理,提取缺陷分量后反变换到时域,然后通过Blob分析获得缺陷的具体位置。 *重点说明: *频域处理部分的过程为: *1)先生成一个滤波器,此处生成的是正弦形状的带通滤波,使用的算子为gen_sin_bandpass。 *2)然后再进行图像的傅里叶变换,使用的算子为rft_generic,此处需要注意参数的使用。 *3)使用之前的滤波器,对图像在频域进行卷积计算,从而增强高频信息。 *4)最后对图像进行傅里叶反变换,使用的算子依然是rft_generic,注意和第二步中的参数进行区分。在这一步,即得到了我们平时进行Blob分析的图像。 *之后的处理分为三部分: *1)第一部分,先进行Blob分析后,筛选掉一些杂点。然后提取出保留区域所对应的图像。 *2)第二部分进行亚像素处理,先提取出亚像素轮廓,使用的是lines_gauss算子;然后按照轮廓总长度进行轮廓筛选,从而得到划痕的亚像素轮廓。至此,已经得到划痕了。 *3)第三部分进行划痕区域的处理,先将亚像素轮廓转为区域,使用的算子是gen_region_contour_xld;得到划痕轮廓之后就可以将划痕标记出来了。 * 首先,创建合适的带通滤波 * 然后,输入图像在频域中进行傅里叶变换和滤波,以便增强高频信息 * 最后,再被转换回空间域,增强的缺陷通过形态学进行之后的处理 dev_update_off () dev_close_window () * 读图像 read_image (Image, '检测-FFT傅里叶变换之划痕检测.png') *彩色转灰度图 count_channels (Image, Channels) if (Channels == 3 or Channels == 4) rgb1_to_gray (Image, Image) endif * 翻转图像,亮变暗 暗变量 invert_image (Image, ImageInverted) * 获取图像宽高 get_image_size (Image, Width, Height) * dev_open_window (0, 0, Width, Height, 'black', WindowHandle) dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) set_display_font (WindowHandle, 16, 'mono', 'true', 'false') dev_display (Image) * * Optimize the speed of the fast fourier transform * 优化快速傅里叶变换的速度 * Message := 'Optimize the speed of the fast fourier transform.' * Message[1] := 'Please wait...' * disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true') * optimize_rft_speed (Width, Height, 'standard') * disp_continue_message (WindowHandle, 'black', 'true') * stop () * * Enhance the scratches by filtering in the frequency domain * 通过在频域中滤波来增强划痕 * 生成具有正弦形状的带通滤波器 gen_sin_bandpass (ImageBandpass, 0.4, 'none', 'rft', Width, Height) * 对一幅图片的实部进行快速傅里叶变换的计算,将图像转为傅里叶图像 * 参数为'to_freq',输出图像为复数形式 * ImageInverted:输入的图片(输入图像需要为背景为暗,前景为亮的图像,一般需要先要将原始图像反转) * ImageFFT:傅里叶变换后输出的图片 * 'to_freq':变换方向,傅里叶变换 * 'none':变换因子的规范 * 'complex':输出图片的数据类型,输出图像为复数形式 * Width: 图像的宽 rft_generic (ImageInverted, ImageFFT, 'to_freq', 'none', 'complex', Width) * 对图片用滤波器在频域进行卷积运算 * ImageFFT: 输入的图片 * ImageBandpass:频域滤波器 * ImageConvol:计算后的输出图像 convol_fft (ImageFFT, ImageBandpass, ImageConvol) * 对滤波后的图片进行傅里叶反变换 * 参数为'from_freq',输入图像为复数形式 rft_generic (ImageConvol, Lines, 'from_freq', 'n', 'byte', Width) * * Segment the scratches by using morphology * 使用形态分割划痕 * 二值化 threshold (Lines, Region, 5, 255) * 获得连通域 connection (Region, ConnectedRegions) * 特征直方图,按照面积特征选择区域 select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 5, 5000) * 膨胀 dilation_circle (SelectedRegions, RegionDilation, 5.5) * 区域联合成一个区域 union1 (RegionDilation, RegionUnion) * 抠图 reduce_domain (Image, RegionUnion, ImageReduced) * 检测线条及其宽度 * 输出亚像素直线轮廓 lines_gauss (ImageReduced, LinesXLD, 0.8, 3, 5, 'dark', 'false', 'bar-shaped', 'false') * 联合直线亚像素轮廓 union_collinear_contours_xld (LinesXLD, UnionContours, 40, 3, 3, 0.2, 'attr_keep') * 按照轮廓的总长度来选择亚像素轮廓 select_shape_xld (UnionContours, SelectedXLD, 'contlength', 'and', 15, 1000) * 由亚像素轮廓生成区域 gen_region_contour_xld (SelectedXLD, RegionXLD, 'filled') * 区域联合 union1 (RegionXLD, RegionUnion) * 膨胀 dilation_circle (RegionUnion, RegionScratches, 10.5) * * Display the results * 显示标记出来的的直线划痕 dev_set_draw ('margin') dev_set_line_width (3) dev_set_colored (12) dev_display (Image) dev_display (RegionScratches)