我正在尝试在Matlab中创建一个高通滤波器。我使用
生成高斯内核function kernel = compute_kernel(sigma,size)
[x,y] = meshgrid(-size/2:size/2,-size/2:size/2);
constant = 1/(2*pi*sigma*sigma);
kernel = constant*exp( -(y.^2 + x.^2 )/(2 * sigma * sigma));
kernel = (kernel - min(kernel(:)))./(max(kernel(:)) - min(kernel(:)));
end
然后在创建内核后,我用它来为图像创建一个低通滤波器(变量im2
):
g = compute_kernel(9,101);
im2_low = conv2(im2,g,'same');
据我所知,我可以使用从原始图像中减去滤波后的图像(在频域中)来提取高频率,使其相当于高通滤波器。
F = fft2(im2_low);
IM2 = fft2(im2);
IM2_high = IM2 - F;
figure; fftshow(IM2_high);
im2_high = ifft2(IM2_high);
figure; imshow(im2_high,[]);
但这似乎有些不对劲。当我查看高通滤波图像时,它似乎是一个颜色反转的模糊图像,而不是像我在网上看到的边缘定义的图像。我不确定我的进程是否错误,或者我是否只使用了高斯内核的错误值。
答案 0 :(得分:5)
您想要用于维护图像功能的任何内核(即您不想要某种程度的内容,但图像看起来像人类可识别的图像),您需要确保对内核做了一些事情:normalize它。
你似乎已经尝试过了,但你误解了内核中规范化的含义。不需要[0-1],他们的总和需要为1.
所以,拿你的代码:
im2=imread('https://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png');
im2=double(rgb2gray(im2));
sigma=9;
sizei=101;
[x,y] = meshgrid(-sizei/2:sizei/2,-sizei/2:sizei/2);
constant = 1/(2*pi*sigma*sigma);
kernel = constant*exp( -(y.^2 + x.^2 )/(2 * sigma * sigma));
%%%%%% NORMALIZATION
kernel=kernel/sum(kernel(:));
%%%%%%
im2_low = conv2(im2,kernel,'same');
F = fft2(im2_low);
IM2 = fft2(im2);
IM2_high = IM2 - F;
im2_high = ifft2(IM2_high);
figure; imshow(im2_high,[]);
但是,正如CrisLuengo所提到的,减法是一种在傅立叶域中不会改变的运算,因此答案就是
im2_high=im2-im2_low
答案 1 :(得分:4)
对于一个简短的问题,这是一个很长的答案。如果你想学点东西,请阅读它。
低通滤波器和高通滤波器都是线性滤波器。 线性滤波器可以通过卷积或在频域(a.k.a.Fierier域)中作为乘法应用于空间域。
确实,在傅立叶域中,低通滤波器内核和身份滤波器(全通滤波器)之间的差异是高通滤波器:
high_pass_filter = identity_filter - low_pass_filter
标识过滤器将是一个内核,其中每个元素都是1.过滤器由乘法应用,所以
IM2 * high_pass_filter = IM2 * ( identity_filter - low_pass_filter )
与
相同IM2 * high_pass_filter = IM2 - IM2 * low_pass_filter
(这里,正如在问题中,IM2
是图像im2
的傅立叶域表示;所有带黄色边框的东西都是方程,但是用伪代码编写,使用用于乘法的*
符号。
因此,OP想要应用低通滤波器并在傅里叶域中减去输入图像以获得高通滤波图像。
然而,properties of the Fourier transform之一是它是线性变换。这意味着
F(a*f + b*g) == a * F(f) + b * F(g)
(F(.)
傅立叶变换,a
和b
常数以及f
和g
函数)。设置a=1
和b=-1
以及g
低通过滤后的图片和f
输入图片,我们得到
F(im2 - im2_low) == F(im2) - F(im2_low)
即,空间域和傅里叶域中的减法是等价的。因此,如果计算空间域中的im2_low
,则无需转到傅立叶域进行减法。这两位代码产生相同的结果(达到数值精度):
F = fft2(im2_low);
IM2 = fft2(im2);
IM2_high = IM2 - F;
im2_high = ifft2(IM2_high);
im2_high = im2 - im2_low;
此外,卷积也是线性的。这意味着,如果您将上面的等式中的F(.)
视为卷积,那么这些等式仍然成立。你可以做这样的操作:
conv(f, h) - f == conv(f, h) - conv(f, 1) == conv(f, h-1)
这直接导致了空间域中高通滤波器的定义:
g = - compute_kernel(9,101);
g(51,51) = g(51,51) + 1;
im2_high2 = conv2(im2,g,'same');
您将看到max(max(abs(im2_high-im2_high2)))
产生的值非常接近0。
有关计算高斯滤波器的注意事项:
问题中发布的compute_kernel
函数通过直接评估2D高斯来计算2D滤镜内核。得到的滤波器内核为101x101像素,这意味着计算卷积需要101 * 101 * N
乘法和加法(MAD),其中N
是滤波图像中的像素数。然而,高斯滤波器是可分离的,这意味着只能在101 * 2 * N
个MAD中获得相同的结果(减少50倍!)。另外,对于sigma = 9
,也可以使用较小的内核。
高斯内核大小:
高斯函数永远不会达到零,但它很快达到非常接近零。在3*sigma
切断它时,很少会丢失它。我发现3西格玛是一个很好的平衡。在sigma = 9的情况下,3 sigma截止通向一个55像素(3 * sigma * 2 + 1)的内核。
高斯分离性:
可以通过将1D高斯相乘来获得多维高斯:
exp(-(y.^2+x.^2)/(2*sigma*sigma)) == exp(-(x.^2)/(2*sigma*sigma)) * exp(-(y.^2)/(2*sigma*sigma))
这可以更有效地实现卷积:
conv(f,h1*h2) == conv( conv(f,h1), h2 )
也就是说,使用列过滤器h1
对图像进行卷积,然后将结果与行过滤器h2
进行卷积,与使用2D过滤器h1*h2
卷积图像相同。在代码中:
sigma = 9;
sizei = ceil(3*sigma); % 3 sigma cutoff
g = exp(-(-sizei:sizei).^2/(2*sigma.^2)); % 1D Gaussian kernel
g = g/sum(g(:)); % normalize kernel
im2_low = conv2(g,g,im2,'same');
g2d = g' * g;
im2_low2 = conv2(im2,g2d,'same');
区别在于数值不精确:
max(max(abs(im2_low-im2_low2)))
ans = 1.3927e-12
您会在我的博客上找到a more detailed description about Gaussian filtering,以及some issues you can run into when using MATLAB's Image Processing Toolbox。