I'm trying to build a function in MATLAB, in which you input a segment (defined by two points) and a polygon (4-sides) by indicating on an array its vertices.
I have the following code:
function intersection = intersectSegmentPolygon (s, p)
% Create a vector with X coords of vertices and same for Y coords.
xv = [p(1,1) p(2,1) p(3,1) p(4,1)];
yv = [p(1,2) p(2,2) p(3,2) p(4,2)];
% Read the segment
x = [s.A(1) s.B(1)];
y = [s.A(2) s.B(2)];
[in,on] = inpolygon(x,y,xv,yv);
% Return vectors containing the coords of the intersecting points
intersection = [x(on), y(on)];
I am intersted in obtaining the points at the position on (the intersecting points) but, obviously the function is only checking the points A and B (the initial and final coordinates of the segment), what can I do in order to check all the points contained on the segment AB? Thank you.
Use the parametric equation of the line segment, P = (1-t) A + t B, with 0<=t<=1.
Find the intersections between the polygon edges and the line of support of the segment, expressing the position of the intersection in terms of t (momentarily ignore the constraint on t).
You will find 0 or 2 intersections, not more, hence 0 or 2 values of t, forming an interval. The solution is given by the intersection of this interval with the interval [0,1], an elementary 1D problem.
Related
This question is from my last post,
But I will just focus on a particular part,
If I have a 3d matrix M, 256*256*124,and a plane defined by 3 points that intersect the 3d matrix, A,B,C, how to find the distance of all points in M to the plane defined by A,B,C
Here is the suggestion I got,
[X Y Z] = meshgrid(1:256,1:256,1:124)
A = [X(:)';Y(:)';Z(:)'];
n = cross([coor_A-coor_B],[coor_B-coor_C]); %The norm vector of the plane
d = norm(mean([coor_A,coor_B,coor_C]),2); %The distance from origin to the plane
According to Hesse normal form,
L = ((n*A)-d); %The distance from all points in M to the plane
But all the values in L are huge, which indicates that no points are found on the intersection plane.
Can anyone tell me what's wrong?
You missed one line on the Wikipedia page
where
So add this line
n0 = n / norm(n);
and change the final line to
L = ((n0*A)-d);
I have a binary image of a human. In MATLAB, boundary points and the center of the image are also defined, and they are two column matrices. Now I want to draw lines from the center to the boundary points so that I can obtain all points of intersection between these lines and the boundary of the image. How can I do that? Here is the code I have so far:
The code that is written just to get the one intersection point if anyone can help please
clear all
close all
clc
BW = im2bw(imread('C:\fyc-90_1-100.png'));
BW = imfill(BW,'holes');
[Bw m n]=preprocess(BW);
[bord sk pr_sk]=border_skeleton(BW);
boundry=bord;
L = bwlabel(BW);
s = regionprops(L, 'centroid');
centroids = cat(1, s.Centroid);
Step #1 - Generating your line
The first thing you need to do is figure out how to draw your line. To make this simple, let's assume that the centre of the human body is stored as an array of cen = [x1 y1] as you have said. Now, supposing you click anywhere in your image, you get another point linePt = [x2 y2]. Let's assume that both the x and y co-ordinates are the horizontal and vertical components respectively. We can find the slope and intercept of this line, then create points between these two points parameterized by the slope and intercept to generate your line points. One thing I will point out is that if we draw a slope with a vertical line, by definition the slope would be infinity. As such, we need to place in a check to see if we have this situation. If we do, we assume that all of the x points are the same, while y varies. Once you have your slope and intercept, simply create points in between the line. You'll have to choose how many points you want along this line yourself as I have no idea about the resolution of your image, nor how big you want the line to be. We will then store this into a variable called linePoints where the first column consists of x values and the second column consists of y values. In other words:
In other words, do this:
%// Define number of points
numPoints = 1000;
%// Recall the equation of the line: y = mx + b, m = (y2-y1)/(x2-x1)
if abs(cen(1) - linePt(1)) < 0.00001 %// If x points are close
yPts = linspace(cen(2), linePt(2), numPoints); %// y points are the ones that vary
xPts = cen(1)*ones(numPoints, 1); %//Make x points the same to make vertical line
else %// Normal case
slp = (cen(2) - linePt(2)) / cen(1) - linePt(1)); %// Solve for slope (m)
icept = cen(2) - slp*cen(1); %// Solve for intercept (b)
xPts = linspace(cen(1), linePt(1), numPoints); %// Vary the x points
yPts = slp*xPts + icept; %// Solve for the y points
end
linePoints = [xPts(:) yPts(:)]; %// Create point matrix
Step #2 - Finding points of intersection
Supposing you have a 2D array of points [x y] where x denotes the horizontal co-ordinates and y denotes the vertical co-ordinates of your line. We can simply find the distance between all of these points in your boundary with all of your points on the line. Should any of the points be under a certain threshold (like 0.0001 for example), then this indicates an intersection. Note that due to the crux of floating point data, we can't check to see if the distance is 0 due to the step size in between each discrete point in your data.
I'm also going to assume border_skeleton returns points of the same format. This method works without specifying what the centroid is. As such, I don't need to use the centroids in the method I'm proposing. Also, I'm going to assume that your line points are stored in a matrix called linePoints that is of the same type that I just talked about.
In other words, do this:
numBoundaryPoints = size(boundry, 1); %// boundary is misspelled in your code BTW
ptsIntersect = []; %// Store points of intersection here
for idx = 1 : numBoundaryPoints %// For each boundary point...
%//Obtain the i'th boundary point
pt = boundry(:,idx);
%//Get distances - This computes the Euclidean distance
%//between the i'th boundary point and all points along your line
dists = sqrt(sum(bsxfun(#minus, linePoints, pt).^2, 2));
%//Figure out which points intersect and store
ptsIntersect = [ptsIntersect; linePoints(dists < 0.0001, :)];
end
In the end, ptsIntersect will store all of the points along the boundary that intersect with this line. Take note that I have made a lot of assumptions here because you haven't (or seem reluctant to) give any more details than what you've specified in your comments.
Good luck.
This is my method for determining if a polygon is intersecting.
Find the equations of each side of the polygon and simultaneously solving one for another to determine where they intersect, at which point.
%m=[vector of gradients] c=[vector of y intersects].
y = (m_n)x + (c_n)
x = (y-c) ./ (m-2) %(don't include first value for m and c)
y = m.*x + c
(I think you will need a loop?)
Then if they intersect this would be entered into a matrix.
intersects = [x_1 y_1;...;x_n y_n] %(x_1 = 1st insect on x plane)
If intersects contained values.
disp('This polygon is complex')
Then it would involve breaking down the polygon into its subsequent parts and calculating the area for each using the new points.
Could someone let me know if this makes sense?
Would this work:
for i=1:length(m)
x{i} = (m(i)- c)./ (m-c(i));
y{i} = m.*x{i}+c;
end
I need to generate N random coordinates for a 2D plane. The distance between any two points are given (number of distance is N(N - 1) / 2). For example, say I need to generate 3 points i.e. A, B, C. I have the distance between pair of them i.e. distAB, distAC and distBC.
Is there any built-in function in MATLAB that can do this? Basically, I'm looking for something that is the reverse of pdist() function.
My initial idea was to choose a point (say A is the origin). Then, I can randomly find B and C being on two different circles with radii distAB and distAC. But then the distance between B and C might not satisfy distBC and I'm not sure how to proceed if this happens. And I think this approach will get very complicated if N is a large number.
Elaborating on Ansaris answer I produced the following. It assumes a valid distance matrix provided, calculates positions in 2D based on cmdscale, does a random rotation (random translation could be added also), and visualizes the results:
%Distance matrix
D = [0 2 3; ...
2 0 4; ...
3 4 0];
%Generate point coordinates based on distance matrix
Y = cmdscale(D);
[nPoints dim] = size(Y);
%Add random rotation
randTheta = 2*pi*rand(1);
Rot = [cos(randTheta) -sin(randTheta); sin(randTheta) cos(randTheta) ];
Y = Y*Rot;
%Visualization
figure(1);clf;
plot(Y(:,1),Y(:,2),'.','markersize',20)
hold on;t=0:.01:2*pi;
for r = 1 : nPoints - 1
for c = r+1 : nPoints
plot(Y(r,1)+D(r,c)*sin(t),Y(r,2)+D(r,c)*cos(t));
plot(Y(c,1)+D(r,c)*sin(t),Y(c,2)+D(r,c)*cos(t));
end
end
You want to use a technique called classical multidimensional scaling. It will work fine and losslessly if the distances you have correspond to distances between valid points in 2-D. Luckily there is a function in MATLAB that does exactly this: cmdscale. Once you run this function on your distance matrix, you can treat the first two columns in the first output argument as the points you need.
thats my first post, so please be kind.
I have a matrix with 3~10 coordinates and I want to connect these points to become a polygone with maximum size.
I tried fill() [1] to generate a plot but how do I calculate the area of this plot? Is there a way of converting the plot back to an matrix?
What would you reccomend me?
Thank you in advance!
[1]
x1 = [ 0.0, 0.5, 0.5 ];
y1 = [ 0.5, 0.5, 1.0 ];
fill ( x1, y1, 'r' );
[update]
Thank you for your answer MatlabDoug, but I think I did not formulate my question clear enough. I want to connect all of these points to become a polygone with maximum size.
Any new ideas?
x1 = rand(1,10)
y1 = rand(1,10)
vi = convhull(x1,y1)
polyarea(x1(vi),y1(vi))
fill ( x1(vi), y1(vi), 'r' );
hold on
plot(x1,y1,'.')
hold off
What is happening here is that CONVHULL is telling us which verticies (vi) are on the convex hull (the smallest polygon that encloses all the points). Knowing which ones are on the convex hull, we ask MATLAB for the area with POLYAREA.
Finally, we use your FILL command to draw the polygon, then PLOT to place the points on there for confirmation.
I second groovingandi's suggestion of trying all polygons; you just have to be sure to check the validity of the polygon (no self-intersections, etc).
Now, if you want to work with lots of points... As MatlabDoug pointed out, the convex hull is a good place to start. Notice that the convex hull gives a polygon whose area is the maximum possible. The problem, of course, is that there could be points in the interior of the hull that are not part of the polygon. I propose the following greedy algorithm, but I am not sure if it guarantees THE maximum area polygon.
The basic idea is to start with the convex hull as a candidate final polygon, and carve out triangles corresponding to the unused points until all the points belong to the final polygon. At each stage, the smallest possible triangle is removed.
Given: Points P = {p1, ... pN}, convex hull H = {h1, ..., hM}
where each h is a point that lies on the convex hull.
H is a subset of P, and it is also ordered such that adjacent
points in the list of H are edges of the convex hull, and the
first and last points form an edge.
Let Q = H
while(Q.size < P.size)
% For each point, compute minimum area triangle
T = empty heap of triangles with value of their area
For each P not in Q
For each edge E of Q
If triangle formed by P and E does not contain any other point
Add triangle(P,E) with value area(triangle(P,E))
% Modify the current polygon Q to carve out the triangle
Let t=(P,E) be the element of T with minimum area
Find the ordered pair of points that form the edge E within Q
(denote them Pa and Pb)
Replace the pair (Pa,Pb) with (Pa,E,Pb)
Now, in practice you don't need a heap for T, just append the data to four lists: one for P, one for Pa, one for Pb, and one for the area. To test if a point lies within a triangle, you only need to test each point against the lines forming the sides of the triangle, and you only need to test points not already in Q. Finally, to compute the area of the final polygon, you can triangulate it (like with the delaunay function, and sum up the areas of each triangle in the triangulation), or you can find the area of the convex hull, and subtract out the areas of the triangles as you carve them out.
Again, I don't know if this greedy algorithm is guaranteed to find the maximum area polygon, but I think it should work most of the time, and is interesting nonetheless.
You said you only have 3...10 points to connect. In this case, I suggest you just take all possible combinations, compute the areas with polyarea and take the biggest one.
Only if your number of points increases or if you have to compute it frequently so that compuation time matters, it's worth investing some time in a better algorithm. However I think it's difficult to come up with an algorithm and prove its completeness.
Finding the right order for the points is the hard part, as Amro commented. Does this function suffice?
function [idx] = Polyfy(x, y)
% [idx] = Polyfy(x, y)
% Given vectors x and y that contain pairs of points, find the order that
% joins them into a polygon. fill(x(idx),y(idx),'r') should show no holes.
%ensure column vectors
if (size(x,1) == 1)
x = x';
end
if (size(y,1) == 1)
y = y';
end
% vectors from centroid of points to each point
vx = x - mean(x);
vy = y - mean(y);
% unit vectors from centroid towards each point
v = (vx + 1i*vy)./abs(vx + 1i*vy);
vx = real(v);
vy = imag(v);
% rotate all unit vectors by first
rot = [vx(1) vy(1) ; -vy(1) vx(1)];
v = (rot*[vx vy]')';
% find angles from first vector to each vector
angles = atan2(v(:,2), v(:,1));
[angles, idx] = sort(angles);
end
The idea is to find the centroid of the points, then find vectors from the centroid to each point. You can think of these vectors as sides of triangles. The polygon is made up the set of triangles where each vector is used as the "left" and "right" only once, and no vectors are skipped. This boils down to ordering the vectors by angle around the centroid.
I chose to do this by normalizing the vectors to unit length, choosing one of them as a rotation vector, and rotating the rest. This allowed me to simply use atan2 to find the angles. There's probably a faster and/or more elegant way to do this, but I was confusing myself with trig identities. Finally, sorting those angles provides the correct order for the points to form the desired polygon.
This is the test function:
function [x, y] = TestPolyArea(N)
x = rand(N,1);
y = rand(N,1);
[indexes] = Polyfy(x, y);
x2 = x(indexes);
y2 = y(indexes);
a = polyarea(x2, y2);
disp(num2str(a));
fill(x2, y2, 'r');
hold on
plot(x2, y2, '.');
hold off
end
You can get some pretty wild pictures by passing N = 100 or so!