How is full convolution performed using MATLAB's conv2 function? - matlab

I am trying to get some input on MATLAB's conv2 function. Suppose that we have an image I of dimensions 5 x 5 and a kernel K that is 3 x 3.
conv2(I,K) would return a 7 x 7 matrix. What extra operations are being done that I am not aware of? I can totally understand how conv2(I,K,'valid') and conv2(I,K,'same') works from a mathematical point of view. However, the default operation returns a larger matrix. Does anyone know what it actually does?

If you know how the 'valid' flag and 'same' flag work, then it's not that far of a stretch to go to what the default option is, which is the 'full' option. As you slide the kernel across the image / matrix, as soon as at least one element from the kernel touches any element from the image / matrix, that is considered valid output. The output of the operation is dictated by the centre of where the kernel is when there is a valid output. For example, take a look at the following 5 x 5 image I below with an example 3 x 3 kernel K:
I = [1 2 3 4 5 ] K = [1 0 1]
[6 7 8 9 10] [1 0 1]
[11 12 13 14 15] [1 0 1]
[16 17 18 19 20]
[21 22 23 24 25]
Note that the numbers aren't that important but they're used for illustration. Also note that the kernel is symmetric and so performing a 180 degree rotation results in the same kernel. This is required of convolution before we start. In the 'full' configuration, we slide the kernel from the top left to bottom right in a left to right, up to down fashion. The output of the first element in the output matrix happens when the bottom right of the kernel touches the top left of the image / matrix:
[1 0 1]
[1 `0` 1]
[1 0 [1*1] 2 3 4 5]
[6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]
Note that the centre of the kernel as we sweep across the image is the location where we need to output in the image, denoted by the `` symbols. Remember that to compute convolution here, we find the weighted and element-wise sum of products between each element in the kernel and where it touches in the matrix / image.
Note that for any elements of the kernel that are out of bounds we ignore and so the output is simply where the bottom right of the kernel and the top left of the image touch and we multiply these elements together. The output is simply 1*1 = 1. Now let's move over to the next element, which is 1 to the right:
[ 1 0 1]
[ 1 `0` 1]
[1 [0*1] [2*1] 3 4 5 ]
[6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]
Note where the centre is as well as what elements the kernel touches the matrix. The output is thus 0*1 + 2*1 = 2. You would continue this until you hit the end of this row where the bottom left of the kernel touches the top right of the image. You would then move down to the next row, repeat the sweep over all of the columns and continue up until the very end until the top left of the kernel touches the bottom right of the image / matrix.
Here are a couple more examples just to be sure you have the theory right. Let's do the point where the kernel touches the top right of the image / matrix
[ 1 0 1]
[ 1 `0` 1]
[1 2 3 4 [5*1]] 0 1]
[6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]
Remember that we ignore all places where the kernel doesn't touch the image / matrix. The output in this case would simply be 5 and also note where the output location is. Here's another example:
[1 2 3 4 5 ]
[6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[1 0 [[21*1] 22 23 24 25]
[1 `0` 1]
[1 0 1]
This location is at the bottom left corner of the image / matrix and the output here would simply be 21*1. Another one just to be sure:
[1 2 3 4 5]
[6 7 8 9 10]
[11 12 13 14 [1*15]] 0 1]
[16 17 18 19 [1*20]] `0` 1]
[21 22 23 24 [1*25]] 0 1]
This location is a bit more complicated. The kernel overlaps the image / matrix fully by its first column and so the output is simply 1*15 + 1*20 + 1*25 = 60. Also note that the output position is at the third-last row because there are still two more rows of filtering to perform. One where the first two rows of the kernel are touching the bottom last two rows of the image / matrix and one where the first row of the kernel touches the bottom last row of the image / matrix.
Therefore, the final output matrix would look something like this.
[1 2 * * * * 5 ]
[* * * * * * * ]
[* * * * * * * ]
[* * * * * * * ]
[* * * * * * 60]
[* * * * * * *]
[21 * * * * * *]
The elements marked as * are unknown as I haven't calculated those, but the point is to notice what the final size of the matrix is. Specifically, note where the output positions are of where we need to write to the matrix for the first couple of cases that you see above. This is the reason why you get a larger matrix - to accommodate for the results when the kernel is not fully contained within the image / matrix itself but still performs valid operations. As you can see, you would need two additional rows: 1 for the top and 1 for the bottom, and two additional columns: 1 for the left and 1 for the right. This results in a (5 + 2) x (5 + 2) = 7 x 7 output matrix. In general if the kernel size is odd, the output you get from use 'full' 2D convolution is usually (rows + 2*floor(kernel_rows/2)) x (cols + 2*floor(kernel_cols/2)) where rows and cols are the rows and columns of the image / matrix to filter and kernel_rows and kernel_cols are the rows and columns of the kernel.
If you want to have a look at what MATLAB actually produces, we can. Using the input image / matrix and kernel I defined earlier, this is what we get:
>> I = reshape(1:25,5,5).'; %'
>> K = [1 0 1; 1 0 1; 1 0 1];
>> out = conv2(I,K)
out =
`1` `2` 4 6 8 4 `5`
7 9 18 22 26 13 15
18 21 42 48 54 27 30
33 36 72 78 84 42 45
48 51 102 108 114 57 `60`
37 39 78 82 86 43 45
`21` 22 44 46 48 24 25
Note that I've marked the sample calculations that we did in MATLAB's output with `` characters. This does agree with the calculations.
Now your true question is how 'valid' and 'same' factor into all this. Where 'valid' and 'same' come in are simply truncated versions of the 'full' convolution. 'same' gives you an output that is the same size as the image / matrix to be filtered and 'valid' gives you an output such that you only provide outputs where the kernel was fully contained inside the image / matrix. Any time the kernel is out of bounds with respect to the image / matrix, we do not include those outputs as part of the final output. Simply put, 'valid' and 'same' use the 'full' result but remove certain portions of the borders of the result to facilitate the option that you choose.

