# Replicate MATLAB's conv2() in Frequency Domain

When conv2d is on same mode, the image needs no padding, because the result is the same size as the image.

When conv2d is on full mode, the result is (image_width + kernel_width -1) * (image_height + kernel_height -1).

Then how do I pad the image?

• Does it help? stackoverflow.com/questions/12253984/… Commented Apr 29, 2021 at 3:24
• @ZRHan Very helpful! Thank you so much! Should I delete my question because of duplication? Commented Apr 29, 2021 at 7:06
• I think closing it is OK. Commented Apr 29, 2021 at 8:16
– Royi
Commented Apr 29, 2021 at 8:37
• – Royi
Commented Apr 29, 2021 at 9:01

I created a MATLAB function which is basically conv2() applied in Frequency Domain:

function [ mO ] = ImageConvFrequencyDomain( mI, mH, convShape )
% ----------------------------------------------------------------------------------------------- %
% [ mO ] = ImageConvFrequencyDomain( mI, mH, convShape )
% Applies Image Convolution in the Frequency Domain.
% Input:
%   - mI                -   Input Image.
%                           Structure: Matrix.
%                           Type: 'Single' / 'Double' (Single Channel).
%                           Range: (-inf, inf).
%   - mH                -   Filtering Kernel.
%                           Structure: Matrix.
%                           Type: 'Single' / 'Double'.
%                           Range: (-inf, inf).
%   - convShape         -   Convolution Shape.
%                           Sets the convolution shape.
%                           Structure: Scalar.
%                           Type: 'Single' / 'Double'.
%                           Range: {1, 2, 3}.
% Output:
%   - mI                -   Output Image.
%                           Structure: Matrix (Single Channel).
%                           Type: 'Single' / 'Double'.
%                           Range: (-inf, inf).
% References:
%   1.  MATLAB's 'conv2()' - https://www.mathworks.com/help/matlab/ref/conv2.html.
% Remarks:
%   1.  A
% TODO:
%   1.
%   Release Notes:
%   -   1.0.000     29/04/2021  Royi Avital     [email protected]
%       *   First release version.
% ----------------------------------------------------------------------------------------------- %

CONV_SHAPE_FULL     = 1;
CONV_SHAPE_SAME     = 2;
CONV_SHAPE_VALID    = 3;

numRows     = size(mI, 1);
numCols     = size(mI, 2);

numRowsKernel = size(mH, 1);
numColsKernel = size(mH, 2);

switch(convShape)
case(CONV_SHAPE_FULL)
numRowsFft  = numRows + numRowsKernel - 1;
numColsFft  = numCols + numColsKernel - 1;
firstRowIdx = 1;
firstColIdx = 1;
lastRowIdx  = numRowsFft;
lastColdIdx = numColsFft;
case(CONV_SHAPE_SAME)
numRowsFft  = numRows + numRowsKernel;
numColsFft  = numCols + numColsKernel;
firstRowIdx = ceil((numRowsKernel + 1) / 2);
firstColIdx = ceil((numColsKernel + 1) / 2);
lastRowIdx  = firstRowIdx + numRows - 1;
lastColdIdx = firstColIdx + numCols - 1;
case(CONV_SHAPE_VALID)
numRowsFft = numRows;
numColsFft = numCols;
firstRowIdx = numRowsKernel;
firstColIdx = numColsKernel;
% The Kernel when transformed is shifted (Namely its (0, 0) is top
% left not middle).
lastRowIdx  = numRowsFft;
lastColdIdx = numColsFft;
end

mO = ifft2(fft2(mI, numRowsFft, numColsFft) .* fft2(mH, numRowsFft, numColsFft), 'symmetric');
mO = mO(firstRowIdx:lastRowIdx, firstColIdx:lastColdIdx);

end

It is fully compatible and validated.
The full code is available on my StackExchange Signal Processing Q74803 GitHub Repository (Look at the SignalProcessing\Q74803 folder).

• I don't get how you're matching conv2 without centering mH, does MATLAB do something different with fft2 or ifft2? Commented Apr 17, 2023 at 11:03
• @OverLordGoldDragon, I am not sure what you mean.
– Royi
Commented Apr 17, 2023 at 11:04
• Nevermind, you compensate by changing unpad indices. Looks good. Commented Apr 17, 2023 at 11:29
• @OverLordGoldDragon, If I got you write, then indeed, the fftshift() you use in your methods is not needed. For performance it is better not to use it. Just need to understand the axis system of the transformation.
– Royi
Commented Apr 17, 2023 at 11:39
• If mH isn't reused and we're not aiming for circular convolution, yes. But it's more confusing to debug and doesn't enjoy offset-invariance in strided context. I'm so used to it that I forgot this alt case, I should mention it in my answer. Commented Apr 17, 2023 at 11:44