我知道这个问题可能与编程无关,但是如果我不了解图像处理背后的理论,那么我将永远无法在实践中实现某些东西。滤波器与图像进行卷积以减少噪声,因为它们可以计算像素邻域的加权平均值,并且在边缘检测中非常有用,因为您可以应用模糊并通过简单地与高斯函数。

但是任何人都可以向我解释一下,或者给我一些有关它们如何计算的参考吗? Canny的边缘检测器谈论的是5x5高斯滤波器,但是他们如何获得这些特定的数字呢?以及它们如何从连续卷积变为矩阵乘法?

评论

dsp.stackexchange.com/questions/2969/…

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

请参阅如何将卷积运算实现为矩阵向量乘法?。

#1 楼

为了使此操作有效,您需要想象图像被重新成形为矢量。
然后,此矢量在其左侧乘以卷积矩阵以获得模糊图像。
请注意,结果也是一个与输入大小相同的向量,即一个大小相同的图像。

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

让我们举个例子:图像大小为$ 3的框模糊乘以3 $像素大小$ 6 \ times 6 $像素。
重塑后的图像是一列36个选举元素,而模糊矩阵的大小为$ 36 \ times 36 $。到处都是0。
现在,考虑输入图像中坐标$(i,j)$的像素(为简单起见,不在其边界上)。通过对自身及其在位置$(i-1,j-1)的每个邻居施加$ 1/9 $的权重,可以获得模糊的对应物。 (i-1,j),(i-1,j + 1),\ ldots,(i + 1,j + 1)$。
在列向量中,像素$(i,j)$的位置为$ 6 * i + j $(假设字典顺序)。我们在模糊矩阵的第((6i + j)$)行中报告了权重$ 1/9 $。
对所有其他像素执行相同操作。

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

评论


$ \ begingroup $
链接无效。
$ \ endgroup $
–高特
2015年9月4日在11:31

#2 楼

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

从Stanford的CS231n中查看此链接,然后向下滚动至“以矩阵乘法实现”部分以获取详细信息。输入图像或激活图,将其与内核相乘,然后通过通常称为im2col的操作将其拉伸为新矩阵X的列。内核也被拉伸以填充权重矩阵W的行,以便在执行矩阵运算W * X时,所得矩阵Y具有所有卷积结果。最后,必须通过通常称为cal2im的操作将列转换回图像,从而重新调整Y矩阵的形状。

评论


$ \ begingroup $
这是一个很好的链接,谢谢!但是,优良作法是将链接中的重要摘录添加到答案中,这样即使链接断开,答案仍然有效。请考虑编辑您的答案以使其被接受!
$ \ endgroup $
–马特奥
17-2-23在18:27

#3 楼

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

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

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

评论


$ \ begingroup $
我不同意过滤是频域中的卷积这一事实。我们在这里讨论的滤波器类型是空间域中的卷积(也就是说,通过频域中的滤波器响应进行逐元素乘法)。
$ \ endgroup $
–小食
13年3月16日在16:51

$ \ begingroup $
感谢您纠正我写的内容。我进行了后续编辑。我想我应该在发布之前仔细检查我的答案。但是,我的大部分回答仍然有效。
$ \ endgroup $
–那雷什
13年3月17日在4:15

$ \ begingroup $
傅立叶变换确实将卷积变成乘法(反之亦然)。但是,它们是品脱乘法,而问题是关于通过重塑图像获得的矩阵矢量乘法。
$ \ endgroup $
–sansuiso
13年3月17日在16:34

$ \ begingroup $
我确实提到了运算符离散的原因是canny / sobel运算符获得5x5矩阵的原因。
$ \ endgroup $
–那雷什
13年3月17日在17:49

#4 楼

我在StackOverflow Q2080835 GitHub Repository(请参阅CreateImageConvMtx())中编写了一个可以解决此问题的函数。 />代码如下:



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的full):



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 same进行了验证。

完整的代码可在我的网站上找到。 StackOverflow Q2080835 GitHub存储库。