컨벌루션을 행렬 곱셈 (매트릭스 형식)으로 표현할 수있는 방법은 무엇입니까?


11

나는이 질문이 프로그래밍과 관련이 없을 수도 있다는 것을 알고 있지만 이미지 처리의 이론을 이해하지 못하면 실제로 무언가를 구현할 수는 없습니다.

내가 알면 가우시안 필터는 픽셀 주변의 가중 평균을 계산하기 때문에 노이즈 감소를위한 이미지와 관련이 있으며 가장자리를 감지하는 데 매우 유용합니다. 블러를 적용하고 동시에 가우스 함수의 미분과 간단히 연관됩니다.

그러나 누구든지 나를 설명하거나 그들이 어떻게 계산되는지에 대한 참조를 해 줄 수 있습니까?

예를 들어 Canny의 에지 검출기 는 5x5 Gaussian 필터에 대해 이야기하지만 어떻게 특정 숫자를 얻었습니까? 그리고 그들은 연속 컨볼 루션에서 행렬 곱셈으로 어떻게 이동 했습니까?



나는 대답을 추가 이미지 회선에 대한 매트릭스를 생성하기위한 전체 코드.
Royi

답변:


3

이 작업이 작동하려면 이미지가 벡터로 재구성된다고 상상해야합니다. 그 후,이 벡터는 그것의 왼쪽에 컨벌루션 매트릭스를 곱하여 흐릿한 이미지를 얻는다. 결과는 입력과 같은 크기의 벡터, 즉 같은 크기의 이미지입니다.

컨벌루션 행렬의 각 행은 입력 이미지의 한 픽셀에 해당합니다. 이미지의 다른 모든 픽셀이 고려한 픽셀의 흐릿한 부분에 기여한 가중치를 포함합니다.

예를 들어 보자 : 상자 흐림 크기 × 이미지 크기의 픽셀 6×6픽셀. 재 형성된 이미지는 36 개의 선으로 구성되며 블러 매트릭스의 크기는36×36.

  • 이 매트릭스를 어디서나 0으로 초기화합시다.
  • 이제 좌표의 픽셀을 고려하십시오 (나는,제이)입력 이미지 (간단 성을 위해 테두리가 아님). 흐리게 처리 된 부분은1/9 그 위치에서 자신과 각 이웃에게 (나는1,제이1);(나는1,제이),(나는1,제이+1),,(나는+1,제이+1).
  • 열 벡터에서 픽셀 (나는,제이) 위치가있다 6나는+제이(사전 순서를 가정). 우리는 무게를보고1/9 에서 (6나는+제이)블러 매트릭스의-번째 라인.
  • 다른 모든 픽셀과 동일하게 수행하십시오.

블로그 게시물 (개인 블로그에서) 과 밀접하게 관련된 프로세스 (컨볼 루션 + 빼기)를 시각적으로 보여줍니다 .


연결이 끊어졌습니다.
gauteh

2

이미지 또는 컨볼 루션 네트워크에 적용하기 위해 최신 GPU에서 매트릭스 멀티 플라이어를보다 효율적으로 사용하기 위해 입력은 일반적으로 활성화 매트릭스의 열로 재 형성되어 여러 필터 / 커널과 동시에 곱할 수 있습니다.

Stanford의 CS231n 에서이 링크 를 확인하고 세부 사항을 보려면 "매트릭스 곱셈으로 구현"섹션으로 스크롤하십시오.

이 프로세스는 입력 이미지 또는 활성화 맵에서 커널과 곱한 모든 로컬 패치를 가져 와서 일반적으로 im2col이라는 작업을 통해 새 매트릭스 X의 열로 늘립니다. 커널은 또한 웨이트 매트릭스 W의 행을 채우도록 확장되어 매트릭스 연산 W * X를 수행 할 때 결과 매트릭스 Y가 컨벌루션의 모든 결과를 갖도록한다. 마지막으로, 일반적으로 cal2im이라고하는 연산을 통해 열을 이미지로 다시 변환하여 Y 행렬을 다시 재구성해야합니다.


1
이것은 매우 좋은 링크입니다, 감사합니다! 그러나 링크에서 중요한 추출을 답변에 추가하는 것이 좋습니다. 이렇게하면 링크가 끊긴 경우에도 답변이 유효합니다. 답변을 수정하여 수락하십시오!
Matteo

1

시간 영역에서의 컨벌루션은 주파수 영역에서의 행렬 곱셈과 동일합니다.

필터링은 시간 영역에서의 컨벌루션 (convolution)과 동일하므로 주파수 영역에서의 행렬 곱셈과 같습니다.

5x5 맵 또는 마스크는 캐니 / 소벨 연산자를 이산화시키는 것에서 비롯됩니다.


2
필터링이 주파수 영역에서 컨볼 루션이라는 사실에 동의하지 않습니다. 여기서 이야기하는 필터의 종류는 공간 영역에서의 컨볼 루션입니다 (즉, 주파수 영역에서의 필터 응답에 의한 요소 별 곱셈).
pichenettes 2016 년

내가 쓴 것을 수정 해 주셔서 감사합니다. 후속 편집을했습니다. 게시하기 전에 답변을 다시 확인해야한다고 생각합니다. 그러나 대부분의 답변은 여전히 ​​유효합니다.
Naresh

푸리에 변환은 실제로 컨벌루션을 곱셈으로 (그리고 그 반대로) 바꿉니다. 그러나 그것들은 파인트 한 곱셈이지만, 문제는 이미지를 재구성함으로써 얻어지는 행렬-벡터 곱셈에 관한 문제입니다.
sansuiso

canny / sobel 연산자에 대해 얻은 5x5 행렬의 이유는 연산자를 구별하는 방법에 대해 언급했습니다.
Naresh

1

StackOverflow Q2080835 GitHub 리포지토리 (이것을 살펴보십시오 CreateImageConvMtx()) 에서이 문제를 해결하는 함수를 작성했습니다 .
실제로 기능을 사용하면 원하는 모든 회선 모양을 지원할 수 - full, same하고 valid.

코드는 다음과 같습니다.

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 리포지토리 에서 사용할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.