Asked by Royi Avital
on 15 Jan 2019

Yet there are 2 issues:

- It is only available to those who purchased Image PRocessing Toolbox.
- It creates the matrix for full convolution shape only.

I implemented the matrix form for imfiter() in Generate the Matrix Form of 2D Convolution Kernel. It was written in simple form (No vectorization tricks) for clarity and simplicity for thos who want to learn.

What I'm after is doing somthing similar for Convolution Matrices for the different shapes: full, same, valid.

Namely a function with the following form:

function [ mK ] = CreateImageConvMtx( mH, numRows, numCols, convShape )

%UNTITLED6 Summary of this function goes here

% Detailed explanation goes here

CONVOLUTION_SHAPE_FULL = 1;

CONVOLUTION_SHAPE_SAME = 2;

CONVOLUTION_SHAPE_VALID = 3;

switch(convShape)

case(CONVOLUTION_SHAPE_FULL)

% Code for the 'full' case

case(CONVOLUTION_SHAPE_SAME)

% Code for the 'same' case

case(CONVOLUTION_SHAPE_VALID)

% Code for the 'valid' case

end

end

I would be happy of someone could assist with that.

Again, prefer clarity over performance.

Thank You.

Answer by Matt J
on 15 Jan 2019

Edited by Matt J
on 15 Jan 2019

Accepted Answer

Readability and clarity over performance.

OK, can't get much simpler and more readable then the following:

function [ mK ] = CreateImageConvMtx( mH, nRows, nCols, convShape )

%convShape is 'full', 'same', or 'valid'

impulse=zeros(nRows,nCols);

for i=numel(impulse):-1:1

impulse(i)=1; %Create impulse image corresponding to i-th output matrix column

tmp=sparse( conv2(impulse,mH,convShape) ); %impulse response

Column{i}=tmp(:);

impulse(i)=0;

end

mK=cell2mat(Column);

end

Matt J
on 16 Jan 2019

Yes, there are ways to generate the (i,j,vals) data efficiently in a stand-alone piece of code. However, since we're now shifting priority to efficiency, the code would not look very C-like.

Could I ask who the audience of this code is going to be? Is it for a programming course that you are teaching? If the idea is to show how this would look in C, I'm having trouble seeing why you are pursuing this in Matlab at all.

Royi Avital
on 16 Jan 2019

Hi Matt, First, thank you fo the effort.

Let me try explain. I would like to be able to explain in asimple way how to build the convolution matrix. Indeed the Impulse Response is probably the simplest way out there.

The full case is usually handled by doubly block toeplitz matrix. Yet I couldn't find a code which shows it clearly (I guess this is what convmtx2() does but the code isn't clear, at leats in my opinion).

Next step would be being more efficinent. I think it can diverge into 2 ways:

- C Style - The code is element wise to construct the I, J, Vals vectors. Preferebly the loop will show the inner structure of the large matrix.
- MATLAB Style - The code is more vectorized yet it is still clear what is being done in order to construct the matrix.

What do you think? How would you show the steps so others could be able to fully understand the code, the structure, what's beiong done and later even implement it, in C let's say.

Royi Avital
on 17 Jan 2019

By the way, instead of mK = cell2mat(cColumn); one could write mK = cat(2, cColumn{1, :});.

Sign in to comment.

Answer by Matt J
on 15 Jan 2019

Edited by Matt J
on 15 Jan 2019

You can use my func2mat (Download) utility. It will find the matrix form of any linear function and doesn't require any toolboxes. However, there are much better options if your convolution kernels are separable.

function [ mK ] = CreateImageConvMtx( mH, nRows, nCols, convShape )

%convShape is 'full', 'same', or 'valid'

Xtypical=zeros([nRows,nCols]);

fun=@(x) conv2(x,mH,convShape);

mK=func2mat(fun,Xtypical);

end

Royi Avital
on 19 Jan 2019

Sign in to comment.

Answer by Matt J
on 15 Jan 2019

Edited by Matt J
on 15 Jan 2019

function [ mK ] = CreateImageConvMtx( mH, nRows, nCols, convShape )

%convShape is 'full', 'same', or 'valid'

E=ndSparse(speye(nRows*nCols), [nRows,nCols,nRows,nCols]);

mK=convn(E,mH, convShape);

mK=sparse2d( reshape(mK, nRows*nCols, [] ) ) ;

end

Matt J
on 15 Jan 2019

Yes, both of my solutions run in Matlab Base (no toolboxes required).

Royi Avital
on 15 Jan 2019

But they require additional functions. I want it to be a stand alone solution within a single function.

Also if someone wants to learn how build this matrix, it will be really hard form those functions.

Royi Avital
on 16 Jan 2019

Sign in to comment.

Answer by Royi Avital
on 19 Jan 2019

Edited by Royi Avital
on 22 Jan 2019

Here is my solution which build Doubly Block Toeplitz Matrix:

function [ mK ] = Create2DKernelConvMtxSparse( mH, numRows, numCols, convShape )

CONVOLUTION_SHAPE_FULL = 1;

CONVOLUTION_SHAPE_SAME = 2;

CONVOLUTION_SHAPE_VALID = 3;

numColsKernel = size(mH, 2);

numBlockMtx = numColsKernel;

cBlockMtx = cell(numBlockMtx, 1);

for ii = 1:numBlockMtx

cBlockMtx{ii} = CreateConvMtxSparse(mH(:, ii), numRows, convShape);

end

switch(convShape)

