Reputation: 377
I'm implementing 2-D convolution by using FFT. Here is my code:
img = im2single(imread('dog.bmp'));
filter = fspecial('gaussian', 53, 3);
F = fft2(img);
mask = fft2(filter, size(img, 1), size(img, 2));
filtered_img = ifft2(F .* mask);
imshow(real(filtered_img));
Here is the original image:
Why does this happen? How can I fix it? Please help me. Many thanks.
Upvotes: 1
Views: 361
Reputation: 112659
The problem is that the frequency response of your filter,
filter = fspecial('gaussian', 53, 3);
is shifted with respect to the origin of coordinates. To check this, use
imagesc(filter)
You can see that filter
has its maximum at coordinates 27
,27
, not 1
,1
. The amount of shift can be computed (for odd-sized impulse response) as (size(filter)-1)/2
, which in this case gives [26, 26]
.
In the following, I'll use this image as an example:
img = im2single(imread('cameraman.tif')); % size [256, 256]
Later in your code, when you call fft2
with the optional second and third arguments,
mask = fft2(filter, size(img, 1), size(img, 2));
the first input is implicitly right-padded with zeros in each dimension to match the specified size, which is 256
for my example img
. You can check this by plotting its inverse transform:
imagesc(real(ifft2(mask)))
After that, your code multiplies the transforms of the original image and the impulse response of the filter, and then transforms back. That's equivalent to a circular convolution of the original image img
and the impulse response filter
. Now, since the latter is shifted by 26
samples with respect to the origin in both dimensions, that produces a circular shift in the output signal. This is the problem you observe.
To solve this, you need to correct for the shifting, while taking into account the padding.
The simplest way is to manually do the padding (instead of letting fft2
do it implicitly) and at the same time circularly shift (size(filter)-1)/2
samples to the left in each dimension:
filter_corrected = filter; % initiallize
filter_corrected(size(img, 1), size(img, 2)) = 0; % this right-pads with zeros
shift = (size(filter)-1)/2; % as seen above
filter_corrected = filter_corrected( ...
mod((1:end)+shift(1)-1, end)+1, ...
mod((1:end)+shift(2)-1, end)+1); % this undoes the circular shifting
Now the corrected impulse response, filter_corrected
, has the required size and its maximum is at 1
, 1
(although it's hard to observe in the example):
imagesc(filter_corrected)
So you can use filter_corrected
in the rest of your code:
F = fft2(img);
mask = fft2(filter_corrected, size(img, 1), size(img, 2));
filtered_img = ifft2(F .* mask);
imshow(real(filtered_img));
With this, the original and filtered images are
imshow(img)
figure
imshow(real(filtered_img))
As additional notes,
filter
as a variable name, because that shadows the built-in function with the same name.Upvotes: 5