How to grow a labeled feature in an scipy ndimage - scipy

Is there a way to grow a labeled feature in an ndimage? Ideally with a way to specify how the growth should happen by using a structure.
I'm using scipy.ndimage.label() for feature detection in an n-dimensional binary image like so:
import numpy as np
from scipy import ndimage
a = np.full((8, 8), 1)
a[2, :] = a[5, :] = 0
a[:, 2] = a[:, 5] = 0
print(a)
>>>>
[[1 1 0 1 1 0 1 1]
[1 1 0 1 1 0 1 1]
[0 0 0 0 0 0 0 0]
[1 1 0 1 1 0 1 1]
[1 1 0 1 1 0 1 1]
[0 0 0 0 0 0 0 0]
[1 1 0 1 1 0 1 1]
[1 1 0 1 1 0 1 1]]
s = [
[0, 1, 0],
[1, 1, 1],
[0, 1, 0],
]
labeled, num = ndimage.label(a, structure=s)
print(labeled)
>>>>
[[1 1 0 2 2 0 3 3]
[1 1 0 2 2 0 3 3]
[0 0 0 0 0 0 0 0]
[4 4 0 5 5 0 6 6]
[4 4 0 5 5 0 6 6]
[0 0 0 0 0 0 0 0]
[7 7 0 8 8 0 9 9]
[7 7 0 8 8 0 9 9]]
Now let's say I want to expand the feature with the label 2.
Here is some pseudo code and output to illustrate what I want the results to be:
# pseudo code
grown = ndimage.grow(labeled, label=2, structure=s)
print(grown)
[[1 1 2 2 2 2 3 3]
[1 1 2 2 2 2 3 3]
[0 0 0 2 2 0 0 0]
[4 4 0 5 5 0 6 6]
[4 4 0 5 5 0 6 6]
[0 0 0 0 0 0 0 0]
[7 7 0 8 8 0 9 9]
[7 7 0 8 8 0 9 9]]
Any help would be greatly appreciated! It seems like something scipy should be able to accomplish, but I'm still new to the module and I'm having trouble searching the docs for what I want to do.

I found that scipy.ndimage.binary_dilation does what I need when I filter the array first:
filtered = np.where(labeled == 2, 1, 0)
grown = ndimage.binary_dilation(filtered, structure=s).astype(np.int)
print(grown)
>>>>
[[0 0 1 1 1 1 0 0]
[0 0 1 1 1 1 0 0]
[0 0 0 1 1 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]]
The output of the pseudo code can be achieved by combing grown and labeled like so:
print(np.where(grown, 2, labeled))
>>>>
[[1 1 2 2 2 2 3 3]
[1 1 2 2 2 2 3 3]
[0 0 0 2 2 0 0 0]
[4 4 0 5 5 0 6 6]
[4 4 0 5 5 0 6 6]
[0 0 0 0 0 0 0 0]
[7 7 0 8 8 0 9 9]
[7 7 0 8 8 0 9 9]]

Related

Matlab Find number of consecutive zeros

I want to find the lengths of series zeros in a matrix
A = [0 0 0 3 1 4 6 0 9 1 0 0 0 0 0 0 1 5 2 1 1;2 3 1 0 0 4 6 0 0 0 2 3 8 6 0 0 0 0 0 1 1]
I need result gives seriesZeros = [3 1 6;2 3 5] and also [rows,cols] from series of zeros value
thank you very much...
You could do this as follows:
A = [0 0 0 3 1 4 6 0 9 1 0 0 0 0 0 0 1 5 2 1 1;
2 3 1 0 0 4 6 0 0 0 2 3 8 6 0 0 0 0 0 1 1];
[N,~] = size(A);
% pad A==0 with zeros, and calculate diff for each row
A2 = diff([zeros(N,1) A==0 zeros(N,1)],[],2);
out_mtx = [];
for row_i = 1:size(A2,1)
row = A2(row_i, :);
zero_lengths = find(row == -1) - find(row == 1);
out_mtx(end+1,:) = zero_lengths;
end
out_mtx
Which gives
out_mtx =
3 1 6
2 3 5

Finding the non-intersecting rows in a matrix

