Vectorize 2d convolution on matlab - arrays

I got this Code for computing two dimensional convolution for two given arrays.
[r,c] = size(x);
[m,n] = size(y);
h = rot90(y, 2);
center = floor((size(h)+1)/2);
Rep = zeros(r + m*2-2, c + n*2-2);
return
for x1 = m : m+r-1
for y1 = n : n+r-1
Rep(x1,y1) = x(x1-m+1, y1-n+1);
end
end
B = zeros(r+m-1,n+c-1);
for x1 = 1 : r+m-1
for y1 = 1 : n+c-1
for i = 1 : m
for j = 1 : n
B(x1, y1) = B(x1, y1) + (Rep(x1+i-1, y1+j-1) * h(i, j));
end
end
end
end
How can i vectorize it , so no for loops exist ?
Thanks in advance.

Here's what I came up with:
%// generate test matrices
x = randi(12, 4, 5)
y = [2 2 2;
2 0 2;
2 2 2]
[r,c] = size(x);
%[m,n] = size(y); %// didn't use this
h = rot90(y, 2);
center = floor((size(h)+1)/2);
Rep = zeros(size(x)+size(h)-1); %// create image of zeros big enough to pad x
Rep(center(1):center(1)+r-1, center(2):center(2)+c-1) = x; %// and copy x into the middle
%// all of this can be compressed onto one line, if desired
%// I'm just breaking it out into steps for clarity
CRep = im2col(Rep, size(h), 'sliding'); %// 'sliding' is the default, but just to be explicit
k = h(:); %// turn h into a column vector
BRow = bsxfun(#times, CRep, k); %// multiply k times each column of CRep
B = reshape(sum(BRow), r, c) %// take the sum of each column and reshape to match x
T = conv2(Rep, h, 'valid') %// take the convolution using conv2 to check
assert(isequal(B, T), 'Result did not match conv2.');
Here are the results of a sample run:
x =
11 12 11 2 8
5 9 2 3 2
7 9 3 4 8
7 10 8 5 4
y =
2 2 2
2 0 2
2 2 2
B =
52 76 56 52 14
96 120 106 80 50
80 102 100 70 36
52 68 62 54 34
T =
52 76 56 52 14
96 120 106 80 50
80 102 100 70 36
52 68 62 54 34

Related

Match vectors of different length by their maxima and get indices

I have variables
A = [40 67 68 70 66 65 99 90 65 20 21]
B = [1 1 2 3 1]
How to get indices if A by matching the maxima of B and A?
So imagine I slide with B over A, stop when the maxima match, and then I'd like to get the "position" of B by means of the according indices of A.
desired result :
4 5 6 7 8
One way of many:
A = [40 67 68 70 66 65 99 90 65 20 21]
B = [1 1 2 3 1]
%// maxima
[~,mA] = max(A(:))
[~,mB] = max(B(:))
%// result
mDiff = mA - mB
idx = ( mDiff + 1 ) : ( mDiff + numel(B) )

Conversion from 3D cell array to a set of 2D matrices

I have a 3D-cell array designated as A{s,i,h}, serving as a store for large amounts of numerical data during a nested-loop portion of my script. Some of the cell entries will be blank [ ], whilst the rest consist of numbers - either singular or in arrays (1 x 10 double etc.):
I want to convert this cell array to a set of 2D matrices.
Specifically, one separate matrix for each value of h (h is always equal 1:3) and one column in each matrix for every value of s. Each column will contain all the numerical data combined - it does not need to be separated by i.
How can I go about this? I ordinarily deal with 3D-cell arrays in this form to produce separate matrices (one for each value of h) using something like this:
lens = sum(cellfun('length',reshape(A,[],size(A,3))),1);
max_length = max(lens);
mat = zeros(max_length,numel(lens));
mask = bsxfun(#le,[1:max_length]',lens);
mat(mask) = [A{:}];
mat(mat==0) = NaN;
mat = sort(mat*100);
Matrix1 = mat(~isnan(mat(:,1)),1);
Matrix2 = mat(~isnan(mat(:,2)),2);
Matrix3 = mat(~isnan(mat(:,3)),3);
However in this instance, each matrix had only a single column. I'm have trouble adding multiple columns to each output matrix.
1. Result in the form of a cell array of matrices (as requested)
Here's one possible approach. I had to use one for loop. However, the loop can be easily avoided if you accept a 3D-array result instead of a cell array of 2D-arrays. See second part of the answer.
If you follow the comments in the code and inspect the result of each step, it's straightforward to see how it works.
%// Example data
A(:,:,1) = { 1:2, 3:5, 6:9; 10 11:12 13:15 };
A(:,:,2) = { 16:18, 19:22, 23; 24:28, [], 29:30 };
%// Let's go
[S, I, H] = size(A);
B = permute(A, [2 1 3]); %// permute rows and columns
B = squeeze(mat2cell(B, I, ones(1, S), ones(1, H))); %// group each col of B into a cell...
B = cellfun(#(x) [x{:}], B, 'uniformoutput', false); %// ...containing a single vector
t = cellfun(#numel, B); %// lengths of all columns of result
result = cell(1,H); %// preallocate
for h = 1:H
mask = bsxfun(#le, (1:max(t(:,h))), t(:,h)).'; %'// values of result{h} to be used
result{h} = NaN(size(mask)); %// unused values will be NaN
result{h}(mask) = [B{:,h}]; %// fill values for matrix result{h}
end
Result in this example:
A{1,1,1} =
1 2
A{2,1,1} =
10
A{1,2,1} =
3 4 5
A{2,2,1} =
11 12
A{1,3,1} =
6 7 8 9
A{2,3,1} =
13 14 15
A{1,1,2} =
16 17 18
A{2,1,2} =
24 25 26 27 28
A{1,2,2} =
19 20 21 22
A{2,2,2} =
[]
A{1,3,2} =
23
A{2,3,2} =
29 30
result{1} =
1 10
2 11
3 12
4 13
5 14
6 15
7 NaN
8 NaN
9 NaN
result{2} =
16 24
17 25
18 26
19 27
20 28
21 29
22 30
23 NaN
2. Result in the form of 3D array
As indicated above, using a 3D array to store the result permits avoiding loops. In the code below, the last three lines replace the loop used in the first part of the answer. The rest of the code is the same.
%// Example data
A(:,:,1) = { 1:2, 3:5, 6:9; 10 11:12 13:15 };
A(:,:,2) = { 16:18, 19:22, 23; 24:28, [], 29:30 };
%// Let's go
[S, I, H] = size(A);
B = permute(A, [2 1 3]); %// permute rows and columns
B = squeeze(mat2cell(B, I, ones(1, S), ones(1, H))); %// group each col of B into a cell...
B = cellfun(#(x) [x{:}], B, 'uniformoutput', false); %// ...containing a single vector
t = cellfun(#numel, B); %// lengths of all columns of result
mask = bsxfun(#le, (1:max(t(:))).', permute(t, [3 1 2])); %'// values of result to be used
result = NaN(size(mask)); %// unused values will be NaN
result(mask) = [B{:}]; %// fill values
This gives (compare with result of the first part):
>> result
result(:,:,1) =
1 10
2 11
3 12
4 13
5 14
6 15
7 NaN
8 NaN
9 NaN
result(:,:,2) =
16 24
17 25
18 26
19 27
20 28
21 29
22 30
23 NaN
NaN NaN
Brute force approach:
[num_s, num_i, num_h] = size(A);
cellofmat = cell(num_h,1);
for matrix = 1:num_h
sizemat = max(cellfun(#numel, A(:,1,matrix)));
cellofmat{matrix} = nan(sizemat, num_s);
for column = 1:num_s
lengthcol = length(A{column, 1, matrix});
cellofmat{matrix}(1:lengthcol, column) = A{column, 1,matrix};
end
end
Matrix1 = cellofmat{1};
Matrix2 = cellofmat{2};
Matrix3 = cellofmat{3};
I don't know what your actual structure looks like but this works for A that is setup using the following steps.
A = cell(20,1,3);
for x = 1:3
for y = 1:20
len = ceil(rand(1,1) * 10);
A{y,1,x} = rand(len, 1);
end
end

Inserting One Row Each Time in a Sequence from Matrix into Another Matrix After Every nth Row in Matlab

I have matrix A and matrix B. Matrix A is 100*3. Matrix B is 10*3. I need to insert one row from matrix B each time in a sequence into matrix A after every 10th row. The result would be Matrix A with 110*3. How can I do this in Matlab?
Here's another indexing-based approach:
n = 10;
C = [A; B];
[~, ind] = sort([1:size(A,1) n*(1:size(B,1))+.5]);
C = C(ind,:);
For canonical purposes, here's how you'd do it via loops. This is a bit inefficient since you're mutating the array at each iteration, but it's really simple to read. Given that your two matrices are stored in A (100 x 3) and B (10 x 3), you would do:
out = [];
for idx = 1 : 10
out = [out; A((idx-1)*10 + 1 : 10*idx,:); B(idx,:)];
end
At each iteration, we pick out 10 rows of A and 1 row of B and we concatenate these 11 rows onto out. This happens 10 times, resulting in 330 rows with 3 columns.
Here's an index-based approach:
%//pre-allocate output matrix
matrixC = zeros(110, 3);
%//create index array for the locations in matrixC that would be populated by matrixB
idxArr = (1:10) * 11;
%//place matrixB into matrixC
matrixC(idxArr,:) = matrixB;
%//place matrixA into matrixC
%//setdiff is used to exclude indexes already populated by values from matrixB
matrixC(setdiff(1:110, idxArr),:) = matrixA;
And just for fun here's the same approach sans magic numbers:
%//define how many rows to take from matrixA at once
numRows = 10;
%//get dimensions of input matrices
lengthA = size(matrixA, 1);
lengthB = size(matrixB, 1);
matrixC = zeros(lengthA + lengthB, 3);
idxArr = (1:lengthB) * (numRows + 1);
matrixC(idxArr,:) = matrixB;
matrixC(setdiff(1:size(matrixC, 1), idxArr),:) = matrixA;
Just for fun... Now with more robust test matrices!
A = ones(3, 100);
A(:) = 1:300;
A = A.'
B = ones(3, 10);
B(:) = 1:30;
B = B.' + 1000
C = reshape(A.', 3, 10, []);
C(:,end+1,:) = permute(B, [2 3 1]);
D = permute(C, [2 3 1]);
E = reshape(D, 110, 3)
Input:
A =
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 26 27
28 29 30
31 32 33
34 35 36
...
B =
1001 1002 1003
1004 1005 1006
...
Output:
E =
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 26 27
28 29 30
1001 1002 1003
31 32 33
34 35 36
...
Thanks to #Divakar for pointing out my previous error.
Solution Code
Here's an implementation based on logical indexing also known as masking and must be pretty efficient when working with large arrays -
%// Get sizes of A and B
[M,d] = size(A);
N = size(B,1);
%// Mask of row indices where rows from A would be placed
mask_idx = reshape([true(A_cutrow,M/A_cutrow) ; false(1,N)],[],1);
%// Pre-allocate with zeros:
%// http://undocumentedmatlab.com/blog/preallocation-performance
out(M+N,d) = 0;
%// Insert A and B using mask and ~mask
out(mask_idx,:) = A;
out(~mask_idx,:) = B;
Benchmarking
%// Setup inputs
A = rand(100000,3);
B = rand(10000,3);
A_cutrow = 10;
num_iter = 200; %// Number of iterations to be run for each approach
%// Warm up tic/toc.
for k = 1:50000
tic(); elapsed = toc();
end
disp(' ------------------------------- With MASKING')
tic
for iter = 1:num_iter
[M,d] = size(A);
N = size(B,1);
mask_idx = reshape([true(A_cutrow,M/A_cutrow) ; false(1,N)],[],1);
out(M+N,d) = 0;
out(mask_idx,:) = A;
out(~mask_idx,:) = B;
clear out
end
toc, clear mask_idx N M d iter
disp(' ------------------------------- With SORT')
tic
for iter = 1:num_iter
C = [A; B];
[~, ind] = sort([1:size(A,1) A_cutrow*(1:size(B,1))+.5]);
C = C(ind,:);
end
toc, clear C ind iter
disp(' ------------------------------- With RESHAPE+PERMUTE')
tic
for iter = 1:num_iter
[M,d] = size(A);
N = size(B,1);
C = reshape(A.', d, A_cutrow , []);
C(:,end+1,:) = permute(B, [2 3 1]);
D = permute(C, [2 1 3]);
out = reshape(permute(D,[1 3 2]),M+N,[]);
end
toc, clear out D C N M d iter
disp(' ------------------------------- With SETDIFF')
tic
for iter = 1:num_iter
lengthA = size(A, 1);
lengthB = size(B, 1);
matrixC = zeros(lengthA + lengthB, 3);
idxArr = (1:lengthB) * (A_cutrow + 1);
matrixC(idxArr,:) = B;
matrixC(setdiff(1:size(matrixC, 1), idxArr),:) = A;
end
toc, clear matrixC idxArr lengthA lengthB
disp(' ------------------------------- With FOR-LOOP')
tic
for iter = 1:num_iter
[M,d] = size(A);
N = size(B,1);
Mc = M/A_cutrow;
out(M+N,d) = 0;
for idx = 1 : Mc
out( 1+(idx-1)*(A_cutrow +1): idx*(A_cutrow+1), :) = ...
[A( 1+(idx-1)*A_cutrow : idx*A_cutrow , : ) ; B(idx,:)];
end
clear out
end
toc
Runtimes
Case #1: A as 100 x 3 and B as 10 x 3
------------------------------- With MASKING
Elapsed time is 4.987088 seconds.
------------------------------- With SORT
Elapsed time is 5.056301 seconds.
------------------------------- With RESHAPE+PERMUTE
Elapsed time is 5.170416 seconds.
------------------------------- With SETDIFF
Elapsed time is 35.063020 seconds.
------------------------------- With FOR-LOOP
Elapsed time is 12.118992 seconds.
Case #2: A as 100000 x 3 and B as 10000 x 3
------------------------------- With MASKING
Elapsed time is 1.167707 seconds.
------------------------------- With SORT
Elapsed time is 2.667149 seconds.
------------------------------- With RESHAPE+PERMUTE
Elapsed time is 2.603110 seconds.
------------------------------- With SETDIFF
Elapsed time is 3.153900 seconds.
------------------------------- With FOR-LOOP
Elapsed time is 19.822912 seconds.
Please note that num_iter was different for these two cases, as the idea was to keep the runtimes > 1 sec mark to compensate for tic-toc overheads.

MATLAB Merging Arrays

I am unable to figure out how to merge two arrays. My data is like this with arrays A and B.
A = [ 0 0; 0 0; 2 2; 2 2;]
B = [ 1 1; 1 1; 3 3; 3 3; 4 4; 4 4; 5 5; 5 5;]
and I need the final array "C" to look like this after merging:
C = [ 0 0; 0 0; 1 1; 1 1; 2 2; 2 2; 3 3; 3 3; 4 4; 4 4; 5 5; 5 5;]
I've tried using different ways with reshaping each array and trying to use a double loop but haven't got it to work yet.
In my actual data it is inserting 9 rows of array B following 3 rows of array A and then repeated 100 times. So, there are 12 new merged rows (3 rows from array A and 9 rows from array B) repeated 100 times with a final row number == 1200. Array A actual data has 300 rows and actual Array B data has 900 rows
thanks,
Here's a solution using only reshape:
A = [ 6 6; 3 3; 5 5; 4 4;]
B = [ 0 0; 21 21; 17 17; 33 33; 29 29; 82 82;]
A_count = 2;
B_count = 3;
w = size(A,2); %// width = number of columns
Ar = reshape(A,A_count,w,[]);
Br = reshape(B,B_count,w,[]);
Cr = [Ar;Br];
C = reshape(Cr,[],w)
The [] in reshape means "how ever many you need to get to the total number of elements". So if we have 12 elements in B and do:
Br = reshape(B,3,2,[]);
We're reshaping B into a 3x2xP 3-dimensional matrix. Since the total number of elements is 12, P = 2 because 12 = 3x2x2.
Output:
A =
6 6
3 3
5 5
4 4
B =
0 0
21 21
17 17
33 33
29 29
82 82
C =
6 6
3 3
0 0
21 21
17 17
5 5
4 4
33 33
29 29
82 82
Approach #1
This could be one approach assuming I got the requirements of the problem right -
%// Inputs
A = [ 6 6; 3 3; 5 5; 4 4;];
B = [ 0 0; 21 21; 17 17; 33 33; 29 29; 82 82;];
%// Parameters that decide at what intervals to "cut" A and B along the rows
A_cutlen = 2; %// Edit this to 3 for the actual data
B_cutlen = 3; %// Edit this to 9 for the actual data
%// Cut A and B along the rows at specified intervals into 3D arrays
A3d = permute(reshape(A,A_cutlen,size(A,1)/A_cutlen,[]),[1 3 2])
B3d = permute(reshape(B,B_cutlen,size(B,1)/B_cutlen,[]),[1 3 2])
%// Vertically concatenate those 3D arrays to get a 3D array
%// version of expected output, C
C3d = [A3d;B3d]
%// Convert the 3D array to a 2D array which is the final output
C_out = reshape(permute(C3d,[1 3 2]),size(C3d,1)*size(C3d,3),[])
Sample run -
A =
6 6
3 3
5 5
4 4
B =
0 0
21 21
17 17
33 33
29 29
82 82
A_cutlen =
2
B_cutlen =
3
C_out =
6 6
3 3
0 0
21 21
17 17
5 5
4 4
33 33
29 29
82 82
Approach #2
Just for the love of bsxfun, here's one approach with it and ones (no reshape or permute) -
%// Assuming A_cutlen and B_cutlen decide cutting intervals for A and B
%// Concatenate A and B along rows
AB = [A;B]
%// Find the row indices corresponding to rows from A and B to be placed
%// according to the problem requirements
idx1 = [1:A_cutlen size(A,1)+[1:B_cutlen]]
idx2 = [A_cutlen*ones(1,A_cutlen) B_cutlen*ones(1,B_cutlen)]
idx = bsxfun(#plus,idx1(:),idx2(:)*[0:size(A,1)/A_cutlen-1])
%// Re-arrange AB based on "idx" for the desired output
C = AB(idx,:)
based on your new criteria this is what you want. My solution isn't the nicest looking (maye someone can think of a nice vectorized approach), but it works
a_step = 2;
b_step = 3;
C = zeros(size([A;B]));
%we use two iterators, one for each matrix, they must be initialized to 1
a_idx = 1;
b_idx = 1;
%this goes through the entire c array doing a_step+b_step rows at a
%time
for c_idx=1:a_step+b_step :size(C,1)-1
%this takes the specified number of rows from a
a_part = A(a_idx:a_idx+a_step-1,:);
%tkaes the specified number of rows from b
b_part = B(b_idx:b_idx+b_step-1,:);
%combines the parts together in the appropriate spot in c
C(c_idx:c_idx + a_step + b_step -1,:) = [a_part;b_part];
%advances the "iterator" on the a and b matricies
a_idx = a_idx + a_step;
b_idx = b_idx + b_step;
end
using
A = [ 6 6; 3 3; 5 5; 4 4;]
B = [ 0 0; 21 21; 17 17; 33 33; 29 29; 82 82;]
produces
C =[6 6; 3 3; 0 0; 21 21; 17 17; 5 5; 4 4; 33 33; 29 29; 82 82;]

matlab retrieve elements in a vector corresponding to matrix of indexes

I have a vector v.
I also have a matrix M of size TxN with each column corresponding to T indices of v.
For example: M(:,1) is the set of indices [1,2,12,5,4] (here T = 5).
I want to have a matrix O of size TxN with O(:,i) = v(M(:,i)) for all i.
Is there a way to do that without using for loops ?
Thanks a lot
Very easy: just use
O = v(M);
Example with T=3, N=4:
>> v = (10:10:50).'
v =
10
20
30
40
50
>> M = randi(5,T,N)
M =
5 3 5 3
2 3 1 4
2 4 5 3
>> O = v(M)
O =
50 30 50 30
20 30 10 40
20 40 50 30

Resources