How to identify multiple intersecting polygons in MATLAB? - matlab

I'm trying to identify overlapping/intersecting polygons. The techniques I have found only compare two polygons at a time. I have tens-of-thousands of cells in a dataset, and in each one there are 2-20 polygons, each described by x-y coordinates. I want to find the overlapping polygons in each cell. Looping between every pair to check for an intersection is very slow, so I want to ask...
Is there a way to compare all polygons at the same time and extract the IDs of those that are overlapping?
Here is a simple example of a single entry from the dataset:
shapes = cell(4,2);
shapes{1,1} = 'poly1';
shapes{2,1} = 'poly2';
shapes{3,1} = 'poly3';
shapes{4,1} = 'poly4';
shapes{1,2} = [1, 3, 3; 1, 1, 3]';
shapes{2,2} = [2, 4, 2; 2, 2, 5]';
shapes{3,2} = [4, 5, 5, 4; 3, 3, 5, 5]';
shapes{4,2} = [1, 3, 3, 1; 4, 4, 6, 6]';
This example contains these 4 polygons:
This plot was made with separate 'polyshape' objects, but that doesn't mean I need to use this kind of object in the solution.
The output I would like is a record of each overlapping pair:
result =
2×2 cell array
{'poly1'} {'poly2'}
{'poly2'} {'poly4'}
P.S. My current method is to loop through each pair and use the poly2mask function on each polygon of the pair. Then use the & operator to add the binary masks together. This produces a logical array of 1's where there is any overlap.
P.P.S. The actual polygons I am looking at are all annular sectors, therefore they are not all convex

