How to update image values by identifying unique locations in MATLAB? - matlab

I have a image matrix named final_img. I have image location matrix with rows and columns is given below
a =
1 1
1 2
2 1
2 2
3 1
3 2
1 1
2 2
and values of this locations are
b =
1
2
3
4
5
6
7
8
In above given locations, some are repeating eg: location [1 1] . I can identify unique locations by using following code
[uniquerow, ~, rowidx] = unique(a, 'rows');
noccurrences = accumarray(rowidx, 1);
I need to update unique image locations by summing image location values. Eg: location [1 1] is repeating twice and corresponding value in b are 1 and 7. So
final_img(1,1) should be 1+7=8;
How can I implement this algorithm in MATLAB without using for loop?

You can use the sparse function, which automatically adds all values corresponding to the same coordinates:
final_img = full(sparse(a(:,1), a(:,2), b));
This will create a matrix as small as possible according to the input.
If you want an ouput that is as small as possible with the resctriction that it be square:
M = max(a(:));
final_img = full(sparse(a(:,1), a(:,2), b, M, M));
If you want to specify size of the output:
M = 3;
N = 3;
final_img = full(sparse(a(:,1), a(:,2), b, M, N));

You were so incredibly close:
[final_coords, ~, rowidx] = unique(a, 'rows');
final_vals = accumarray(rowidx, b);
Then to get it to image form:
% empty matrix with size of your image
final_img = zeros(max(final_coords,[],1));
% get linear indexes from coordinates
ind = sub2ind(size(final_img), final_coords(:,1), final_coords(:,2));
% fill image
final_img(ind) = final_vals;

Related

extract 3D matrix's columns based on "surface" values - vectorization

I have an NxMxK matrix A
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3])
A(:,:,1) =
1 2 1
1 1 2
A(:,:,2) =
3 3 3
3 3 3
and I want to create a YxK 2D matrix B where K is the number of elements of A(:,:,1)==2:
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = A(ii,jj,:);
end
end
end
Is there a way of vectorizing this code?
My attempt was to find the indices of A(:,:,1)==2 and then try to select the whole column but I do not know how to do it:
inds = find(A(:,:,1)==2)
B = A(inds,:) %this line does not make sense
EDIT
Preallocating B helps:
inds=find(A(:,:,1)==2);
B=NaN(numel(inds),size(A,3));
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = squeeze(A(ii,jj,:));
end
end
end
But still not vectorized.
You can reshape matrix A to a (N*M)xK 2D matrix.
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3]);
A_ = reshape(A,numel(A(:,:,1)),size(A,3));
B = A_(A_(:,1)==2,:);
Your first attempt at vectorization is almost right. Just don't use find, but use the logical matrix for indexing.
inds = A(:,:,1)==2;
The inds matrix is 2D, not 3D, so we use repmat to repeat its values along the 3rd dimension:
K = size(A,3);
inds = repmat(inds,1,1,K); % or simply cat(3,inds,inds) if K==2
B = A(inds);
The result is a column vector of size Y*K, not a matrix of size YxK, we can use reshape to fix that:
B = reshape(B,[],K);
I guess this answer is similar to Anthony's, except the indexing and the reshaping are reversed. I didn't really notice the similarity until after I wrote it down. I guess also Anthony's is a little shorter. :/

Convert a cell containing matrices to a 2d matrix