I want to find the non-intersecting rows in a large matrix. As an example:
A=[1 5 3; 3 4 5; 7 9 10;4 5 6;11 2 8; 3 5 10]
In this matrix, the non-intersecting rows are: [1 5 3], [11 2 8] and [7 9 10]. How can I program this in Matlab in a fast way?
If I may bsxfun -
M = squeeze(any(bsxfun(#eq,A,permute(unique(A),[3 2 1])),2))
[~,row_idx] = max(M,[],1)
out = A(sum(M,2).' == histc(row_idx,1:size(A,1)),:)
Sample step-by-step run -
A =
1 5 3
3 4 5
7 9 10
4 5 6
11 2 8
3 5 10
M =
1 0 1 0 1 0 0 0 0 0 0
0 0 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0 1 1 0
0 0 0 1 1 1 0 0 0 0 0
0 1 0 0 0 0 0 1 0 0 1
0 0 1 0 1 0 0 0 0 1 0
row_idx =
1 5 1 2 1 4 3 5 3 3 5
out =
1 5 3
7 9 10
11 2 8
You can look for rows that adding them to union of previous rows increases the number of elements in the union by the number of columns (i.e. all elements in that row are new):
B = [];
C = zeros(1,size(A,1));
for k=1:size(A,1),
B1 = union(B, A(k,:));
C(k) = numel(B1)-numel(B);
B=B1;
end
result = A(C==size(A,2),:);

Get all vector elements between different indices

I have one vector of starting points (start) and another vector for end points (end):
start=[1 0 0 0 1 0 0 0 1 0 0 0]
end= [0 0 1 0 0 0 1 0 0 0 1 0]
I want a third vector A all the numbers between each start and endpoint.
A = [1 2 3 4 5 6 7 8 9 1 2 3]
so the result for this example could look like this:
A_result= [1 2 3 5 6 7 9 1 2]
any ideas?
Without loop:
s=[1 0 0 0 1 0 0 0 1 0 0 0];
e=[0 0 1 0 0 0 1 0 0 0 1 0];
mask = cumsum(s)-cumsum([shift(e,1)])
# Will be [1 1 1 0 1 1 1 0 1 1 1 0]
A = [1 2 3 4 5 6 7 8 9 1 2 3];
A(find(mask))
# will be [1 2 3 5 6 7 9 1 2]
Or as The Minion pointed out, simply:
A(mask==1)
# will be [1 2 3 5 6 7 9 1 2]
Check the solution from seb it is way faster for large arrays.
This is a solution using a for-loop
d_start=[1 0 0 0 1 0 0 0 1 0 0 0];
d_end= [0 0 1 0 0 0 1 0 0 0 1 0];
A = [1 2 3 4 5 6 7 8 9 1 2 3];
starting_idx = find(d_start);
ending_idx = find(d_end);
A_result = [];
for l=1:numel(starting_idx)
A_result=horzcat(A_result, A(starting_idx(l):ending_idx(l)));
end
What I am doing is: 1. I get the index of the starting and end points (find). Then i predefine my result. Now i just have to loop over the number of entries in the starting points and add those values from that position to next ending to my result.
Edit: Timing test:
I used my solution as well as the solution from seb (modified to my variable names):
mask = cumsum(d_start)-cumsum([0 d_end(1:end-1)]);
B_res =A(find(mask));
as well as my comment to his solution:
mask = cumsum(d_start)-cumsum([0 d_end(1:end-1)]);
B_res =A(mask==1);
These were the timing results for A:[12888x1]double
t_forloop : 33 sec
t_seb_find: 0.31 sec
t_seb_logical: 0.1964 sec

How to get the unique slices of a matrix?

In matlab, if you have a matrix A you can find the matrix B containing all of the unique rows of A as follows:
B = unique(A,'rows');
What I have is a 3d matrix, with rows and columns as the first two dimensions, and one additional dimension ('slices').
How can I get the 3d matrix containing all the unique slices in a matrix A? Here's an example of the kind of functionality I want:
>> A % print out A
A(:,:,1) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
A(:,:,2) =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
A(:,:,3) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
A(:,:,4) =
0 0 0 1
0 0 1 0
0 1 0 0
1 0 0 0
>> unique(A,'slices'); % get unique slices
A(:,:,1) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
A(:,:,2) =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
A(:,:,3) =
0 0 0 1
0 0 1 0
0 1 0 0
1 0 0 0
I would begin by reshaping A so each slice becomes a row (with the reshape command). Then use unique(A, 'rows'). Finally, reshape the unique rows back to the same shape the slices.
For example:
% transforming so each row is a slice in row form
reshaped_A = reshape(A, [], size(A, 3))';
% getting unique rows
unique_rows = unique(reshaped_A, 'rows');
% reshaping back
unique_slices = reshape(unique_rows', size(A, 1), size(A, 2), []);
Or all in one line:
reshape(unique(reshape(A, [], size(A, 3))', 'rows')', size(A, 1), size(A, 2), [])
I haven't checked this above code so use with caution! But it should give the idea.
EDIT
Here it is working on your data (also fixed little bug in above code):
>> reshaped_A = reshape(A, [], size(A, 3))'
reshaped_A =
Columns 1 through 11
16 5 9 4 2 11 7 14 3 10 6
1 0 0 0 0 1 0 0 0 0 1
16 5 9 4 2 11 7 14 3 10 6
0 0 0 1 0 0 1 0 0 1 0
Columns 12 through 16
15 13 8 12 1
0 0 0 0 1
15 13 8 12 1
0 1 0 0 0
Each of these ^^ rows is one of the original slices
>> unique_rows = unique(reshaped_A, 'rows')
unique_rows =
Columns 1 through 11
0 0 0 1 0 0 1 0 0 1 0
1 0 0 0 0 1 0 0 0 0 1
16 5 9 4 2 11 7 14 3 10 6
Columns 12 through 16
0 1 0 0 0
0 0 0 0 1
15 13 8 12 1
These ^^ are the unique slices, but in the wrong shape.
>> unique_slices = reshape(unique_rows', size(A, 1), size(A, 2), [])
unique_slices(:,:,1) =
0 0 0 1
0 0 1 0
0 1 0 0
1 0 0 0
unique_slices(:,:,2) =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
unique_slices(:,:,3) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
A very simple and scalable solution would be:
A = cat(3, [16 2 3 13;5 11 10 8;9 7 6 12;4 14 15 1], [1 0 0 0;0 1 0 0;0 0 1 0;0 0 0 1], [16 2 3 13;5 11 10 8;9 7 6 12;4 14 15 1], [0 0 0 1;0 0 1 0;0 1 0 0;1 0 0 0])
[n,m,p] = size(A);
a = reshape(A,n,[],1);
b = reshape(a(:),n*m,[])';
c = unique(b,'rows', 'stable')'; %If the 'stable' option is supported by your version.
%If the 'stable' option is not supported, but it's still required, use the index vector option, as required.
%i.e.,
%[c,I,J] = unique(b,'rows');
unique_A = reshape(c,n,m,[])
Results:
A(:,:,1) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
A(:,:,2) =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
A(:,:,3) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
A(:,:,4) =
0 0 0 1
0 0 1 0
0 1 0 0
1 0 0 0
unique_A(:,:,1) =
0 0 0 1
0 0 1 0
0 1 0 0
1 0 0 0
unique_A(:,:,2) =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
unique_A(:,:,3) =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
Source: How to find unique pages in a 3d matrix?

Create a matrix with sequencial values padded with zeros (without loops)

I have two matrices aand b of 1 column and n rows.
These matrices represent min and max values.
From these I need to create a n by m matrix.
m is the max value of [a ; b]
The result must contain each value between a(:) and b(:), padded with zeros.
note: it's easy to do with a for loop, but I want to avoid loops.
Exemple :
Starting with these two matrices :
>> a = [3 ; 5 ; 2 ; 7 ; 4]
a =
3
5
2
7
4
>> b = [7 ; 7 ; 5 ; 8 ; 4]
b =
5
7
4
8
4
I want to end with this matrix :
result =
3 4 5 6 7 0 0 0
5 6 7 0 0 0 0 0
2 3 4 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
So far I have this :
>> result = zeros(size(a,1), max([a ; b]));
>> rows = [1:size(a,1)]
rows =
1 2 3 4 5
>> index = sub2ind(size(result), rows, b - a + 1)
>> result(index) = b
result =
0 0 0 0 7 0 0 0
0 0 7 0 0 0 0 0
0 0 0 5 0 0 0 0
0 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
>> result(:,1) = a
result =
3 0 0 0 7 0 0 0
5 0 7 0 0 0 0 0
2 0 0 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
I'd solve this with a simple loop, which is also probably the fastest solution
a = [3 ; 5 ; 2 ; 7 ; 4]
b = [7 ; 7 ; 5 ; 8 ; 4]
nSteps = b-a+1;
nRows = size(a,1);
result = zeros(nRows, max([a ; b]));
for iRow = 1:nRows
result(iRow,1:nSteps(iRow)) = a(iRow):b(iRow);
end
result =
3 4 5 6 7 0 0 0
5 6 7 0 0 0 0 0
2 3 4 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
In case you don't need readability, here's a non-loop solution:
result = ones(nRows, max([a ; b]));
result(:,1) = a;
result = cumsum(result,2);
result(bsxfun(#gt,result,b))=0
result =
3 4 5 6 7 0 0 0
5 6 7 0 0 0 0 0
2 3 4 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
Some problems aren't worth the effort of vectorising:
a = [3 ; 5 ; 2 ; 7 ; 4];
b = [7 ; 7 ; 5 ; 8 ; 4];
cols = max(max(a),max(b))
result = zeros(length(a),cols)
for i = 1:length(a)
A = a(i):b(i)
result(i,:) = padarray(A, [0 cols-length(A)], 0, 'post');
end