Find indices of neighbors in masked region - matlab

I have this piece of code:
[I, J] = find(mask == 1);
for k = 1 : numel(I)
i = I(k);
j = J(k);
neighbor_ind = [i, j - 1;
i, j + 1;
i - 1, j;
i + 1, j];
end
Now I would like to find all indices s such that [I(s), J(s)] is equal to one of the rows in neighbor_ind. The neighbor indices for which this is not possible should be ignored.
How can I achieve this?
EDIT: Here is a small example.
Suppose we have the mask
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0
(it does not have to be rectangular)
[I, J] = find(mask == 1) will give I = [2, 3, 2, 3] and J = [2, 2, 3, 3].
Now lets set i = 2, j = 2.
There are two neighbors inside the mask, and two outside. I want to know where in I, J I need to go look to find these neighbors. In this example, the solution would be I(3), J(3) for the right neighbor, and I(2), J(2) for the bottom neighbor.

You can use nchoosek to generate indices of neighbors.
mask = [...
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0];
[R , C] = find(mask==1);
n = numel(R);
idx_pix = (n:-1:1).';
idx_neighbors = nchoosek(1:n,n-1);
that results:
idx_pix =
4
3
2
1
idx_neighbors =
1 2 3
1 2 4
1 3 4
2 3 4
So in your example (n == 4) neighbors of 4th pixel are [1 2 3]
in the other words :
neighbors of `idx_pix(1)` are `idx_neighbors(1,:)` : 4 ->> 1 2 3
neighbors of `idx_pix(2)` are `idx_neighbors(2,:)` : 3 ->> 1 2 4
neighbors of `idx_pix(3)` are `idx_neighbors(3,:)` : 2 ->> 1 3 4
neighbors of `idx_pix(4)` are `idx_neighbors(4,:)` : 1 ->> 2 3 4
or
neighbors of R(idx_pix(1)) are R(idx_neighbors(1,:))
...
...
...
note: It is more efficient to use linear indices instead of rows and columns so you can use this signature: IDX = find(mask==1);

Related

How to leave only the elements that appear once per specific row x column per page?

