Làm thế nào để một Convolution có thể được thể hiện dưới dạng phép nhân ma trận (dạng ma trận)?


11

Tôi biết câu hỏi này có thể không liên quan lắm đến lập trình, nhưng nếu tôi không hiểu lý thuyết đằng sau xử lý hình ảnh, tôi sẽ không bao giờ có thể thực hiện điều gì đó trong thực tế.

Nếu tôi hiểu đúng, các bộ lọc Gaussian được tích hợp với hình ảnh để giảm nhiễu do chúng tính toán trung bình có trọng số của vùng lân cận pixel và chúng rất hữu ích trong việc phát hiện cạnh, vì bạn có thể áp dụng độ mờ và tạo ra hình ảnh cùng một lúc chỉ đơn giản là kết hợp với đạo hàm của hàm Gaussian.

Nhưng bất cứ ai có thể giải thích cho tôi, hoặc cho tôi một số tài liệu tham khảo về cách họ được tính toán?

Ví dụ: máy dò cạnh của Canny nói về bộ lọc Gaussian 5x5, nhưng làm thế nào họ có được những con số cụ thể đó? Và làm thế nào mà họ đi từ một tổ hợp liên tục sang phép nhân Ma trận?



Tôi đã thêm một câu trả lời với mã đầy đủ để tạo ma trận cho Kết hợp hình ảnh.
Royi

Câu trả lời:


3

Để thao tác này hoạt động, bạn cần tưởng tượng rằng hình ảnh của bạn được định hình lại dưới dạng vector. Sau đó, vectơ này được nhân bên trái bởi ma trận tích chập để thu được hình ảnh mờ. Lưu ý rằng kết quả cũng là một vectơ có cùng kích thước với đầu vào, tức là một hình ảnh có cùng kích thước.

Mỗi hàng của ma trận tích chập tương ứng với một pixel trong ảnh đầu vào. Nó chứa trọng số của sự đóng góp của tất cả các pixel khác trong ảnh với đối tác mờ của pixel được xem xét.

Hãy lấy một ví dụ: hộp mờ kích thước pixel trên ảnh có kích thước pixel. Hình ảnh được định hình lại là một cột gồm 36 điện tử, trong khi ma trận mờ có kích thước .3×36×636×36

  • Hãy khởi tạo ma trận này thành 0 ở mọi nơi.
  • Bây giờ, hãy xem xét pixel của tọa độ trong hình ảnh đầu vào (không phải trên đường viền của nó cho đơn giản). Đối tác mờ của nó có được bằng cách áp dụng trọng số cho chính nó và từng hàng xóm của nó tại các vị trí .(i,j)1/9(i1,j1);(i1,j),(i1,j+1),,(i+1,j+1)
  • Trong vectơ cột, pixel có vị trí (giả sử thứ tự từ điển). chúng tôi báo cáo trọng số trong dòng thứ 6 của ma trận mờ.6 * i + j 1 / 9 ( 6 i + j )(i,j)6i+j1/9(6i+j)
  • Làm tương tự với tất cả các pixel khác.

Một minh họa trực quan của một quá trình liên quan chặt chẽ (tích chập + phép trừ) có thể được tìm thấy trên bài đăng blog này (từ blog cá nhân của tôi).


một liên kết là chết.
gauteh

2

Đối với các ứng dụng cho hình ảnh hoặc mạng tích chập, để sử dụng hiệu quả hơn hệ số nhân ma trận trong GPU hiện đại, các đầu vào thường được định hình lại thành các cột của ma trận kích hoạt, sau đó có thể được nhân với nhiều bộ lọc / hạt nhân cùng một lúc.

Kiểm tra liên kết này từ CS231n của Stanford và cuộn xuống phần "Thực hiện dưới dạng nhân ma trận" để biết chi tiết.

Quá trình này hoạt động bằng cách lấy tất cả các bản vá cục bộ trên một hình ảnh đầu vào hoặc bản đồ kích hoạt, những bản vá sẽ được nhân với kernel và kéo dài chúng vào một cột của ma trận X mới thông qua một hoạt động thường được gọi là im2col. Các hạt nhân cũng được kéo dài để điền vào các hàng của ma trận trọng số W để khi thực hiện thao tác ma trận W * X, ma trận kết quả Y có tất cả các kết quả của phép chập. Cuối cùng, ma trận Y phải được định hình lại một lần nữa bằng cách chuyển đổi các cột trở lại thành hình ảnh bằng một thao tác thường được gọi là cal2im.


1
Đây là một liên kết rất tốt, cảm ơn! Tuy nhiên, nên thêm các trích đoạn quan trọng từ liên kết vào câu trả lời, cách này là câu trả lời hợp lệ ngay cả khi liên kết bị hỏng. Vui lòng xem xét chỉnh sửa câu trả lời của bạn để được chấp nhận!
Matteo

1

Convolution trong miền Thời gian bằng phép nhân ma trận trong miền tần số và ngược lại.

Lọc tương đương với tích chập trong miền thời gian và do đó nhân ma trận trong miền tần số.

Đối với các bản đồ hoặc mặt nạ 5x5, chúng xuất phát từ việc loại bỏ các toán tử canny / sobel.


2
Tôi không đồng ý với thực tế rằng lọc là một tổ hợp trong miền tần số. Loại bộ lọc mà chúng ta đang nói đến ở đây là các cấu trúc trong miền không gian (nghĩa là nhân bội phần tử theo đáp ứng bộ lọc trong miền tần số).
pichenettes

Cảm ơn đã sửa những gì tôi đã viết. Tôi đã thực hiện một chỉnh sửa tiếp theo. Tôi đoán tôi nên kiểm tra lại câu trả lời của mình trước khi đăng. Tuy nhiên, phần lớn câu trả lời của tôi vẫn đứng vững.
Naresh 17/03/13

Biến đổi Fourier thực sự biến các kết cấu thành phép nhân (và ngược lại). Tuy nhiên, chúng là các phép nhân thông minh, trong khi câu hỏi là về phép nhân vectơ ma trận thu được bằng cách định hình lại các hình ảnh.
sansuiso 17/03/13

Tôi đã đề cập đến việc làm thế nào sự rời rạc của các toán tử là lý do cho các ma trận 5x5 thu được cho các toán tử canny / sobel.
Naresh 17/03/13

1

Tôi đã viết một hàm giải quyết điều này trong Kho lưu trữ GitHub của StackOverflow Q2080835 của tôi (Hãy xem CreateImageConvMtx()).
Trên thực tế các chức năng có thể hỗ trợ bất kỳ hình dạng chập bạn muốn - full, samevalid.

Mã như sau:

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

Tôi cũng đã tạo một chức năng để tạo Ma trận cho Lọc ảnh (Tương tự ý tưởng của 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

Mã đã được xác nhận chống lại MATLAB imfilter().

Mã đầy đủ có sẵn trong Kho lưu trữ GitHub StackOverflow Q2080835 của tôi .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.