In MATLAB, I am looking for an efficient (and/or vectorized) way of filling a matrix by selecting from multiple matrices given a "selector matrix." For instance, given three source matrices
M1 = [0.1, 0.2; 0.3, 0.4]
M2 = [1, 2; 3, 4]
M3 = [10, 20; 30, 40]
and a matrix of indices
I = [1, 3; 1, 2]
I want to generate a new matrix M = [0.1, 20; 0.3, 4] by selecting the first entry from M1, second from M3, etc.
I can definitely do it in a nested loop, going through each entry and filling in the value, but I am sure there is a more efficient way.
What if M1, M2, M3 and M are all 3D matrices (RGB images)? Each entry of I tells us from which matrix we should take a 3-vector. Say, if I(1, 3) = 3, then we know entries indexed by (1, 3, :) of M should be M3(1, 3, :).
A way of doing this, without changing the way you store your variable is to use masks. If you have a few matrices, it is doing the job avoiding a for loop. You won't be able to fully vectorize without going through the cat function, or using cells.
M = zeros(size(M1));
Itmp = repmat(I==1,[1 1 size(M1,3)]); M(Itmp) = M1(Itmp);
Itmp = repmat(I==2,[1 1 size(M1,3)]); M(Itmp) = M2(Itmp);
Itmp = repmat(I==3,[1 1 size(M1,3)]); M(Itmp) = M3(Itmp);
I think the best way to approach this is to stack dimensions (ie have a matrix with values that are each of your indvidiual matricies). Unfortunately MATLAB doesn't really support array level indexing so what ends up happening is you end up using linear indexing to convert your values through the subs2ind command. I believe you can use the code below.
M1 = [0.1, 0.2; 0.3, 0.4]
M2 = [1, 2; 3, 4]
M3 = [10, 20; 30, 40]
metamatrix=cat(3,M1,M2,M3)
%Create a 3 dimenssional or however many dimension matrix by concatenating
%lower order matricies
I=[1,1,1;1,2,3;2,1,1;2,2,2]
M=reshape(metamatrix(sub2ind(size(metamatrix),I(:,1),I(:,2),I(:,3))),size(metamatrix(:,:,1)))
With a more complex (3 dimensional case), you would have to extend the code for higher dimensions.
One way of doing this could be to generate a 4D matrix with you images. It has the cost of increasing the amount of memory, or at least, change you memory scheme.
Mcat = cat(4, M1, M2, M3);
Then you can use the function sub2ind to get a vectorized Matrix creation.
% get the index for the basic Image matrix
I = repmat(I,[1 1 3]); % repeat the index for for RGB images
Itmp = sub2ind(size(I),reshape(1:numel(I),size(I)));
% update so that indices reach the I(x) value element on the 4th dim of Mcat.
Itmp = Itmp + (I-1)*numel(I);
% get the matrix
M = Mcat(Itmp);
I haven't tested it properly, but it should work.
Related
I have a 3D-matrix A, with size lets say 3x12x100. The first two dimensions define 3×12 matrices, the latter one is simply the linear index. I want a very simple operation on these 100 matrices. For all these matrices, i want them multiplied with its conjugate transpose. With a very simple for loop, i can create this:
data = data;
A = zeros(100, 12, 12);
for i=1:100
A(i, :, :) = data(:, :, i)'*data(:, :, i);
end
But i like clean code, so i dont really prefer this for-loop. I have done some searching and sometimes find something like mtimesx (which is a custom made MATLAB function from 2010). I think i am missing something very obvious (as usual), because this seems a fairly easy operation (its just an "element-wise" matrix multiplication).
The size of my actual matrix is 3x12x862400. My original script takes about 10 minutes or longer, a variant on what #FangQ posts fixes it in a matter of seconds. My new code is as following, note that it still is under construction and i still need to validate it:
data = rand(3, 12, 862400) + i*rand(3, 12, 862400)
data2 = conj(permute(data, [2 1 3])); % conjugate transpose each matrix
% my data matrix contains 862400 3x12 matrices with complex numbers
Ap = permute(data2, [2 1 4 3]);
Bp = permute(data, [1 4 2 3]);
M = Ap.*Bp;
M = sum(M, 1);
M = permute(M, [2 3 4 1]);
#Cris was right, you can find an example from this MatlabCentral post
https://www.mathworks.com/matlabcentral/answers/10161-3d-matrix-multiplication#answer_413531
I tried to padarray more than 1000 images. However when I time my code, this specific line take the highest amount of time to complete
I=abs(padarray(I, [2, 2], 'replicate', 'both'));
Mainly because of the line 35 of the padarray algorithm (inside profiler): images\private\padarray_algo
b = a(aIdx{:});
Any way to improve the efficiency? Perhaps using another method? Thanks!
You can use repmat and matrix concatenation to get the same result:
r=#repmat;
pad=#(I,d)[r(I(1),d) r(I(1,:),d(1),1) r(I(1,end),d)
r(I(:,1),1,d(2)) I r(I(:,end),1,d(2))
r(I(end,1),d) r(I(end,:),d(1),1) r(I(end),d)];
Usage:
pad(I,[2 2])
If all images are of the same size you can create a matrix of linear indices of the image and apply padarray to it then use padded index array to pad images:
%create matrix of indices
Idx = reshape(1:numel(I),size(I));
%pad the index
Idx_padded = padarray(Idx, [2, 2], 'replicate', 'both');
%use the padded index to pad images
result = I(Idx_padded);
result2 = I2(Idx_padded);
Consider this example of code to obtain the best fit from data varying the number of fitting Gaussians according the Akaike criterion
MU1 = [1];
SIGMA1 = [2];
MU2 = [-3];
SIGMA2 = [1 ];
X = [mvnrnd(MU1,SIGMA1,1000);mvnrnd(MU2,SIGMA2,1000)];
AIC = zeros(1,4);
obj = cell(1,4);
options = statset('Display','final');
for k = 1:4
obj{k} = gmdistribution.fit(X,k,'Options',options);
AIC(k)= obj{k}.AIC;
end
[minAIC,numComponents] = min(AIC)
I want to do the same thing but with data that are given in a form of a histogram (consider for example the data http://pastebin.com/embed_js.php?i=1mNRuEHZ).
What is the most direct way to implement the same procedure in matlab in this case?
If I'm getting you right, then your problem is to convert between data that is already compiled as a histogram (so numbers of observations paired with the actual value of an observation) and the original individual observations. Of course, when compiling the histogram, you have lost two things:
Order. You don't know what the order of observations was in the original data, which is probably not important, provided your observations are independent. Also, the way I get gmdistribution.fit() it doesn't take into account order anyway.
Resolution. When you create a histogram, you need to bin your data, which makes you lose precision, so to speak, because it is impossible to recover the precise values of your observations from the bins.
Once you are aware of that you can create a 'vector of observations' from your histogram data. Say, X1 is your histogram data (Nx2 vector). If you do
invX = cell2mat(arrayfun(#(x,y) repmat(y,1,x), abs(int16(1000*X1(:, 2)))', X1(:, 1)', ...
'UniformOutput', false))';
you get a vector that contains individual observations, just like X in your example.
Note that you have to convert the bin counts to integers first. At this step, because the given data's precision is quite high, I had to round to make the computation possible for my machine. However, the final result seems fairly reasonable.
Also note that I used absolute values, there are some cases in your histogram data were your data is actually negative, which, for a histogram obviously doesn't make sense.
Last but not least you have to change the number of iterations for the fit procedure to 1000. The final code to produce the below figure reads
MU1 = [1];
SIGMA1 = [2];
MU2 = [-3];
SIGMA2 = [1 ];
X = [mvnrnd(MU1,SIGMA1,1000);mvnrnd(MU2,SIGMA2,1000)];
X = X1(:, 2);
invX = cell2mat(arrayfun(#(x,y) repmat(y,1,x), abs(int16(1000*X1(:, 2)))', X1(:, 1)', ...
'UniformOutput', false))'; %'
X = invX;
AIC = zeros(1,4);
obj = cell(1,4);
options = statset('Display','final', 'MaxIter', 1000);
for k = 1:4
obj{k} = gmdistribution.fit(X,k,'Options',options);
AIC(k)= obj{k}.AIC;
end
[minAIC,numComponents] = min(AIC);
hold on;
plot(linspace(-1, 2, length(X1(:, 2))), abs(X1(:, 2)), 'LineWidth', 2)
plot(x, pd/max(pd)*double(max(abs(X1(:, 2)))), 'LineWidth', 5);
h = legend('Original data', 'PDF');
set(h,'FontSize',32);
Output looks like this:
Given a square matrix of say size 400x400, how would I go about splitting this into constituent sub-matrices of 20x20 using a for-loop? I can't even think where to begin!
I imagine I want something like :
[x,y] = size(matrix)
for i = 1:20:x
for j = 1:20:y
but I'm unsure how I would proceed. Thoughts?
Well, I know that the poster explicitly asked for a for loop, and Jeff Mather's answer provided exactly that.
But still I got curious whether it is possible to decompose a matrix into tiles (sub-matrices) of a given size without a loop. In case someone else is curious, too, here's what I have come up with:
T = permute(reshape(permute(reshape(A, size(A, 1), n, []), [2 1 3]), n, m, []), [2 1 3])
transforms a two-dimensional array A into a three-dimensional array T, where each 2d slice T(:, :, i) is one of the tiles of size m x n. The third index enumerates the tiles in standard Matlab linearized order, tile rows first.
The variant
T = permute(reshape(A, size(A, 1), n, []), [2 1 3]);
T = permute(reshape(T, n, m, [], size(T, 3)), [2 1 3 4]);
makes T a four-dimensional array where T(:, :, i, j) gives the 2d slice with tile indices i, j.
Coming up with these expressions feels a bit like solving a sliding puzzle. ;-)
I'm sorry that my answer does not use a for loop either, but this would also do the trick:
cellOf20x20matrices = mat2cell(matrix, ones(1,20)*20, ones(1,20)*20)
You can then access the individual cells like:
cellOf20x20matrices{i,j}(a,b)
where i,j is the submatrix to fetch (and a,b is the indexing into that matrix if needed)
Regards
You seem really close. Just using the problem as you described it (400-by-400, divided into 20-by-20 chunks), wouldn't this do what you want?
[x,y] = size(M);
for i = 1:20:x
for j = 1:20:y
tmp = M(i:(i+19), j:(j+19));
% Do something interesting with "tmp" here.
end
end
Even though the question is basically for 2D matrices, inspired by A. Donda's answer I would like to expand his answer to 3D matrices so that this technique could be used in cropping True Color images (3D)
A = imread('peppers.png'); %// size(384x512x3)
nCol = 4; %// number of Col blocks
nRow = 2; %// number of Row blocks
m = size(A,1)/nRow; %// Sub-matrix row size (Should be an integer)
n = size(A,2)/nCol; %// Sub-matrix column size (Should be an integer)
imshow(A); %// show original image
out1 = reshape(permute(A,[2 1 4 3]),size(A,2),m,[],size(A,3));
out2 = permute(reshape(permute(out1,[2 1 3 4]),m,n,[],size(A,3)),[1 2 4 3]);
figure;
for i = 1:nCol*nRow
subplot(nRow,nCol,i); imshow(out2(:,:,:,i));
end
The basic idea is to make the 3rd Dimension unaffected while reshaping so that the image isn't distorted. To achieve this, additional permuting was done to swap 3rd and 4th dimensions. Once the process is done, the dimensions are restored as it was, by permuting back.
Results:
Original Image
Subplots (Partitions / Sub Matrices)
Advantage of this method is, it works good on 2D images as well.
Here is an example of a Gray Scale image (2D). Example used here is MatLab in-built image 'cameraman.tif'
With some many upvotes for the answer that makes use nested calls to permute, I thought of timing it and comparing to the other answer that makes use of mat2cell.
It is true that they don't return the exact same thing but:
the cell can be easily converted into a matrix like the other (I timed this, see further down);
when this problem arises, it is preferable (in my experience) to have the data in a cell since later on one will often want to put the original back together;
Anyway, I have compared them both with the following script. The code was run in Octave (version 3.9.1) with JIT disabled.
function T = split_by_reshape_permute (A, m, n)
T = permute (reshape (permute (reshape (A, size (A, 1), n, []), [2 1 3]), n, m, []), [2 1 3]);
endfunction
function T = split_by_mat2cell (A, m, n)
l = size (A) ./ [m n];
T = mat2cell (A, repmat (m, l(1), 1), repmat (n, l (2), 1));
endfunction
function t = time_it (f, varargin)
t = cputime ();
for i = 1:100
f(varargin{:});
endfor
t = cputime () - t;
endfunction
Asizes = [30 50 80 100 300 500 800 1000 3000 5000 8000 10000];
Tsides = [2 5 10];
As = arrayfun (#rand, Asizes, "UniformOutput", false);
for d = Tsides
figure ();
t1 = t2 = [];
for A = As
A = A{1};
s = rows (A) /d;
t1(end+1) = time_it (#split_by_reshape_permute, A, s, s);
t2(end+1) = time_it (#split_by_mat2cell, A, s, s);
endfor
semilogy (Asizes, [t1(:) t2(:)]);
title (sprintf ("Splitting in %i", d));
legend ("reshape-permute", "mat2cell");
xlabel ("Length of matrix side (all squares)");
ylabel ("log (CPU time)");
endfor
Note that the Y axis is in log scale
Performance
Performance wise, using the nested permute will only be faster for smaller matrices where big changes in relative performance are actually very small changes in time. Note that the Y axis is in log scale, so the difference between the two functions for a 100x100 matrix is 0.02 seconds while for a 10000x10000 matrix is 100 seconds.
I have also tested the following which will convert the cell into a matrix so that the return values of the two functions are the same:
function T = split_by_mat2cell (A, m, n)
l = size (A) ./ [m n];
T = mat2cell (A, repmat (m, l(1), 1), repmat (n, l (2), 1), 1);
T = reshape (cell2mat (T(:)'), [m n numel(T)]);
endfunction
This does slow it down a bit but not enough to consider (the lines will cross at 600x600 instead of 400x400).
Readability
It is so much more difficult to get your head around the use of the nested permute and reshape. It's mad to use it. It will increase maintenance time by a lot (but hey, this is Matlab language, it's not supposed to be elegant and reusable).
Future
The nested calls to permute does not expand nicely at all into N dimensions. I guess it would require a for loop by dimension (which would not help at all the already quite cryptic code). On the other hand, making use of mat2cell:
function T = split_by_mat2cell (A, lengths)
dl = arrayfun (#(l, s) repmat (l, s, 1), lengths, size (A) ./ lengths, "UniformOutput", false);
T = mat2cell (A, dl{:});
endfunction
Edit (and tested in Matlab too)
The amount of upvotes on the answer suggesting to use permute and reshape got me so curious that I decided to get this tested in Matlab (R2010b). The results there were pretty much the same, i.e., it's performance is really poor. So unless this operation will be done a lot of times, in matrices that will always be small (less than 300x300), and there will always be a Matlab guru around to explain what it does, don't use it.
If you want to use a for loop you can do this:
[x,y] = size(matrix)
k=1; % counter
for i = 1:20:x
for j = 1:20:y
subMatrix=Matrix(i:i+19, j:j+19);
subMatrixCell{k}=subMatrix; % if you want to save all the
% submatrices into a cell array
k=k+1;
end
end
I would like to compute the weighted maxima of a vector in Matlab. For weighted maxima I intend the following:
Given a vector of 2*N+1 weights W={w[-N], w[-N+1] .. w[0] .. w[N]} and given an input sequence A, weighted maxima is a vector M where m[i]=max(w[-N]*a[i-N], w[-N+1]*a[i-N+1], ... w[N]*a[i+N])
So for example given a vector A= [1, 4, 12, 2, 4] and weights W=[0.5, 1, 0.5], the weighted maxima would be M=[2, 6, 12, 6, 4].
This can be done using ordfilt2, but ordfilt2 uses weights as additive rather then multiplicative.
I am actually working on 4D matrixes, but any 1D solution would work as the 4D weight matrix is separable.
My current solution is to generate shifted copies of the input array A, weight them according to the shift and maximize all the arrays. Shift is performed using circshift and is the bottleneck in the process. generating shifted matrixes "manually" trough indexing turned out to be even slower.
Can you suggest any more efficient solution?
EDIT: For a positive A, M=exp(ordfilt2(log(A), length(W), ones(size(W)), log(W))) does the job, but still takes longer than the circshift solution above. I am still looking for more efficient solutions.
>> B = padarray(A, [0 floor(numel(W)/2)], 0); % Pad A with zeros
>> B = bsxfun(#times, B(bsxfun(#plus, 1:numel(B)-numel(W)+1, (0:numel(W)-1)')), W(:)); % Apply the weights
>> M = max(B) % Compute the local maxima
M =
2 6 12 6 4