Why do the delaunay edges from Matlab's delaunayn() connect points with non-adjacent Voronoi regions? - matlab

I am trying to find the points with edge-adjacent Voronoi regions in a given dataset. I'm new to computational geometry, but from reading up online, it appeared that using the Delaunay tessellation would be an easy way to do this. This PDF in particular even has a lemma that states
Lemma 2.4 Two points of S are joined by a Delaunay edge iff their Voronoi regions
are edge-adjacent.
So, I found the delaunay tessellation of my dataset as
dt = delaunay(dataset); %using delaunayn() since dataset can be multidimensional
But now, when I plot this along with the voronoi diagram for this dataset, I find that the delaunay edges returned connect points whose regions are not actually edge-adjacent.
Here is the code I used to plot Voronoi and Delaunay together:
voronoi(dataset(:, 1),dataset(:, 2));
hold on;
dt = delaunayn(dataset);
triplot(dt, dataset(:, 1), dataset(:, 2), 'red');
And here is the output:
As an example of the problem, see the point X on the right end of the figure connected to point Y near the lower left corner.
Another example is in this SO question - the point 1 is connected to 2 and 3, even though they're not adjacent, and there doesn't seem to be any way 1 and 2 could share an edge even if extended to infinity. This question is actually what prompted me to test the delaunayn output with the above code.
Why is this happening, and how do I actually get the edge-adjacent neighbour regions that I need?
Note: For seeing the image in full size and clarity, please right click and choose 'View image' or similar.

As far as I can see (the quality of the diagram is not so good), the regions for X and Y should be adjacent below the plotted part. If you zoom out far enough, you should see them.
I.e. the edge where X and Y meet exists, but is just not shown on the plot.
The following diagram does not show the voronoi diagram outside of the plotting area, but how to find the intersection point described above (note, the bisector is not shown here):

Related

Connecting scatter points in matlab

I have a dataset with points (X,Y coordinates) that represent the shape of a glacier. However, when I plot them with
% Import glacier shape
Glaciershape = readtable('dem_glacierlocation.txt');
figure(1);
S = Glaciershape(:,1);
T = Glaciershape(:,2);
plot(S,T,'-')
It seems that an points connect when they don't need to (see attachment, at the left upper corner of the shape). Is there a way to fix this? You can download the dataset in the link below
Thanks!
Download text file glacier
If you are looking for the shortest path connecting all dots in a loop, this is known as the traveling salesman problem, which is very difficult to solve.
If you are just looking for an easy way to visualize try:
plot(S,T,'.')
where you replace the '-' you had above with '.'. This will plot the x,y coordinates unconnected and you can let the human brain do the connections, which it is good at.
Here is the image without connections. It looks like at the top there are two areas which are holes, which is why connecting all the points might be problematic.
plot will assume every set of x,y coords is sequential in a series and plot them all without a care as to content.
If you could make some assumption about the distribution of points then you could use that to break on those that fail. For example, a point in the series is always less than 100 units from the previous point. Otherwise, this begins a new series list. Using that you could do a check on distance between subsequent points using diff as in:
% assume data stored in xcoord,ycoord
% check for distance greater than 100
idx = find(sqrt(diff(xcoords).^2 + diff(ycoords).^2) > 100 );
% in this particular data set there are 3 disjoint sections
% plot out each section - here each done explicitly for illustration
plot(xcoords(1:idx(1)),ycoords(1:idx(1)));
hold on;
plot(xcoords(idx(1)+1:idx(2)),ycoords(idx(1)+1):idx(2));
plot(xcoords(idx(2)+1:idx(3)),ycoords(idx(2)+1):idx(3));
plot(xcoords(idx(3)+1:end),ycoords(idx(2)+1):end);
edit: added other plot's after looking at data file provided
hope that helps...

Output of delaunay triangulation from lidar data

