卷积如何表示为矩阵乘法(矩阵形式)?


11

我知道这个问题可能与编程无关,但是如果我不了解图像处理背后的理论,那么我将永远无法在实践中实现某些功能。

如果我做对的话,高斯滤波器会与图像卷积以减少噪声,因为它们可以计算像素邻域的加权平均值,并且在边缘检测中非常有用,因为您可以应用模糊并同时得出图像只需与高斯函数的导数卷积即可。

但是,谁能给我解释一下,或者给我一些有关它们如何计算的参考呢?

例如,Canny的边缘检测器谈论的是5x5高斯滤波器,但是他们如何获得这些特定的数字呢?以及它们如何从连续卷积变为矩阵乘法?



添加了带有完整代码的答案,用于生成图像卷积矩阵。
罗伊

Answers:


3

为了使此操作正常进行,您需要想象将图像重塑为矢量。然后,将此向量在其左侧乘以卷积矩阵,以获得模糊图像。注意,结果也是与输入大小相同的矢量,即,大小相同的图像。

卷积矩阵的每一行对应于输入图像中的一个像素。它包含图像中所有其他像素对所考虑像素的模糊对应部分的贡献权重。

让我们举个例子:大小为像素的图像上的大小为像素的框模糊。整形后的图像是一列36个选举字,而模糊矩阵的大小是 x。3×36×636×36

  • 让我们将此矩阵初始化为0。
  • 现在,考虑输入图像中坐标的像素(为简单起见,不在其边界上)。通过对其自身和位置每个邻居施加的权重来获得其模糊对应物。(i,j)1/9(i1,j1);(i1,j),(i1,j+1),,(i+1,j+1)
  • 在列向量中,像素的位置为(假定字典顺序)。我们报告的重量的模糊矩阵的第一行。6 * + Ĵ 1 / 9 6 + Ĵ (i,j)6i+j1/9(6i+j)
  • 对所有其他像素执行相同操作。

在此博客文章中(从我的个人博客中)可以找到密切相关的过程(卷积+减法)的直观图示。


链接无效。
gauteh,2015年

2

对于图像或卷积网络的应用,为了更有效地使用现代GPU中的矩阵乘法器,通常将输入整形为激活矩阵的列,然后可以将这些矩阵与多个过滤器/内核立即相乘。

从Stanford的CS231n中查看此链接,然后向下滚动到“矩阵乘法实现”部分以了解详细信息。

该过程的工作方式是:将输入图像或激活图上的所有局部色块(将与内核相乘的色块)获取,然后通过通常称为im2col的操作将其扩展为新矩阵X的列。内核也被拉伸以填充权重矩阵W的行,以便在执行矩阵运算W * X时,所得矩阵Y具有所有卷积结果。最后,必须通过通常称为cal2im的操作将列转换回图像,从而重新调整Y矩阵的形状。


1
这是一个很好的链接,谢谢!但是,优良作法是将链接中的重要摘录添加到答案中,这样即使链接断开,答案仍然有效。请考虑编辑您的答案以使其被接受!
Matteo

1

时域中的卷积等于频域中的矩阵乘法,反之亦然。

滤波等效于时域中的卷积,因此等效于频域中的矩阵乘法。

至于5x5的地图或蒙版,它们来自离散化canny / sobel运算符。


2
我不同意过滤是频域中的卷积这一事实。我们在这里讨论的滤波器类型是空间域中的卷积(也就是说,通过频域中的滤波器响应进行逐元素乘法)。
pichenettes

感谢您纠正我写的内容。我进行了后续编辑。我想我应该在发布之前仔细检查我的答案。但是,我的大部分回答仍然有效。
Naresh 2013年

傅立叶变换确实确实将卷积变成乘法(反之亦然)。但是,它们是品脱乘法,而问题是关于通过重塑图像获得的矩阵矢量乘法。
sansuiso

我确实提到过运算符的离散性是canny / sobel运算符获得5x5矩阵的原因。
Naresh 2013年

1

我在StackOverflow Q2080835 GitHub Repository中编写了一个解决此问题的函数(请参阅CreateImageConvMtx())。
实际上,功能可以支持你喜欢的任何卷积形状- fullsamevalid

代码如下:

function [ mK ] = CreateImageConvMtx( mH, numRows, numCols, convShape )

CONVOLUTION_SHAPE_FULL  = 1;
CONVOLUTION_SHAPE_SAME  = 2;
CONVOLUTION_SHAPE_VALID = 3;

switch(convShape)
    case(CONVOLUTION_SHAPE_FULL)
        % Code for the 'full' case
        convShapeString = 'full';
    case(CONVOLUTION_SHAPE_SAME)
        % Code for the 'same' case
        convShapeString = 'same';
    case(CONVOLUTION_SHAPE_VALID)
        % Code for the 'valid' case
        convShapeString = 'valid';