I want to find easy way to convert a 1x324 cell array which contains matrices to a 2-dimensional matrix.
Each of the cell array's elements is a matrix of size 27x94, so they contain 2538 different values. I want to convert this cell array of matrices to a 324x2538 matrix - where the rows of the output contain each matrix (as a row vector) from the cell array.
To clarify what my data looks like and what I'm trying to create, see this example:
matrix1 = [1,2,3,4,...,94 ; 95,96,97,... ; 2445,2446,2447,...,2538]; % (27x94 matrix)
% ... other matrices are similar
A = {matrix1, matrix2, matrix3, ..., matrix324}; % Matrices are in 1st row of cell array
What I am trying to get:
% 324x2538 output matrix
B = [1 , 2 , ..., 2538 ; % matrix1
2539 , 2540, ..., 5076 ; % matrix2
...
819775, 819776, ..., 822312];
The cell2mat function does exactly that. The doc example:
C = {[1], [2 3 4];
[5; 9], [6 7 8; 10 11 12]};
A = cell2mat(C)
A =
1 2 3 4
5 6 7 8
9 10 11 12
You have your matrix now, so just rework it to contain rows:
B = rand(27,302456); % your B
D = reshape(B,27,94,324); % stack your matrices to 3D
E = reshape(D,1, 2538,324); % reshape each slice to a row vector
E = permute(E,[3 2 1]); % permute the dimensions to the correct order
% Based on sizes instead of fixed numbers
% D = reshape(B, [size(A{1}) numel(A)]);
% E = reshape(D,[1 prod(size(A{1})) numel(A)]);
% E = permute(E,[3 2 1]); % permute the dimensions to the correct order
Or, to one line it from your B:
B = reshape(B,prod(size(A{1})),numel(A)).'
One way to write this would be using cellfun to operate on each element of the cell, then concatenating the result.
% Using your input cell array A, turn all matrices into column vectors
% You need shiftdim so that the result is e.g. [1 2 3 4] not [1 3 2 4] for [1 2; 3 4]
B = cellfun(#(r) reshape(shiftdim(r,1),[],1), A, 'uniformoutput', false);
% Stack all columns vectors together then transpose
B = [B{:}].';
Now I found the solution and I will add it here if anyone have similar problems in future:
for ii = 1:length(A)
B{ii} = A{ii}(:);
end
B = cell2mat(B).';

How to index a matrix with the column maxima of other matrix

I have 2 matrices A and B.
I find the max values in the columns of A, and keep their indices in I. So far so good.
Now, I need to choose those arrays of B with the same index as stored in I. I don't know how to do this.
See below:
A = [1,2,3; 0,8,9]
B = [0,1,2; 4,2,3]
[~,I] = max(A)
h = B(I)
I need to get these values of B:
h = [0 2 3]
But the code results in a different one. How can I fix it?
A =
1 2 3
0 8 9
B =
0 1 2
4 2 3
I =
1 2 2
h =
0 4 4
Thanks in advance
The max function how you used it works like
If A is a matrix, then max(A) is a row vector containing the maximum value of each column.
so M = max(A) is equivalent to M = max(A,[],1). But rather use the third input if you're not sure.
If you use max to find the maxima in the columns of the matrix, it returns the row indices. The column indices are for your case simply 1:size(A,2) = [1 2 3].
Now you need to convert your row and column indices to linear indices with sub2ind:
%// data
A = [1,2,3; 0,8,9]
B = [0,1,2; 4,2,3]
%// find maxima of each column in A
[~, I] = max( A, [], 1 ) %// returns row indices
%// get linear indices for both, row indices and column indices
I = sub2ind( size(A), I, 1:size(A,2) )
%// index B
h = B(I)
returns:
h =
0 2 3

pick random numbers in certain range from vector

how would you use randperm to randomly pick three numbers out of a range of 5 in a vector?
i have a vector like this:
A = [1 2 3 4 5 6 7 8 9 10]
now from every 5 consecutive values i want to randomly pick 3 of them:
A_result = [1 3 5 6 7 9]
any help is appreciated!
This one uses different random indices in every 5-group.
A = [1 2 3 4 5 6 7 8 9 10]
B = reshape(A,5,[]) % (5 x 2)
ind = cell2mat(arrayfun(#(x)sort(randperm(5,3))',1:size(B,2),'UniformOutput',false)) % (3 x 2), row indices into B
ind = bsxfun(#plus,ind,size(B,1)*(0:size(B,2)-1)) % (3 x 2), linear indices into B
C = B(ind) % (3 x 2) result
C(:)' % result vector
Every sort(randperm(5,3))' call generates a random column vector with 3 ascending numbers from 1 to 5, like [1;3;4] or [2;4;5]. arrayfun with the dummy argument x calls this 2 times in this example, because A consists of 2 sub-vectors of length 5. With the argument 'Uniform output' set to false, it generates a cell array of these random vectors, and cell2mat converts it to the (3 x 2)-matrix ind. The bsxfun call converts the values in the matrix ind to a matrix of linear indices into matrix B or A.
For your (900 x 25)-matrix, do
A = rand(900,25); % (900 x 25)
B = A'; % (25 x 900)
ind = cell2mat(arrayfun(#(x)sort(randperm(25,17))',1:size(B,2),'UniformOutput',false)); % (17 x 900), row indices into B
ind = bsxfun(#plus,ind,size(B,1)*(0:size(B,2)-1)); % (17 x 900), linear indices into B
C = B(ind); % (17 x 900), result
You can use reshape and randsample
rA = reshape( A, 5, [] ); % groups of 5 in a a row
A_result = rA( randsample( 5, 3, false ), : );
A_result = reshape( A_result, 1, [] );
You can pre-generate all possible picking patterns, and then randomly select one such pattern for each group. This approach is suitable for small group sizes, otherwise it may use a lot of memory.
A = 1:10; %// example data
n = 5; %// group size. Assumed to divide numel(A)
k = 3; %// how many to pick from each group
S = numel(A);
patterns = nchoosek(1:n, k); %// all possible picking patterns
K = size(patterns, 1);
p = randi(K, S/n, 1); %// generate random indices for patterns
pick = bsxfun(#plus, patterns(p,:).', (0:n:S-1)); %'// to linear indices
A_result = A(pick(:));

random sampling from a 3-way data cube

imagine a 2 x 2 x 2 three-way data cube:
data = [1 2; 3 4];
data(:,:,2) = [5 6; 7 8]
I wish to generate a row-column slice from this cube (i.e. a 2x2 matrix) in which each element of the slice is obtained by randomly sampling its 3-mode fiber (i.e. an nth mode fiber is a vector running along the nth mode/dimension/way. There are 4 3-mode fibers in this cube, one of them is f1 = [1 5], another one is f2 = [2 6] and so on). For example, one slice could turn out to be:
slice = [5 2; 3 4]
a different sampling might lead to the slice:
slice = [1 2; 7 8]
Is there a quick way to do this?
I tried using slice = datasample(data,1,3) but this function randomly picks a row-column slice from the cube (i.e. either, slice = [1 2; 3 4] or [5 6; 7 8]).
If I understand correctly, then it's quite simple actually. You have four 3-mode fibers and you wish to construct a 2x2 matrix where each element was sampled from the corresponding fiber.
So, you need to sample 4 times (one for each fiber) one element out of 2 (each fiber has two elements):
>> [h w fiberSize] = size(data); % make the solution more general
>> fIdx = randsample( fiberSize, h*w ); % sample with replacements
After sampling we construct the slice, for simplicity I'll "flatten" the 3D data into a 2D matrix
>> fData = reshape( data, [], fiberSize );
>> slice = fData( sub2ind( [h*w fiberSize], 1:(h*w), fIdx ) );
>> slice = reshape( slice, [h w] ); % shape the slice
The following solution is valid for any cube size. No toolbox required.
N = size(data,1); %//length of side of cube
r = randi(N,1,N^2)-1; %//this is the random sampling
data_permuted = permute(data,[3 1 2]); %//permute so that sampling is along first dim
slice = data_permuted((1:N:N^3)+r); %//sample using linear indexing
slice = reshape(slice.',N,N); %//reshape into a matrix
Try this solution for any nmode (e.g. nmode=3 is the 3-mode):
data = cat(3,[1 2; 3 4],[5 6; 7 8]);
nmode = 3;
Dn = size(data,nmode);
modeSampleInds = randi(Dn,1,numel(data)/Dn); % here is the random sample
dp = reshape(permute(data,[nmode setdiff(1:ndims(data),nmode)]), Dn, []);
outSize = size(data); outSize(nmode)=1;
slice = reshape(dp(sub2ind(size(dp),modeSampleInds,1:size(dp,2))),outSize)
Note that this does not require the Statistics Toolbox. It is also completely generalized for any matrix size, number of dimension, and "N-mode fiber".
I'd be glad to explain each line if needed.