Related

Comparing two array and combining them with condition

I have these matrices:
I1 = [60 30 15 35 20 -25 30 5 45 25 -10 40 10];
I2 = [60 30 60 35 20 60 30 60 45 25 60 40 60];
A= 0:12 ;
I want this:
Ir=[60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10 NaN 60 40 10 NaN 60]
Ar= [0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10 10 10 11 12 12 12]
How:
When I1 and I2 are same, proceed. When different, use elements of both I1 and I2 and insert an NaN between them.
and Ar is such that use element of A and proceed when elements of I1 and I2 are same. But when different, repeat the value of A 3 times. 1st for I1 and then for NaN and then for I2.
Can't figure out a way to do this. How do I do this?
A more vectorized way to do this would be to create an augmented matrix where the top row is I1, the bottom row is I2 and the middle row is full of NaN. After, create a logical matrix which is the same size as this augmented matrix you just created and you would adjust the columns of this matrix where you'd set the bottom two rows of this logical matrix to 0 for each corresponding column in this matrix where I1 == I2. Once you're done, use this logical matrix and index the augmented matrix. The advantage of this indexing is that it accesses the elements in column major format, which is exactly what you're after. We will only sample the top row of the augmented matrix unless the elements in I1 and I2 don't equal each other. If that's the case, we sample the entire column which includes both I1, nan and I2. Because we accessed by columns, your desired output is a row so you'll need to transpose the result when you're done.
To create the index vector, you would do the same thing but you would create a matrix of IDs where we have three rows where each row is the index ID array A. You'll also need to transpose this result after you index into A:
aug = [I1; nan(1,numel(I1)); I2];
V = true(size(aug));
V(2:3, I1 == I2) = false;
Ir = aug(V).';
ID = repmat(A, 3, 1);
Ar = ID(V).';
We get:
>> format compact
>> Ir
Ir =
Columns 1 through 17
60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10
Columns 18 through 23
NaN 60 40 10 NaN 60
>> Ar
Ar =
Columns 1 through 17
0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10
Columns 18 through 23
10 10 11 12 12 12
I can provide you with pseudo-code.
for each value in A
if I1 at [current A value] equals I2 at [current A value]
add I1 at [current A value] to end of Ir
add [current A value] to end of Ar
else
add I1 at [current A value] to end of Ir
add NaN to Ir
add I2 at [current A value] to end of Ir
add [current A value] to Ar 3 times
If your problem was conceptual hopefully this is enough.
Let me know if you have any further questions.
You can use two indices and solve the problem. The first index ii goes over the input vectors, and the second index k grows as the computation proceeds. A simple code using for-loop and if-statement looks like this:
I1 = [60 30 15 35 20 -25 30 5 45 25 -10 40 10];
I2 = [60 30 60 35 20 60 30 60 45 25 60 40 60];
Ir = [];
Ar = [];
k = 1;
for ii=1:length(I1)
if I1(ii)==I2(ii)
Ir(k) = I1(ii);
Ar(k) = ii-1;
k = k + 1;
else
Ir(k:k+2) = [I1(ii) NaN I2(ii)];
Ar(k:k+2) = ii-1;
k = k + 3;
end
end
Will result in:
Ir =
60 30 15 NaN 60 35 20 -25 NaN 60 30 5 NaN 60 45 25 -10 NaN 60 40 10 NaN 60
Ar =
0 1 2 2 2 3 4 5 5 5 6 7 7 7 8 9 10 10 10 11 12 12 12

Merger of Multiple matrices using Matlab/Octave