end

mImpulse = zeros(numRows, numCols);

for ii = numel(mImpulse):-1:1
    mImpulse(ii)    = 1; %<! Create impulse image corresponding to i-th output matrix column
    mTmp            = sparse(conv2(mImpulse, mH, convShapeString)); %<! The impulse response
    cColumn{ii}     = mTmp(:);
    mImpulse(ii)    = 0;
end

mK = cell2mat(cColumn);


end

我还创建了一个函数来创建图像过滤矩阵(类似于MATLAB的想法imfilter()):

function [ mK ] = CreateImageFilterMtx( mH, numRows, numCols, operationMode, boundaryMode )
%UNTITLED6 Summary of this function goes here
%   Detailed explanation goes here

OPERATION_MODE_CONVOLUTION = 1;
OPERATION_MODE_CORRELATION = 2;

BOUNDARY_MODE_ZEROS         = 1;
BOUNDARY_MODE_SYMMETRIC     = 2;
BOUNDARY_MODE_REPLICATE     = 3;
BOUNDARY_MODE_CIRCULAR      = 4;

switch(operationMode)
    case(OPERATION_MODE_CONVOLUTION)
        mH = mH(end:-1:1, end:-1:1);
    case(OPERATION_MODE_CORRELATION)
        % mH = mH; %<! Default Code is correlation
end

switch(boundaryMode)
    case(BOUNDARY_MODE_ZEROS)
        mK = CreateConvMtxZeros(mH, numRows, numCols);
    case(BOUNDARY_MODE_SYMMETRIC)
        mK = CreateConvMtxSymmetric(mH, numRows, numCols);
    case(BOUNDARY_MODE_REPLICATE)
        mK = CreateConvMtxReplicate(mH, numRows, numCols);
    case(BOUNDARY_MODE_CIRCULAR)
        mK = CreateConvMtxCircular(mH, numRows, numCols);
end


end


function [ mK ] = CreateConvMtxZeros( mH, numRows, numCols )
%UNTITLED6 Summary of this function goes here
%   Detailed explanation goes here

numElementsImage    = numRows * numCols;
numRowsKernel       = size(mH, 1);
numColsKernel       = size(mH, 2);
numElementsKernel   = numRowsKernel * numColsKernel;

vRows = reshape(repmat(1:numElementsImage, numElementsKernel, 1), numElementsImage * numElementsKernel, 1);
vCols = zeros(numElementsImage * numElementsKernel, 1);
vVals = zeros(numElementsImage * numElementsKernel, 1);

kernelRadiusV = floor(numRowsKernel / 2);
kernelRadiusH = floor(numColsKernel / 2);

pxIdx       = 0;
elmntIdx    = 0;

for jj = 1:numCols
    for ii = 1:numRows
        pxIdx = pxIdx + 1;
        for ll = -kernelRadiusH:kernelRadiusH
            for kk = -kernelRadiusV:kernelRadiusV
                elmntIdx = elmntIdx + 1;

                pxShift = (ll * numCols) + kk;

                if((ii + kk <= numRows) && (ii + kk >= 1) && (jj + ll <= numCols) && (jj + ll >= 1))
                    vCols(elmntIdx) = pxIdx + pxShift;
                    vVals(elmntIdx) = mH(kk + kernelRadiusV + 1, ll + kernelRadiusH + 1);
                else
                    vCols(elmntIdx) = pxIdx;
                    vVals(elmntIdx) = 0; % See the accumulation property of 'sparse()'.
                end
            end
        end
    end
end

mK = sparse(vRows, vCols, vVals, numElementsImage, numElementsImage);


end


function [ mK ] = CreateConvMtxSymmetric( mH, numRows, numCols )
%UNTITLED6 Summary of this function goes here
%   Detailed explanation goes here

numElementsImage    = numRows * numCols;
numRowsKernel       = size(mH, 1);
numColsKernel       = size(mH, 2);
numElementsKernel   = numRowsKernel * numColsKernel;

vRows = reshape(repmat(1:numElementsImage, numElementsKernel, 1), numElementsImage * numElementsKernel, 1);
vCols = zeros(numElementsImage * numElementsKernel, 1);
vVals = zeros(numElementsImage * numElementsKernel, 1);

kernelRadiusV = floor(numRowsKernel / 2);
kernelRadiusH = floor(numColsKernel / 2);

pxIdx       = 0;
elmntIdx    = 0;