case(CONVOLUTION_SHAPE_FULL)

diagIdx = 0;

numRowsKron = numCols + numColsKernel - 1;

case(CONVOLUTION_SHAPE_SAME)

diagIdx = floor(numColsKernel / 2);

numRowsKron = numCols;

case(CONVOLUTION_SHAPE_VALID)

diagIdx = numColsKernel - 1;

numRowsKron = numCols - numColsKernel + 1;

end

vI = ones(min(numRowsKron, numCols), 1);

mK = kron(spdiags(vI, diagIdx, numRowsKron, numCols), cBlockMtx{1});

for ii = 2:numBlockMtx

diagIdx = diagIdx - 1;

mK = mK + kron(spdiags(vI, diagIdx, numRowsKron, numCols), cBlockMtx{ii});

end

end

It relies on a function called CreateConvMtxSparse which creates a convolution matrix for 1D Kernel which goes:

function [ mK ] = CreateConvMtxSparse( vK, numElements, convShape )

CONVOLUTION_SHAPE_FULL = 1;

CONVOLUTION_SHAPE_SAME = 2;

CONVOLUTION_SHAPE_VALID = 3;

kernelLength = length(vK);

switch(convShape)

case(CONVOLUTION_SHAPE_FULL)

rowIdxFirst = 1;

rowIdxLast = numElements + kernelLength - 1;

outputSize = numElements + kernelLength - 1;

case(CONVOLUTION_SHAPE_SAME)

rowIdxFirst = 1 + floor(kernelLength / 2);

rowIdxLast = rowIdxFirst + numElements - 1;

outputSize = numElements;

case(CONVOLUTION_SHAPE_VALID)

rowIdxFirst = kernelLength;

rowIdxLast = (numElements + kernelLength - 1) - kernelLength + 1;

outputSize = numElements - kernelLength + 1;

end

mtxIdx = 0;

% The sparse matrix constructor ignores valus of zero yet the Row / Column

% indices must be valid indices (Positive integers). Hence 'vI' and 'vJ'

% are initialized to 1 yet for invalid indices 'vV' will be 0 hence it has

% no effect.

vI = ones(numElements * kernelLength, 1);

vJ = ones(numElements * kernelLength, 1);

vV = zeros(numElements * kernelLength, 1);

for jj = 1:numElements

for ii = 1:kernelLength

if((ii + jj - 1 >= rowIdxFirst) && (ii + jj - 1 <= rowIdxLast))

% Valid otuput matrix row index

mtxIdx = mtxIdx + 1;

vI(mtxIdx) = ii + jj - rowIdxFirst;

vJ(mtxIdx) = jj;

vV(mtxIdx) = vK(ii);

end

end

end

mK = sparse(vI, vJ, vV, outputSize, numElements);

end

The above pass the test (As the code by Matt) in Cody.

I wonder if there is more efficient way to create the matrices while keeping the main idea of creating the Doubly Block Toeplitz Matrix. Maybe even doing vI, vJ and vV directly to the 2D convolution matrix.

Matt J
on 22 Jan 2019

@Matt, any thoughts on the code I posted at -

Overall, I think it's a fine blend of simplicity and efficiency (so +1!), but as I still don't know who this code is for, it's hard for me to say whether it's the best you can do.

I wonder if there is more efficient way to create the matrices while keeping the main idea of creating the Doubly Block Toeplitz Matrix.

I would consider this:

function [ mK ] = CreateConvMtxSparse( vK, numElements, convShape )

%conShape is 'full','same', or'valid'

mK = sparse( conv2(eye(numElements),vk,convShape) );

Royi Avital
on 22 Jan 2019

Hi Matt,

The problem with mK = sparse( conv2(eye(numElements),vk,convShape) ); that in case numElements is a large number this becomes inefficient both computationaly and memory wise.

You can see issues I'm having with Improving Speed and Reducing Memory Consumption with Creation of 2D Sparse Convolution Matrix.

Regaridng the code audience, What I'm looking for is a code which makes sense Math wise (Namely you can see what it does matches the math of the case) while being efficient, not tricky and if someone want to reprodcue it in C or any other low level language he can do that easily.

Matt J
on 22 Jan 2019

that in case numElements is a large number this becomes inefficient both computationaly and memory wise.

Less efficient, yes, but clearer (are you still prioritizing clarity over performance?), and still very fast even for absurdly large image sizes.

N=5000; %image length

K=7; %kernel length

vK=rand(K,1);

E=eye(N);

tic

mK1 = CreateConvMtxSparse( vK, N, 3 );

toc; %Elapsed time is 0.002846 seconds.

tic;

mK2=sparse(conv2(E,vK,'valid'));

toc %Elapsed time is 0.275314 seconds.

Sign in to comment.

Opportunities for recent engineering grads.

Apply Today
## 3 Comments

## Royi Avital (view profile)

## Direct link to this comment

https://www.mathworks.com/matlabcentral/answers/439928-creating-convolution-matrix-of-2d-kernel-for-different-shapes-of-convolution#comment_660839

## Image Analyst (view profile)

## Direct link to this comment

https://www.mathworks.com/matlabcentral/answers/439928-creating-convolution-matrix-of-2d-kernel-for-different-shapes-of-convolution#comment_660855

## Royi Avital (view profile)

## Direct link to this comment

https://www.mathworks.com/matlabcentral/answers/439928-creating-convolution-matrix-of-2d-kernel-for-different-shapes-of-convolution#comment_660928

Sign in to comment.