Convolution สามารถแสดงเป็นการคูณเมทริกซ์ได้อย่างไร (แบบฟอร์มเมทริกซ์)


11

ฉันรู้ว่าคำถามนี้อาจไม่เกี่ยวข้องกับการเขียนโปรแกรม แต่ถ้าฉันไม่เข้าใจทฤษฎีที่อยู่เบื้องหลังการประมวลผลภาพฉันจะไม่สามารถนำบางสิ่งไปใช้ในทางปฏิบัติ

ถ้าฉันเข้าใจถูกต้องตัวกรองเกาส์เซียนจะถูกโน้มน้าวด้วยภาพเพื่อลดสัญญาณรบกวนเนื่องจากพวกมันคำนวณค่าเฉลี่ยของพื้นที่ใกล้เคียงของพิกเซลและพวกมันมีประโยชน์มากในการตรวจจับขอบเนื่องจากคุณสามารถใช้ความเบลอและได้ภาพในเวลาเดียวกันด้วย เพียงแค่สนทนากับอนุพันธ์ของฟังก์ชันเกาส์เซียน

แต่ทุกคนสามารถอธิบายฉันได้หรือให้การอ้างอิงบางอย่างแก่ฉันเกี่ยวกับวิธีคำนวณ

เช่นเครื่องตรวจจับขอบของ Cannyพูดถึงตัวกรอง Gaussian 5x5 แต่พวกเขารับหมายเลขเหล่านั้นได้อย่างไร และพวกเขาเปลี่ยนจากการโน้มน้าวอย่างต่อเนื่องเป็นการคูณเมทริกซ์ได้อย่างไร



ฉันเพิ่มคำตอบพร้อมรหัสเต็มสำหรับสร้างเมทริกซ์สำหรับ Image Convolution
Royi

คำตอบ:


3

เพื่อให้การดำเนินการนี้ใช้งานได้คุณต้องจินตนาการว่าภาพของคุณได้รับการเปลี่ยนรูปร่างเป็นเวกเตอร์ จากนั้นเวกเตอร์นี้จะถูกคูณทางซ้ายโดยเมทริกซ์คอนโวลูชันเพื่อให้ได้ภาพเบลอ โปรดทราบว่าผลลัพธ์นั้นเป็นเวกเตอร์ที่มีขนาดเท่ากันกับอินพุตเช่นรูปภาพที่มีขนาดเดียวกัน

แต่ละแถวของเมทริกซ์การสนทนาสอดคล้องกับหนึ่งพิกเซลในภาพอินพุต มันมีน้ำหนักของการมีส่วนร่วมของพิกเซลอื่น ๆ ทั้งหมดในภาพเพื่อเบลอคู่ของพิกเซลที่พิจารณา

ลองมาตัวอย่าง: กล่องเบลอขนาด 3×3 พิกเซลบนภาพขนาด 6×6พิกเซล รูปภาพที่ได้รับการปรับแต่งใหม่เป็นคอลัมน์ที่มี 36 elects ในขณะที่เมทริกซ์เบลอมีขนาด36×36.

  • ลองเริ่มเมทริกซ์นี้เป็น 0 ได้ทุกที่
  • ตอนนี้ให้พิจารณาพิกเซลของพิกัด (ผม,J)ในภาพอินพุต (ไม่ใช่บนขอบเพื่อความเรียบง่าย) มันเบลอคู่กันโดยใช้น้ำหนักเป็น1/9 เพื่อตัวเองและแต่ละเพื่อนบ้านในตำแหน่ง (ผม-1,J-1);(ผม-1,J),(ผม-1,J+1),...,(ผม+1,J+1).
  • ในเวกเตอร์คอลัมน์, พิกเซล (ผม,J) มีตำแหน่ง 6* * * *ผม+J(สมมติว่าเรียงลำดับพจนานุกรม) เรารายงานน้ำหนัก1/9 ใน (6ผม+J)-th บรรทัดของเมทริกซ์เบลอ
  • ทำเช่นเดียวกันกับพิกเซลอื่น ๆ ทั้งหมด

ภาพประกอบที่แสดงให้เห็นถึงกระบวนการที่เกี่ยวข้องอย่างใกล้ชิด (การลบ + การบิด) สามารถพบได้ในโพสต์บล็อกนี้(จากบล็อกส่วนตัวของฉัน)


ลิงค์เสียชีวิต
gauteh

2