Assuming we have 3 matrices, A, B and C, all are of the same size 256x256. It is known that last 20% of columns of Matrix A is identical to first 20% of Matrix B and last 10% of Matrix B is identical to first 10% of Matrix C. So in these cases since we know the overlapping amount, I do not need to compare the 3 matrices, but i want to join them at the overlap.
Taking a smaller Matrix as an example here are the 3 Matrices
A = [1 2 3 4 ; 5 6 7 8; 9 10 11 12];
B = [3 4 13 14; 7 8 15 16; 11 12 17 18];
C = [14 19 20 21; 16 22 23 24; 18 25 26 27];
So I would like my output to be
D = [1 2 3 4 13 14 19 20 21
5 6 7 8 15 16 22 23 24
9 10 11 12 17 18 25 26 27
I hope this might explain it better. I am extremely new to matlab. I tried using matrix shift, but we have only circular shift available.
Concatenation does not work because it just joins the 3 matrices. What would be the best way to overlay these 3 matrices together ?
Make proper use of matrix indexing and concatenation
For your example
D = [A B(:,3) C];
For a 256x256 Matrix and your concatenation conditions:
D = [A B(:, 0.2*256+1 : 0.9*256) C]
Since 256/10 is no integer you may adjust the index values

Find closest matching distances for a set of points in a distance matrix in Matlab

I have a matrix of measured angles between M planes
0 52 77 79
52 0 10 14
77 10 0 3
79 14 3 0
I have a list of known angles between planes, which is an N-by-N matrix which I name rho. Here's is a subset of it (it's too large to display):
0 51 68 75 78 81 82
51 0 17 24 28 30 32
68 17 0 7 11 13 15
75 24 7 0 4 6 8
78 28 11 4 0 2 4
81 30 13 6 2 0 2
82 32 15 8 4 2 0
My mission is to find the set of M planes whose angles in rho are nearest to the measured angles.
For example, the measured angles for the planes shown above are relatively close to the known angles between planes 1, 2, 4 and 6.
Put differently, I need to find a set of points in a distance matrix (which uses cosine-related distances) which matches a set of distances I measured. This can also be thought of as matching a pattern to a mold.
In my problem, I have M=5 and N=415.
I really tried to get my head around it but have run out of time. So currently I'm using the simplest method: iterating over every possible combination of 3 planes but this is slow and currently written only for M=3. I then return a list of matching planes sorted by a matching score:
function [scores] = which_zones(rho, angles)
N = size(rho,1);
scores = zeros(N^3, 4);
index = 1;
for i=1:N-2
for j=(i+1):N-1
for k=(j+1):N
found_angles = [rho(i,j) rho(i,k) rho(j,k)];
score = sqrt(sum((found_angles-angles).^2));
scores(index,:)=[score i j k];
index = index + 1;
end
end;
end
scores=scores(1:(index-1),:); % was too lazy to pre-calculate #
scores=sortrows(scores, 1);
end
I have a feeling pdist2 might help but not sure how. I would appreciate any help in figuring this out.
There is http://www.mathworks.nl/help/matlab/ref/dsearchn.html for closest point search, but that requires same dimensionality. I think you have to bruteforce find it anyway because it's just a special problem.
Here's a way to bruteforce iterate over all unique combinations of the second matrix and calculate the score, after that you can find the one with the minimum score.
A=[ 0 52 77 79;
52 0 10 14;
77 10 0 3;
79 14 3 0];
B=[ 0 51 68 75 78 81 82;
51 0 17 24 28 30 32;
68 17 0 7 11 13 15;
75 24 7 0 4 6 8;
78 28 11 4 0 2 4;
81 30 13 6 2 0 2;
82 32 15 8 4 2 0];
M = size(A,1);
N = size(B,1);
% find all unique permutations of `1:M`
idx = nchoosek(1:N,M);
K = size(idx,1); % number of combinations = valid candidates for matching A
score = NaN(K,1);
idx_triu = triu(true(M,M),1);
Atriu = A(idx_triu);
for ii=1:K
partB = B(idx(ii,:),idx(ii,:));
partB_triu = partB(idx_triu);
score = norm(Atriu-partB_triu,2);
end
[~, best_match_idx] = min(score);
best_match = idx(best_match_idx,:);
The solution of your example actually is [1 2 3 4], so the upperleft part of B and not [1 2 4 6].
This would theoretically solve your problem, and I don't know how to make this algorithm any faster. But it will still be slow for large numbers. For example for your case of M=5 and N=415, there are 100 128 170 583 combinations of B which are a possible solution; just generating the selector indices is impossible in 32-bit because you can't address them all.
I think the real optimization here lies in cutting away some of the planes in the NxN matrix in a preceding filtering part.

Finding index of vector from its original matrix

