1. 理解傅里叶变换
为了理解频域处理的相关概念,我向大家推荐知乎上面的一篇文章:
傅里叶分析之掐死教程(完整版)https://zhuanlan.zhihu.com/p/19763358
2. 读懂频谱图
我们先仔细地观察下面的几幅图片,左边的是图像在空间域中,右边是图像在频域中:
从上面这几幅图中我们可以观察到以下几个现象:
- 频域的图像总是轴对称图像;
- 频域中的亮线总是与空间域中的边缘垂直;
- 低频集中在图像中心(低频指变化不剧烈的地方),由频谱图中心向四周扩散,频率不断增加;
- 频谱图中心不是图像中心;
- 频谱图中每一点表示的是频率而不是像素;
可能我们从图像中还能观察到更多的细节,总结出更多的规律,但在这里就不再继续阐述了。由于我们一直以来都是在空间域中观察图像,突然要理解图像的频域肯定是有困难的,简单的图像或许还可以想象其对应的频谱图,但是如果要想出复杂图像的频谱,那简直是不可能完成的任务。比如下图:
但是我们至少能够在频谱中观察到一条竖直的亮线,说明图像在空间域中有一条水平的边缘,但也仅此而已。
理解不了,那就欣赏吧!
3. 使用频域处理图像的案例
3.1 脏污检测
detect_mura_defects_blur.hdev
- 如下图,在塑料薄膜上有一些线条型的脏污,在空间域中向提取脏污的区域是比较困难的,所以我们就想到了在频域中去处理它。
ImageFiles := []
ImageFiles[0] := './mura_defects_blur_01.png'
ImageFiles[1] := './mura_defects_blur_02.png'
ImageFiles[2] := './mura_defects_blur_03.png'
* 根据要提取的线的最大宽度和对比度,计算Sigma和高低阈值
calculate_lines_gauss_parameters (42.5, [25,5], Sigma, Low, High)
for Index := 0 to |ImageFiles| - 1 by 1
read_image (Image, ImageFiles[Index])
* 这种脏污的提取可以考虑在频域中处理
* 让前景和背景分离,然后再提取脏污的区域
* 彩色图转灰度 这里拆通道更好,因为方格会影响脏污的提取
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)
stop()
endfor
3.2 检测表面微小凸起
detect_indent_fft.hdev
- 频域这种方法可以处理这种细微的缺陷;
- 这个案例使用了两个高斯滤波器一同构造了一个差分滤波器,如下图所示:
- 在频域处理完成转会空间域之后,halcon用了一个能扩大亮点区域的函数:gray_range_rect 辅助后面的二值化,最终完成了缺陷的检测,这个函数可以说是点睛之笔。
dev_update_off ()
dev_set_draw ('margin')
dev_set_line_width (1)
dev_set_color ('blue')
list_files ('./', ['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)
read_image (Image, ImageFiles[0])
get_image_size (Image, Width, Height)
* 使用高斯滤波器组合成一个差分滤波器
sigma1:=3
sigma2:=10
gen_gauss_filter (ImageGauss1, sigma1, sigma1, 0, 'none', 'dc_center', Width, Height)
gen_gauss_filter (ImageGauss2, sigma2, sigma2, 0, 'none', 'dc_center', Width, Height)
sub_image (ImageGauss1, ImageGauss2, ImageSub, 1, 0)
for Index := 0 to |ImageFiles| - 1 by 1
read_image (Image, ImageFiles[Index])
rgb1_to_gray (Image, GrayImage)
* 先看一下图像在频域中的表现
fft_generic (GrayImage, ImageFFT, 'to_freq', -1, 'none', 'dc_center', 'complex')
* 频域乘积(相当于空间域卷积)
convol_fft (ImageFFT, ImageSub, ImageConvol)
* 频域转空间域
fft_generic (ImageConvol, ImageFFT1, 'from_freq', 1, 'n', 'dc_center', 'real')
* 原图矩形内的灰度值范围(max-min)作为输出图像像素值,扩大了亮的部分
gray_range_rect (ImageFFT1, 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 (GrayImage)
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
stop()
endfor
3.3 检测磨砂表面的缺陷
detect_mura_defects_texture.hdev
halcon提供的这个案例使用了以下方法:
- 高斯滤波差分
- 分水岭域
- 图像能量
dev_update_off ()
dev_set_draw ('margin')
dev_set_line_width (2)
ImageFiles := []
ImageFiles[0] := './mura_defects_texture_01.png'
ImageFiles[1] := './mura_defects_texture_02.png'
for Index := 0 to |ImageFiles| - 1 by 1
read_image (Image, ImageFiles[Index])
get_image_size (Image, Width, Height)
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)
stop()
endfor
dev_update_on ()