สำหรับแอปพลิเคชันไปยังรูปภาพหรือเครือข่ายการแปลงเพื่อเพิ่มประสิทธิภาพการใช้ตัวคูณเมทริกซ์ใน GPU ที่ทันสมัยอินพุตโดยทั่วไปจะถูกเปลี่ยนรูปในคอลัมน์ของเมทริกซ์การเปิดใช้งานที่สามารถคูณด้วยตัวกรอง / หลายเมล็ดได้

ลองดูลิงค์นี้จาก CS231n ของ Stanford และเลื่อนลงไปที่หัวข้อ "การดำเนินการตามการคูณเมทริกซ์" เพื่อดูรายละเอียด

กระบวนการทำงานโดยใช้แพตช์ท้องถิ่นทั้งหมดบนภาพอินพุตหรือแผนที่เปิดใช้งานซึ่งจะถูกคูณด้วยเคอร์เนลและขยายเข้าไปในคอลัมน์ของเมทริกซ์ใหม่ X ผ่านการดำเนินการทั่วไปที่เรียกว่า im2col เมล็ดยังถูกยืดขยายเพื่อเติมแถวของเมทริกซ์น้ำหนัก W ดังนั้นเมื่อทำการดำเนินการเมทริกซ์ W * X เมทริกซ์ผลลัพธ์ Y มีผลลัพธ์ทั้งหมดของการแปลง ในที่สุดเมทริกซ์ Y จะต้องถูกเปลี่ยนรูปร่างอีกครั้งโดยแปลงคอลัมน์กลับเป็นรูปภาพโดยการดำเนินการโดยทั่วไปเรียกว่า cal2im


1
นี่คือลิงค์ที่ดีมากขอบคุณ! อย่างไรก็ตามเป็นวิธีปฏิบัติที่ดีในการเพิ่มสารสกัดที่สำคัญจากลิงก์ลงในคำตอบวิธีนี้คำตอบจะใช้ได้แม้ว่าลิงก์จะแตก โปรดพิจารณาแก้ไขคำตอบของคุณเพื่อให้ได้รับการยอมรับ!
Matteo

1

การแปลงในโดเมนเวลาเท่ากับการคูณเมทริกซ์ในโดเมนความถี่และในทางกลับกัน

การกรองนั้นเทียบเท่ากับการแปลงในโดเมนเวลาและด้วยการคูณเมทริกซ์ในโดเมนความถี่

สำหรับแผนที่หรือมาสก์ 5x5 พวกเขามาจากการแยกแยะผู้ประกอบการ canny / sobel


2
ฉันไม่เห็นด้วยกับความจริงที่ว่าการกรองเป็นการโน้มน้าวใจในโดเมนความถี่ ชนิดของตัวกรองที่เรากำลังพูดถึงอยู่ที่นี่คือการโน้มน้าวใจในโดเมนอวกาศ (กล่าวคือการคูณองค์ประกอบที่ชาญฉลาดโดยการตอบสนองของตัวกรองในโดเมนความถี่)
pichenettes

ขอบคุณสำหรับการแก้ไขสิ่งที่ฉันเขียน ฉันทำการแก้ไขในภายหลัง ฉันเดาว่าฉันควรตรวจสอบคำตอบอีกครั้งก่อนโพสต์ อย่างไรก็ตามคำตอบส่วนใหญ่ของฉันยังคงอยู่
Naresh

การแปลงฟูริเยร์จะเปลี่ยนการโน้มน้าวใจให้เป็นทวีคูณ (และในทางกลับกัน) อย่างไรก็ตามพวกมันคือการคูณไพน์อย่างชาญฉลาดในขณะที่คำถามเกี่ยวกับการคูณเมทริกซ์เวกเตอร์ที่ได้มาจากการปรับภาพใหม่
sansuiso

ฉันได้พูดถึงวิธีการ discretizing ผู้ประกอบการเป็นเหตุผลสำหรับการฝึกอบรม 5x5 ที่ได้รับสำหรับผู้ประกอบการ canny / sobel
Naresh

1

ฉันเขียนฟังก์ชันที่แก้ปัญหานี้ในStackOverflow Q2080835 GitHub Repositoryของฉัน(ดูที่CreateImageConvMtx())
ที่จริงฟังก์ชั่นสามารถรองรับรูปร่างบิดใด ๆ ที่คุณต้องการ - full, และsamevalid

รหัสดังต่อไปนี้:

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

ฉันยังสร้างฟังก์ชันเพื่อสร้าง Matrix สำหรับการกรองรูปภาพ (ความคิดคล้ายกับของ 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

รหัสที่ได้รับการตรวจสอบกับ imfilter()MATLAB

รหัสเต็มสามารถใช้ได้ในของฉันStackOverflow Q2080835 GitHub Repository

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.