处理频域卷积的循环边界条件以应用线性卷积

信息处理 图像处理 Python 自由度 卷积 快速卷积
2022-02-05 07:47:52
def fft_convolution(image, kernel):
    imageC = image.copy()
    kSize = (kernel.size // 2)+1
    kernelShape = tuple(ti//2 for ti in kernel.shape)
    centery ,centerx = kernelShape
    imagePad = np.pad(image, kSize, mode = "edge")
    imageC = np.fft.fft2(imagePad)
    kernel= np.fft.fft2(kernel, imagePad.shape)
    output = np.real(np.fft.ifft2(np.multiply(imageC, kernel)))
    return output[kSize+centery:-kSize+centery, kSize+centerx:-kSize+centerx]

所以我的代码目前在 kernelSize // 2 + 1 的所有侧面拍摄图像 M x N 和内核 P x K Pad,然后使内核从 fft2 返回为正确的形状

然后我根据我的垫尺寸使用内核的形状进行裁剪,我真的不知道为什么,可能与圆形卷积有关。

但这真的很令人困惑,对我来说毫无意义,尤其是当我们进行裁剪时,我不得不处理循环卷积的伪影。

我想知道完美应用裁剪和填充的正确方法是什么,考虑到我希望我的图像保持相同大小并且必须对边缘进行插值而不是使用恒定的零值。

1个回答

我创建了一个基本上conv2()应用于频域的 MATLAB 函数:

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     RoyiAvital@yahoo.com
%       *   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


它完全兼容并经过验证。
完整代码可在我的StackExchange Signal Processing Q74803 GitHub 存储库中找到(查看SignalProcessing\Q74803文件夹)。

如果您想应用具有恒定/镜像或复制边界模式的图像过滤器,只需在函数之前填充图像并使用CONV_SHAPE_VALIDas convShape没有填充,应用的边界条件是周期性的。