I have to generate the mesh of the 3D-point cloud. So I used the delaunay function to perform the triangulation of points. The lidar data is a result of a human head scanning.
dt = delaunay(X,Y);
trisurf(dt,X,Y,Z);
When I used delaunay with two inputs it gives me output but not perfect. So I used three inputs (X,Y,Z)
dt = delaunay(X,Y,Z);
trisurf(dt,X,Y,Z);
But now the result comes out worse. I don't know what the problem is?
This is the full code that I have written:
load Head_cloud_point.txt;
data = Head_cloud_point;
for i = 1 : 3
X = data(:, 1);
end
for i = 1 : 3
Y = data(:, 2);
end
for i = 1 : 3
Z = data(:, 3);
end
[m n] = size(X);
[o p] = size(Y);
[r s] = size(Z);
[XI,YI]= meshgrid(X(m,n),Y(o,p));
ZI = interp2(X,Y,Z,XI,YI);
% dt = delaunay(X,Y);
% trisurf(dt,X,Y,ZI);
Head_cloud_point is the file with X,Y,Z coordinates. I have to generate the mesh using these coordinates.
Well, Delaunay is not going to do the trick directly here, neither the 2D nor the 3D version. The main reason is the way Delaunay is working. You can get some of the way, but the result is in general not going to be perfect.
You have not specified whether the poing cloud is the surface of the head, or the entire inner of the head (though another answer indicates the former).
First remember that Delaunay is going to triangulate the convex hull of the data, filling out any concavities, e.g. a C-like shape will have the inner part of the C triangulated (Ending like a mirrored D triangulation).
Assuming the point cloud is the surface of the head.
When using 2D Delaunay on all (X,Y), it can not distinguish between coordinates at the top of the head and at the bottom/neck, so it will mix those when generating the triangulation. Basically you can not have two layers of skin for the same (X,Y) coordinate.
One way to circumvent this is to split the data in a top and bottom part, probably around the height of the tip of the nose, triangulate them individually and merge the result. That could give something fairly nice to look at, though there are other places where there are similar issues, for example around the lips and ears. You may also have to connect the two triangulations, which is somewhat difficult to do.
Another alternative could be to transform the (X,Y,Z) to spherical coordinates (radius, theta, gamma) with origin in the center of the head and then using 2D Delaunay on (theta,gamma). That may not work well around the ear, where there can be several layers of skin at the same (theta,gamma) direction, where again Delaunay will mix those. Also, at the back of the head (at the coordinate discontinuity) some connections will be missing. But at the rest of the head, results are probably nice. The Delaunay triangulation in (theta, gamma) is not going to be a Delaunay triangulation in (X,Y,Z) (the circumcircle associated with each triangle may contain other point in its interior), but for visualization purposes, it is fine.
When using the 3D Delaunay using (X,Y,Z), then all concavities are filled out, especially around the tip of the nose and the eyes. In this case you will need to remove all elements/rows in the triangulation matrix that represents something "outside" the head. That seems difficult to do with the data at hand.
For the perfect result, you need another tool. Try search for something like:
meshing of surface point cloud
Since you have a cloud of raw data representing a 3D surface, you need to do a 3D surface interpolation to remove the noise. This will determine a function z=f(x,y) that best fits your data. To do that, you can use griddata, triscatteredinterp (deprecated) or interp2.
Note: From the context of your question, I assumed you use MATLAB.
[EDIT]
As you indicated that your data represents a head, the surface of a head which is a spheroid, it is not a function of the form z=f(x,y). See this post concerning possible solutions to visualizing spherical surfaces http://www.mathworks.com/matlabcentral/newsreader/view_thread/2287.

Evaluate straightness of an arbitrary countour

I want to get a metric of straightness of contour in my binary image (relatively faster). The image looks as follows:
Now, the contours in the red box are the ones which I would like to be removed preferably. Since they are not straight. These are the things I have tried. I am as of now implementing in MATLAB.
1.Collect row and column coordinates of each contour and then take derivative. For straight objects (such as rectangle), derivative will be mostly low with a few spikes (along the corners of the rectangle).
Problem: The coordinates collected are not in order i.e. the order in which the contour will be traversed if we imaging it as a path. Therefore, derivative gives absurdly high values sometimes. Also, the contour is not absolutely straight, its an output of edge detection algorithm, so you can imagine that there might be some discontinuity (see the rectangle at the bottom, human eye can understand that it is a rectangle though it is not absolutely straight).
2.Tried to think about polyfit, but again this contour issue comes up. Since its a rectangle I don't know how to apply polyfit to that point set.
Also, I would like to remove contours which are distributed vertically/horizontally. Basically this is a lane detection algorithm. So lanes cannot be absolutely vertical/horizontal.
Any ideas?
You should look into the features of regionprops more. To be fair I stole the script from this answer, but here it is:
BW = imread('lanes.png');
BW = im2bw(BW);
figure(1),
subplot(1,2,1);
imshow(BW);
cc = bwconncomp(BW);
l = labelmatrix(cc);
a_rp = regionprops(CC,'Area','MajorAxisLength','MinorAxislength','Orientation','PixelList','Eccentricity');
idx = ([a_rp.Eccentricity] > 0.99 & [a_rp.Area] > 100 & [a_rp.Orientation] < 70 & [a_rp.Orientation] > -90);
BW2 = ismember(l,find(idx));
subplot(1,2,2);
imshow(BW2);
You can mess around with the properties. 'Orientation', 'Eccentricity', and 'Area' are probably the parameters you want to mess with. I also messed with the ratios of the major/minor axis lengths but eccentricity basically does this (eccentricity is a measure of how "circular" an ellipse is). Here's the output:
I actually saw a good video specifically from matlab for lane detection using regionprops. I'll try to see if I can find it and link it.
You can segment your image using bwlabel, then work separately on each bwlabel connected object, using find. This should help solve your order problem.
About a metric, the only thing that come to mind at the moment is to fit to an ellipse, and set the a/b (major axis/minor axis) ratio (basically eccentricity) a parameter. For example a straight line (even if not perfect) will be fitted to an ellipse with a very big major axis and a very small minor axis. So say you set a ratio threshold of >10 etc... Fitting to an ellipse can be done using this FEX submission for example.