I have a matrix of 2d lets assume the values of the matrix
a =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
17 24 1 8 15
11 18 25 2 9
This matrix is going to be divided into three different matrices randomly let say
b =
17 24 1 8 15
23 5 7 14 16
c =
4 6 13 20 22
11 18 25 2 9
d =
10 12 19 21 3
17 24 1 8 15
How can i know the index of the vectors in matrix d for example in the original matrix a,note that the values of the matrix can be duplicated.
for example if i want to know the index of {10 12 19 21 3} in matrix a?
or the index of {17 24 1 8 15} in matrix a,but for this one should return only on index value?
I would appreciate it so much if you can help me with this. Thank you in advance
You can use ismember with the 'rows' option. For example:
tf = ismember(a, c, 'rows')
Should produce:
tf =
0
0
1
0
0
1
To get the indices of the rows, you can apply find on the result of ismember (note that it's redundant if you're planning to use this vector for matrix indexing). Here find(tf) return the vector [3; 6].
If you want to know the number of the row in matrix a that matches a single vector, you either use the method explained and apply find, or use the second output parameter of ismember. For example:
[tf, loc] = ismember(a, [10 12 19 21 3], 'rows')
returns loc = 4 for your example. Note that here a is the second parameter, so that the output variable loc would hold a meaningful result.
Handling floating-point numbers
If your data contains floating point numbers, The ismember approach is going to fail because floating-point comparisons are inaccurate. Here's a shorter variant of Amro's solution:
x = reshape(c', size(c, 2), 1, []);
tf = any(all(abs(bsxfun(#minus, a', x)) < eps), 3)';
Essentially this is a one-liner, but I've split it into two commands for clarity:
x is the target rows to be searched, concatenated along the third dimension.
bsxfun subtracts each row in turn from all rows of a, and the magnitude of the result is compared to some small threshold value (e.g eps). If all elements in a row fall below it, mark this row as "1".
It depends on how you build those divided matrices. For example:
a = magic(5);
d = a([2 1 2 3],:);
then the matching rows are obviously: 2 1 2 3
EDIT:
Let me expand on the idea of using ismember shown by #EitanT to handle floating-point comparisons:
tf = any(cell2mat(arrayfun(#(i) all(abs(bsxfun(#minus, a, d(i,:)))<1e-9,2), ...
1:size(d,1), 'UniformOutput',false)), 2)
not pretty but works :) This would be necessary for comparisons such as: 0.1*3 == 0.3
(basically it compares each row of d against all rows of a using an absolute difference)

Matlab - Sum of surrounding elements

I want to calculate the sum of the elements surrounding a given element in a matrix. So far, I have written these lines of code:
for i=1:m,
rij(1:n)=0
for j=1:n,
alive = tijdelijk(i-1,j)+tijdelijk(i+1,j)+tijdelijk(i-1,j-1)+tijdelijk(i+1,j-1)+tijdelijk(i,j+1)+tijdelijk(i,j-1)+tijdelijk(i-1,j+1)+tijdelijk(i+1,j+1)
This results in an error because, for example, i-1 becomes zero for i=1. Anyone got an idea how to do this without getting this error?
You can sum the elements via filtering. conv2 can be used for this manner.
Let me give an example. I create a sample matrix
>> A = reshape(1:20, 4, 5)
A =
1 5 9 13 17
2 6 10 14 18
3 7 11 15 19
4 8 12 16 20
Then, I create a filter. The filter is like a mask where you put the center on the current cell and the locations corresponding to the 1's on the filter are summed. For eight-connected neighbor case, the filter should be as follows:
>> B = [1 1 1; 1 0 1; 1 1 1]
B =
1 1 1
1 0 1
1 1 1
Then, you simply convolve the matrix with this small matrix.
>> conv2(A, B, 'same')
ans =
13 28 48 68 45
22 48 80 112 78
27 56 88 120 83
18 37 57 77 50
If you want four-connected neighbors, you can make the corners of your filter 0. Similarly, you can design any filter for your purpose, such as for averaging all neighbors instead of summing them.
For details, please see the convolution article in Wikipedia.
Two possibilities : change the limits of the loops to i=k:(m-k) and j=k:(n-k) or use blkproc
ex :
compute the 2-D DCT of each 8-by-8 block
I = imread('cameraman.tif');
fun = #dct2;
J = blkproc(I,[8 8],fun);
imagesc(J), colormap(hot)
There are lots of things you can do at the edges. Which you do depends very specifically on your problem and is different from usage case to usage case. Typical things to do:
If (i-1) or (i+1) is out of range, then just ignore that element. This is equivalent to zero padding the matrix with zeros around the outside and adjusting the loop limits accordingly
Wrap around the edges. In other words, for an MxN matrix, if (i-1) takes you to 0 then instead of taking element (i-1, j) = (0, j) you take element (M, j).
Since your code mentions "your teacher" I'd guess that you can ask what should happen at the edges (or working it out in a sensible manner may well be part of the task!!).