for jj = 1:numCols
    for ii = 1:numRows
        pxIdx = pxIdx + 1;
        for ll = -kernelRadiusH:kernelRadiusH
            for kk = -kernelRadiusV:kernelRadiusV
                elmntIdx = elmntIdx + 1;

                pxShift = (ll * numCols) + kk;

                if(ii + kk > numRows)
                    pxShift = pxShift - (2 * (ii + kk - numRows) - 1);
                end

                if(ii + kk < 1)
                    pxShift = pxShift + (2 * (1 -(ii + kk)) - 1);
                end

                if(jj + ll > numCols)
                    pxShift = pxShift - ((2 * (jj + ll - numCols) - 1) * numCols);
                end

                if(jj + ll < 1)
                    pxShift = pxShift + ((2 * (1 - (jj + ll)) - 1) * numCols);
                end

                vCols(elmntIdx) = pxIdx + pxShift;
                vVals(elmntIdx) = mH(kk + kernelRadiusV + 1, ll + kernelRadiusH + 1);

            end
        end
    end
end

mK = sparse(vRows, vCols, vVals, numElementsImage, numElementsImage);


end


function [ mK ] = CreateConvMtxReplicate( mH, numRows, numCols )
%UNTITLED6 Summary of this function goes here
%   Detailed explanation goes here

numElementsImage    = numRows * numCols;
numRowsKernel       = size(mH, 1);
numColsKernel       = size(mH, 2);
numElementsKernel   = numRowsKernel * numColsKernel;

vRows = reshape(repmat(1:numElementsImage, numElementsKernel, 1), numElementsImage * numElementsKernel, 1);
vCols = zeros(numElementsImage * numElementsKernel, 1);
vVals = zeros(numElementsImage * numElementsKernel, 1);

kernelRadiusV = floor(numRowsKernel / 2);
kernelRadiusH = floor(numColsKernel / 2);

pxIdx       = 0;
elmntIdx    = 0;

for jj = 1:numCols
    for ii = 1:numRows
        pxIdx = pxIdx + 1;
        for ll = -kernelRadiusH:kernelRadiusH
            for kk = -kernelRadiusV:kernelRadiusV
                elmntIdx = elmntIdx + 1;

                pxShift = (ll * numCols) + kk;

                if(ii + kk > numRows)
                    pxShift = pxShift - (ii + kk - numRows);
                end

                if(ii + kk < 1)
                    pxShift = pxShift + (1 -(ii + kk));
                end

                if(jj + ll > numCols)
                    pxShift = pxShift - ((jj + ll - numCols) * numCols);
                end

                if(jj + ll < 1)
                    pxShift = pxShift + ((1 - (jj + ll)) * numCols);
                end

                vCols(elmntIdx) = pxIdx + pxShift;
                vVals(elmntIdx) = mH(kk + kernelRadiusV + 1, ll + kernelRadiusH + 1);

            end
        end
    end
end

mK = sparse(vRows, vCols, vVals, numElementsImage, numElementsImage);


end


function [ mK ] = CreateConvMtxCircular( mH, numRows, numCols )
%UNTITLED6 Summary of this function goes here
%   Detailed explanation goes here

numElementsImage    = numRows * numCols;
numRowsKernel       = size(mH, 1);
numColsKernel       = size(mH, 2);
numElementsKernel   = numRowsKernel * numColsKernel;

vRows = reshape(repmat(1:numElementsImage, numElementsKernel, 1), numElementsImage * numElementsKernel, 1);
vCols = zeros(numElementsImage * numElementsKernel, 1);
vVals = zeros(numElementsImage * numElementsKernel, 1);

kernelRadiusV = floor(numRowsKernel / 2);
kernelRadiusH = floor(numColsKernel / 2);

pxIdx       = 0;
elmntIdx    = 0;

for jj = 1:numCols
    for ii = 1:numRows
        pxIdx = pxIdx + 1;
        for ll = -kernelRadiusH:kernelRadiusH
            for kk = -kernelRadiusV:kernelRadiusV
                elmntIdx = elmntIdx + 1;

                pxShift = (ll * numCols) + kk;

                if(ii + kk > numRows)
                    pxShift = pxShift - numRows;
                end

                if(ii + kk < 1)
                    pxShift = pxShift + numRows;
                end

                if(jj + ll > numCols)
                    pxShift = pxShift - (numCols * numCols);
                end

                if(jj + ll < 1)
                    pxShift = pxShift + (numCols * numCols);
                end

                vCols(elmntIdx) = pxIdx + pxShift;
                vVals(elmntIdx) = mH(kk + kernelRadiusV + 1, ll + kernelRadiusH + 1);

            end
        end
    end
end

mK = sparse(vRows, vCols, vVals, numElementsImage, numElementsImage);


end

该代码已针对MATLAB进行了验证imfilter()

完整代码可在我的StackOverflow Q2080835 GitHub存储库中找到

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.