Як може виражатися згортання як матричне множення (матрична форма)?


11

Я знаю, що це питання може бути не дуже актуальним для програмування, але якщо я не розумію теорії обробки зображень, я ніколи не зможу щось реалізувати на практиці.

Якщо я правильно зрозумів, фільтри Гаусса поєднуються із зображенням для зменшення шуму, оскільки вони обчислюють зважене середнє значення сусідства пікселів, і вони дуже корисні при виявленні ребер, оскільки ви можете застосувати розмиття та отримати зображення одночасно шляхом просто сполучення з похідною функції Гаусса.

Але чи може хтось пояснити мені чи дати мені кілька посилань на те, як вони обчислюються?

Наприклад , крайовий детектор Кенні говорить про фільтр Гаусса 5x5, але як вони отримали саме ці цифри? І як вони перейшли від суцільної згортки до множення Матриці?



Я додав відповідь з повним кодом для створення матриці для Image Convolution.
Рой

Відповіді:


3

Щоб ця операція працювала, вам потрібно уявити, що ваше зображення перероблене як вектор. Потім цей вектор множиться ліворуч на матрицю згортки для отримання розмитого зображення. Зауважте, що результат також є вектором того ж розміру, що і вхід, тобто зображення однакового розміру.

Кожен рядок матриці згортки відповідає одному пікселю у вхідному зображенні. Він містить вагу внеску всіх інших пікселів на зображення в розмиту аналогію розглянутого пікселя.

Візьмемо приклад: розмиття поля розміром 3×3 пікселів на зображенні розміру 6×6пікселів. Перероблене зображення - це стовпчик з 36 виборців, тоді як матриця розмиття має розмір36×36.

  • Давайте скрізь запровадимо цю матрицю до 0.
  • Тепер розглянемо піксель координат (i,j)у вхідному зображенні (не на його межі для простоти). Його розмитий аналог отримують, застосовуючи вагу1/9 собі та кожному своєму сусідові на посадах (i1,j1);(i1,j),(i1,j+1),,(i+1,j+1).
  • У векторі стовпця піксель (i,j) має позицію 6i+j(припускаючи лексикографічне впорядкування). ми повідомляємо про вагу1/9 в (6i+j)-та лінія матриці розмиття
  • Зробіть те саме з усіма іншими пікселями.

Візуальну ілюстрацію тісно пов’язаного процесу (згортання + віднімання) можна знайти в цьому дописі (з мого особистого блогу).


посилання мертва.
gauteh

2

Для застосувань до зображень або мереж згортки, щоб більш ефективно використовувати матричні множники в сучасних графічних процесорах, входи, як правило, перерозподіляються на стовпці активаційної матриці, які потім можуть бути помножені на кілька фільтрів / ядер одночасно.

Перегляньте це посилання з CS231n Стенфорда та прокрутіть униз до розділу "Реалізація як множення матриць" для отримання детальної інформації.

Процес працює, беручи всі локальні патчі на вхідному зображенні або карті активації, ті, які були б помножені на ядро, і розтягуючи їх у стовпчик нової матриці X через операцію, яку зазвичай називають im2col. Ядра також розтягуються для заповнення рядків вагової матриці W так, що при виконанні операції з матрицею W * X отримана матриця Y має всі результати згортки. Нарешті, матрицю Y потрібно знову змінити шляхом перетворення стовпців назад у зображення операцією, яка зазвичай називається cal2im.


1
Це дуже гарне посилання, дякую! Однак є важливою практикою додавання важливих витягів із посилання у відповідь, таким чином відповідь справедлива, навіть якщо посилання розривається. Будь ласка, подумайте про редагування своєї відповіді, щоб прийняти її!
Маттео

1

Згортання в часовій області дорівнює множенню матриць у частотній області і навпаки.

Фільтрація еквівалентна згортці в часовій області і, отже, матричне множення в частотній області.

Що стосується карт або масок 5x5, вони походять від дискретизації операторів canny / sobel.


2
Я не згоден з тим, що фільтрація - це згортання в частотній області. Фільтри, про які ми говоримо тут, - це згортки в просторовій області (тобто, елементарне множення на відповідь фільтра в частотній області).
пікенети

Дякуємо, що виправили те, що я написав. Я зробив наступну редагування. Я думаю, я повинен ще раз перевірити свої відповіді перед публікацією. Однак більшість моєї відповіді все ще стоїть.
Нареш

Перетворення Фур'є справді перетворює згортки на множення (і навпаки). Однак вони є мудрими мультиплікаційними розмноженнями, тоді як питання стосується матричного векторного множення, яке отримують шляхом перестановки зображень.
sansuiso

Я згадував, як дискретність операторів є причиною матриць 5x5, отриманих для операторів canny / sobel.
Нареш

1

Я написав функцію, яка вирішує це в моєму StackOverflow Q2080835 GitHub Repository (Подивіться 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 Repository .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.