Here is a solution that makes use of 'polyshape' vectors and avoids making all those pairwise comparisons in extra loops (although I don't know how the 'overlap' function works).
% Set up empty vector to hold the different shapes
polyvec = [];
% Loop all shapes and combine into polyshape vector
for ii = 1 : size(shapes, 1)
poly = polyshape(shapes{ii,2}(:,1), shapes{ii,2}(:,2));
% When you combine polyshape objects together the you get
% a vector that is of the polyshape object type
polyvec = [polyvec, poly];
end
% Use the overlap function to compute a symmetric binary matrix
% of which polygons in the polygon vector overlap.
interMatSym = overlaps(polyvec);
% I only need the upper triangle of the symmetric interaction
% matrix and all polygons overlap with themselves so use 'triu'
interMat = triu(overlaps(polyvec), 1);
% Find the coordinates of the overlap in the interaction matrix
[x, y] = find(interMat);
% Save the result
result = [shapes(x,1), shapes(y,1)];
result =
2×2 cell array
{'poly1'} {'poly2'}
{'poly2'} {'poly4'}
If there is a way to create a polyshpae vector any more efficiently then I'd love to know!

Related

how to rotate images in a batch separately in pytorch

I am randomly rotating 3D images in pytorch using torch.rot90 but this rotates all the images in the batch in the same way. I would like to find a differentiable way to randomly rotate each image in a different axis.
here is the code which rotates each image to the same orientation:
#x = next batch
k = torch.randint(0, 4, (1,)).item()
dims = [0,0]
dims[0] = dims[1] = torch.randint(2, 5, (1,))
while dims[0] == dims[1]:#this makes sure the two axes aren't the same
dims[1] = torch.randint(2, 5, (1,))
x = torch.rot90(x, k, dims)
# x is now a batch of 3D images that have all been rotated in the same random orientation
You could split the data in the batch randomly into 3 subsets, and apply each dimensional rotation respectively.
Let me expand on iacob's answer. Firstly, let me go over the parameters of rot90 function. Other than the input tensor, it expects k and dims where k is the number of rotations to be done, and dims is a list or tuple containing two dimensions on how the tensor to be rotated. If a tensor is 4D for example, dims could be [0, 3] or (1,2) or [2,3] etc. They have to be valid axes and it should contain two numbers. You don't really need to create tensors for this parameter or k. It is important to note that, depending on the given dims, output shape can drastically change:
x = torch.rand(15, 3, 4,6)
y1 = torch.rot90(x[0:5], 1, [1,3])
y2 = torch.rot90(x[5:10], 1, [1,2])
y3 = torch.rot90(x[10:15], 1, [2,3])
print(y1.shape) # torch.Size([5, 6, 4, 3])
print(y2.shape) # torch.Size([5, 4, 3, 6])
print(y3.shape) # torch.Size([5, 3, 6, 4])
Similar to iacob's answer, here we apply 3 different rotations to slices of the input. Note that how the output dimensions are all different, due to nature of rotations over different dimensions. You can't really join these results into one tensor, unless you have a really specific input size, for example Batch x 10 x 10 x 10 where rotating over combinations of 1,2,3 axes will always return same dimensions. You can however use each of these different sized output separately as inputs to different modules, layers etc.
I personally can't think of a use case where your random axes rotation can be used. If you can elaborate on why you are trying to do this, I can try to give some better solutions.

Calculating the barycenter of multiple triangles

I want to calculate each individual barycenter (centroid) of a list of triangles. Thus far I've managed to write this much :
function Triangle_Source_Centroid(V_Epoch0, F_Epoch0)
for i = 1:length(F_Epoch0)
Centroid_X = F_Epoch0(V_Epoch0(:,1),1) + F_Epoch0(V_Epoch0(:,1),2) + F_Epoch0(V_Epoch0(:,1),3);
Centroid_Y = F_Epoch0(V_Epoch0(:,2),1) + F_Epoch0(V_Epoch0(:,2),2) + F_Epoch0(V_Epoch0(:,2),3);
Centroid_Z = F_Epoch0(V_Epoch0(:,3),1) + F_Epoch0(V_Epoch0(:,3),2) + F_Epoch0(V_Epoch0(:,3),3);
Triangle_Centroid = [Centroid_X; Centroid_Y; Centroid_Z];
end
end
it doesn't work, and only gives me an error message:
Subscript indices must either be real positive integers or logicals.
Given how the variables are named, I'm guessing that V_Epoch0 is an N-by-3 matrix of vertices (X, Y, and Z for the columns) and F_Epoch0 is an M-by-3 matrix of face indices (each row is a set of row indices into V_Epoch0 showing which points make each triangle). Assuming this is right...
You can actually avoid using a for loop in this case by making use of matrix indexing. For example, to get the X coordinates for every point in F_Epoch0, you can do this:
allX = reshape(V_Epoch0(F_Epoch0, 1), size(F_Epoch0));
Then you can take the mean across the columns to get the average X coordinate for each triangular face:
meanX = mean(allX, 2);
And meanX is now a M-by-1 column vector. You can then repeat this for Y and Z coordinates:
allY = reshape(V_Epoch0(F_Epoch0, 2), size(F_Epoch0));
meanY = mean(allY, 2);
allZ = reshape(V_Epoch0(F_Epoch0, 3), size(F_Epoch0));
meanZ = mean(allZ, 2);
centroids = [meanX meanY meanZ];
And centroids is an M-by-3 matrix of triangle centroid coordinates.
Bonus:
All of the above can actually be done with just this one line:
centroids = squeeze(mean(reshape(V_Epoch0(F_Epoch0, :), [size(F_Epoch0, 1) 3 3]), 2));
Check out the documentation for multidimensional arrays to learn more about how this works.

Select values with a matrix of indices in MATLAB?

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.

Index position of randsample in Matlab

I'm using the randsample function of Matlab to sample a data point in the following way:
points = randi(100,1,10);
weighting_vector = rand(1,10);
normalized_weighting_vector = weighting_vector ./ sum(weighting_vector);
point = randsample(points,1,'true',normalized_weighting_vector);
How can I get the index of the selected point?
For example if points = [1,2,2,3,4,4,4,8,9,3] and point = 4, I would like to know the index position of the chosen value, which can either be 5, 6 or 7.
Rather than using randsample on your data, randomly sample the indices and then convert these indices to their corresponding values in points.
points = [1,2,2,3,4,4,4,8,9,3];
% Randomly choose N indices from the possible index values for points
index = randsample(1:numel(points), 1, true);
% Get the point corresponding to these indices
point = points(index)

How to plot a 3D object from line segments in Matlab

I have a problem with fast plotting of a simple 3D model which I read from a .dxf file. The hole object is defined by points and the lines between them.
I have a matrix with coordinates. Every row is a unique point and every column is one coordinate.
Then I have an index matrix of size Nx2, where N is the number of rows in the model and on every row there are 2 points indexed from the coordinate matrix which should be connected by a line.
So the structure of the data is very similar to that of the data after triangulation and I need a function similar to trimesh or trisurf, though not for triangles, but for lines.
I can do that by letting a for loop cycle through the index matrix and plot every row separately, but it is very slow as compared built-in functions like trimesh.
Brief example:
%Coordinate matrix
NODES=[
-12.76747 -13.63075 -6.41142
-12.76747 -8.63075 -6.41142
-8.76747 -13.63075 -6.41142
-16.76747 -13.63075 -6.41142
-11.76747 -7.63075 -2.41142
];
%index matrix
LINES=[
1 2
3 4
1 4
3 5
1 5
];
%The slow way of creating the figure
figure(1)
hold on
for k=1:length(LINES)
plot3(NODES(LINES(k,:), 1), NODES(LINES(k,:), 2), NODES(LINES(k,:), 3), '.-')
end
view(20, 20)
hold off
I want to find a better and faster way to produce this figure
I think the code is self-explanatory (it assumes that NODES and LINES are already defined):
%'Calculated: edge coordinates and line specs'
TI = transpose(LINES);
DI = 2*ones(1,size(TI,2));
X = mat2cell(NODES(TI,1), DI);
Y = mat2cell(NODES(TI,2), DI);
Z = mat2cell(NODES(TI,3), DI);
L = repmat({'.-'}, size(X));
%'Output: plot'
ARGS = transpose([X,Y,Z,L]);
plot3(ARGS{:});