Regarding Voronoi diagram

In MATLAB's function of Voronoi diagram, the vertices of edges at infinity are plotted at some distant point. Have a look at the first diagram on the page here. The first point from the top on Y-axis is (0,0.75). (Though it is extended beyond the bounds of the image). I know if I run the following matlab function:
[vx,vy]=voronoi(x,y)
I can get the coordinates of the vertices, but they will be beyond the bounds of the plot. Is there any way to get the coordinate in bounds of the plot (for example, (0,0.75) as mentioned above).
All you need is to detect which of the vx,vy crosses the axes (using find or logical conditions, find(vx<0) , find(vy>1) etc...) , and then apply the equation of the line y=a*x+b. For the point you wanted (which happens to be the 19th col of vx,vy, the slope a is:
a=diff(vy(:,19))/diff(vx(:,19));
and the intersection with y axis is given by b:
b=vy(1,19)-a*vx(1,19)
b =
0.7546
To calc b I picked the first point [vx(1,19),vy(1,19)] but this of course works also for the second point, i.e. b=vy(2,19)-a*vx(2,19)

How do I break a polyhedron into tetrahedra in MATLAB?

I have a polyhedron, with a list of vertices (v) and surfaces (s). How do I break this polyhedron into a series of tetrahedra?
I would particularly like to know if there are any built-in MATLAB commands for this.
For the convex case (no dents in the surface which cause surfaces to cover each other) and a triangle mesh, the simple solution is to calculate the center of the polyhedron and then connect the three corners of every face with the new center.
If you don't have a triangle mesh, then you must triangulate, first. Delaunay triangulation might help.
If there are holes or caves, this can be come arbitrarily complex.
I'm not sure the OP wanted a 'mesh' (Steiner points added) or a tetrahedralization (partition into tetrahedra, no Steiner points added). for a convex polyhedron, the addition of Steiner points (e.g. the 'center' point) is not necessary.
Stack overflow will not allow me to comment on gnovice's post (WTF, SO?), but the proof of the statement "the surfaces of a convex polyhedron are constraints in a Delaunay Tesselation" is rather simple: by definition, a simplex or subsimplex is a member in the Delaunay Tesselation if and only if there is a n-sphere circumscribing the simplex that strictly contains no point in the point set. for a surface triangle, construct the smallest circumscribing sphere, and 'puff' it outwards, away from the polyhedron, towards 'infinity'; eventually it will contain no other point. (in fact, the limit of the circumscribing sphere is a half-space; thus the convex hull is always a subset of the Delaunay Tesselation.)
for more on DT, see Okabe, et. al, 'Spatial Tesselations', or any of the papers by Shewchuk
(my thesis was on this stuff, but I remember less of it than I should...)
I would suggest trying the built-in function DELAUNAY3. The example given in the documentation link resembles Aaron's answer in that it uses the vertices plus the center point of the polyhedron to create a 3-D Delaunay tessellation, but shabbychef points out that you can still create a tessellation without including the extra point. You can then use TETRAMESH to visualize the resulting tetrahedral elements.
Your code might look something like this (assuming v is an N-by-3 matrix of vertex coordinate values):
v = [v; mean(v)]; %# Add an additional center point, if desired (this code
%# adds the mean of the vertices)
Tes = delaunay3(v(:,1),v(:,2),v(:,3)); %# Create the triangulation
tetramesh(Tes,v); %# Plot the tetrahedrons
Since you said in a comment that your polyhedron is convex, you shouldn't have to worry about specifying the surfaces as constraints in order to do the triangulation (shabbychef appears to give a more rigorous and terse proof of this than my comments below do).
NOTE: According to the documentation, DELAUNAY3 will be removed in a future release and DelaunayTri will effectively take its place (although currently it appears that defining constrained edges is still limited to only 2-D triangulations). For the sake of completeness, here is how you would use DelaunayTri and visualize the convex hull (i.e. polyhedral surface) as well:
DT = DelaunayTri(v); %# Using the same variable v as above
tetramesh(DT); %# Plot the tetrahedrons
figure; %# Make new figure window
ch = convexHull(DT); %# Get the convex hull
trisurf(ch,v(:,1),v(:,2),v(:,3),'FaceColor','cyan'); %# Plot the convex hull