I have a 3D array. I need to remove any elements that are in the same row, column position but on the next page (3rd dimension), and only use the first occurrence at that position. So if all pages were to multiply the result would be 0.
Since the 3D array may be of any size, I can't hard code solutions like isMember. I also can't use unique because elements can be the same, just not share the same position.
For example, input:
A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 1 1 0];
A(:,:,3) = [ 0 1 0];
the desired output is:
A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 0 1 0];
A(:,:,3) = [ 0 0 0];
How can I accomplish this?
Not the most elegant, but at least it works.
A(:,:,1) = [ 1 0 2 ];
A(:,:,2) = [ 1 1 0 ];
A(:,:,3) = [ 0 1 0 ];
for ii = 1:size(A,1)
for jj = 1:size(A,2)
unique_el = unique(A(ii, jj, :)); % Grab unique elements
for kk = 1:numel(unique_el)
idx = find(A(ii,jj,:) == kk); % Contains indices of unique elements
if numel(idx) > 1 % If an element occurs more than once
A(ii, jj, idx(2:end)) = 0; % Set to 0
end
end
end
end
A
A(:,:,1) =
1 0 2
A(:,:,2) =
0 1 0
A(:,:,3) =
0 0 0
I loop over the first two dimensions of A (rows and columns), find any unique elements which occur on a certain row and column location through the third dimensions (pages). Then set all occurrences of a unique element after the first to 0.
Given a more elaborate 3D matrix this still works:
A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4];
A(:,:,1) =
1 0 2 0
2 1 3 0
A(:,:,2) =
0 1 0 0
0 2 1 0
A(:,:,3) =
0 0 1 3
1 0 2 4
If you want the first non-zero element and discard any element occurring afterwards, simply get rid of the unique() call:
A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4];
for ii = 1:size(A,1)
for jj = 1:size(A,2)
idx = find(A(ii,jj,:) ~= 0); % Contains indices of nonzero elements
if numel(idx) > 1 % If more than one element
A(ii, jj, idx(2:end)) = 0; % Set rest to 0
end
end
end
A(:,:,1) =
1 0 2 0
2 1 3 0
A(:,:,2) =
0 1 0 0
0 0 0 0
A(:,:,3) =
0 0 0 3
0 0 0 4
My solution assumes, that, for a given "position", EVERY value after the first occurence of any value is cleared. Some of the MATLAB regulars around here had some discussions on that, from there comes the "extended" example as also used in Adriaan's answer.
I use permute and reshape to rearrange the input, so that we have all "positions" as "page" columns in a 2D array. Then, we can use arrayfun to find the proper indices of the first occurence of a non-zero value (kudos to LuisMendo's answer here). Using this approach again, we find all indices to be set to 0.
Let's have a look at the following code:
A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4]
[m, n, o] = size(A);
B = reshape(permute(A, [3 1 2]), o, m*n);
idx = arrayfun(#(x) find(B(:, x), 1, 'first'), 1:size(B, 2));
idx = arrayfun(#(x) find(B(idx(x)+1:end, x)) + idx(x) + 3*(x-1), 1:size(B, 2), 'UniformOutput', false);
idx = vertcat(idx{:});
B(idx) = 0;
B = permute(reshape(B, o, m , n), [2, 3, 1])
Definitely, it makes sense to have a look at the intermediate outputs to understand the functioning of my approach. (Of course, some lines can be combined, but I wanted to keep a certain degree of readability.)
And, here's the output:
A =
ans(:,:,1) =
1 0 2 0
2 1 3 0
ans(:,:,2) =
1 1 0 0
2 2 1 0
ans(:,:,3) =
0 1 1 3
1 2 2 4
B =
ans(:,:,1) =
1 0 2 0
2 1 3 0
ans(:,:,2) =
0 1 0 0
0 0 0 0
ans(:,:,3) =
0 0 0 3
0 0 0 4
As you can see, it's identical to Adriaan's second version.
Hope that helps!
A vectorized solution. You can use the second output of max to find the index of the first occurence of a nonzero value along the third dimension and then use sub2ind to convert that to linear index.
A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 1 1 0];
A(:,:,3) = [ 0 1 0];
[~, mi] =max(logical(A) ,[], 3);
sz=size(A) ;
[x, y] =ndgrid(1:sz(1),1:sz(2));
idx=sub2ind( sz, x,y,mi);
result=zeros(sz) ;
result(idx) =A(idx);

Fastest way of generating a logical matrix by given row indices of true values?

What is the most efficient way of generating
>> A
A =
0 1 1
1 1 0
1 0 1
0 0 0
with
>> B = [2 3; 1 2; 1 3]
B =
2 3
1 2
1 3
in MATLAB?
E.g., B(1, :), which is [2 3], means that A(2, 1) and A(3, 1) are true.
My attempt still requires one for loop, iterating through B's row. Is there a loop-free or more efficient way of doing this?
This is one way of many, though sub2ind is the dedicated function for that:
%// given row indices
B = [2 3; 1 2; 1 3]
%// size of row index matrix
[n,m] = size(B)
%// size of output matrix
[N,M] = deal( max(B(:)), n)
%// preallocation of output matrix
A = zeros(N,M)
%// get col indices to given row indices
cols = bsxfun(#times, ones(n,m),(1:n).')
%// set values
A( sub2ind([N,M],B,cols) ) = 1
A =
0 1 1
1 1 0
1 0 1
If you want a logical matrix, change the following to lines
A = false(N,M)
A( sub2ind([N,M],B,cols) ) = true
Alternative solution
%// given row indices
B = [2 3; 1 2; 1 3];
%// number if rows
r = 4; %// e.g. = max(B(:))
%// number if cols
c = 3; %// size(B,1)
%// preallocation of output matrix
A = zeros(r,c);
%// set values
A( bsxfun(#plus, B.', 0:r:(r*(c-1))) ) = 1;
Here's a way, using the sparse function:
A = full(sparse(cumsum(ones(size(B))), B, 1));
This gives
A =
0 1 1
1 1 0
1 0 1
If you need a predefined number of rows in the output, say r (in your example r = 4):
A = full(sparse(cumsum(ones(size(B))), B, 1, 4, size(B,1)));
which gives
A =
0 1 1
1 1 0
1 0 1
0 0 0
You can equivalently use the accumarrray function:
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1);
gives
A =
0 1 1
1 1 0
1 0 1
Or with a predefined number of rows, r = 4,
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1, [r size(B,1)]);
gives
A =
0 1 1
1 1 0
1 0 1
0 0 0

How to generate a customized checker board matrix as fast as possible?

I need a function that creates a checker board matrix with M rows and N columns of P*Q rectangles. I modified the third solution from here to get that:
function [I] = mycheckerboard(M, N, P, Q)
nr = M*P;
nc = N*Q;
i = floor(mod((0:(nc-1))/Q, 2));
j = floor(mod((0:(nr-1))/P, 2))';
r = repmat(i, [nr 1]);
c = repmat(j, [1 nc]);
I = xor(r, c);
it works with no problem:
I=mycheckerboard(2, 3, 4, 3)
I =
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
0 0 0 1 1 1 0 0 0
1 1 1 0 0 0 1 1 1
1 1 1 0 0 0 1 1 1
1 1 1 0 0 0 1 1 1
1 1 1 0 0 0 1 1 1
But it's not fast enough since there are lots of calls of this function in a single run. Is there a faster way to get the result? How can I remove floating point divisions and/or calls of the floor function?
Your code is fairly fast for small matrices, but becomes less so as the dimensions get larger. Here's a one-liner using bsxfun and imresize (requires Image Processing toolbox that most have):
m = 2;
n = 3;
p = 4;
q = 3;
I = imresize(bsxfun(#xor, mod(1:m, 2).', mod(1:n, 2)), [p*m q*n], 'nearest')
Or, inspired by #AndrasDeak's use of kron, this is faster with R2015b:
I = kron(bsxfun(#xor, mod(1:m, 2).', mod(1:n, 2)), ones(p, q))
For a small bit more speed, the underlying code for kron can be simplified by taking advantage of the structure of the problem:
A = bsxfun(#xor, mod(1:m, 2).', mod(1:n, 2));
A = permute(A, [3 1 4 2]);
B = ones(q, 1, p);
I = reshape(bsxfun(#times, A, B), [m*n p*q]);
or as one (long) line:
I = reshape(bsxfun(#times, permute(bsxfun(#xor, mod(1:m, 2).', mod(1:n, 2)), [3 1 4 2]), ones(q, 1, p)), [m*n p*q]);
I suggest first creating a binary matrix for the checkerboard's fields, then using the built-in kron to blow it up to the necessary size:
M = 2;
N = 3;
P = 4;
Q = 3;
[iM,iN] = meshgrid(1:M,1:N);
A = zeros(M,N);
A(mod(iM.'+iN.',2)==1) = 1;
board = kron(A,ones(P,Q))

Edge Coordinates of a Cube

I have a matrix corresponding to 8 vortex of a cube,
CubeVortex = [3 3 0;
0 3 0;
0 3 3;
3 3 3;
0 0 3;
3 0 3;
3 0 0;
0 0 0];
Now I want to get the coordinates of all the edges divided in 3, like,
As you can see, there will be 12x2 = 24 coordinates.
It would be a little hard to write them.
Is there a way to calculate them from CubeVortex?
One way to do this:
Cube = [
3 3 0;
0 3 0;
0 3 3;
3 3 3;
0 0 3;
3 0 3;
3 0 0;
0 0 0];
% find edges by looking for all combinations of points on cube that
% differ by only one coordinate
sections_per_edge = 3;
weights = ((1:sections_per_edge-1) / sections_per_edge).';
edges = []; % indices into Cube
points = [];
n = size(Cube, 1);
for i = 1:n-1
pointA = Cube(i, :);
for j = i+1:n
pointB = Cube(j, :);
if nnz(pointA - pointB) == 1
edges = [edges; i, j];
% find points along edge as weighted average of point A and B
points = [points; weights * pointA + (1 - weights) * pointB];
end
end
end
% plot corners
plot3(Cube(:,1), Cube(:,2), Cube(:,3), '.r', 'markersize', 20)
hold on
% plot points along edges
plot3(points(:,1), points(:,2), points(:,3), '.b', 'markersize', 20)
% draw edges
line([Cube(edges(:,1), 1), Cube(edges(:,2), 1)].', ...
[Cube(edges(:,1), 2), Cube(edges(:,2), 2)].', ...
[Cube(edges(:,1), 3), Cube(edges(:,2), 3)].', 'color', 'k')
axis([-1,4,-1,4])
Result:
Increasing sections_per_edge to 10, you get
One approach could be this -
n = 3; %// number of IDs
m = 3; %// number of columns
combs = dec2base(0:(n+1)^m-1,n+1,m)-'0' %// form repeated combinations
out = c1(sum(ismember(combs,[1 2]),2)==1,:) %// combinations for intermediate points
You can make this generic for a N-point case and more efficient one, with this -
N = 3;
[x,y,z] = ndgrid(0:N,0:N,0:N)
combs = [z(:) y(:) x(:)]
out = combs(sum(combs~=0 & combs~=N,2)==1,:)
Thus, for your 3-point (0 to 3 that is) case, you would have -
out =
0 0 1
0 0 2
0 1 0
0 1 3
0 2 0
0 2 3
0 3 1
0 3 2
1 0 0
1 0 3
1 3 0
1 3 3
2 0 0
2 0 3
2 3 0
2 3 3
3 0 1
3 0 2
3 1 0
3 1 3
3 2 0
3 2 3
3 3 1
3 3 2
This should do it:
NewVortex=[];
for i=1:3
NewVortex=[CubeVortex*i/3;NewVortex];
end
NewVortex

How to count number of 1's in the matrix

I have one matrix like-
A=[1 1 3 0 0;
1 2 2 0 0;
1 1 1 2 0;
1 1 1 1 1];
From these "A" i need to count the number of 1"s of each row and after that i want to give the condition that after scanning each row of 'A' if the number of 1's >=3 then it take that. It means my final result will be
A= [1 1 1 2 0;
1 1 1 1 1].
How can I do this. Matlab experts need your valuable suggestion.
>> A(sum(A == 1, 2) >= 3, :)
ans =
1 1 1 2 0
1 1 1 1 1
Here, sum(A == 1, 2) counts the number of ones in each row, and A(... >= 3, :) selects the rows where the count is at least 3.
A=[1 1 3 0 0;...
1 2 2 0 0;...
1 1 1 2 0;...
1 1 1 1 1]
accept = sum((A == 1)')
i = 1;k = 1;
while i <= length(A(:,1))
if accept(k) < 3
A(i,:) = [];
i = i - 1;
end
i = i + 1;
k = k + 1;
end
A