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.
Related
I need to make a surface triangulation of stones (aggregates) point cloud in Matlab. For this I have x,y,z in Cartesian coordinate system.
For the simplicity I have started with spherical object.
DT = delaunayTriangulation(x,y,z);
figure
trisurf(DT.ConnectivityList,DT.Points(:,1),DT.Points(:,2),DT.Points(:,3), ...
'FaceColor','cyan','FaceAlpha', 0.8);
Run the code and result is:
Triangulation of Spherical Object
It makes triangles inside the sphere too. Connecting one point to another one in opposite or somewhere else.
Also, delaunayTriangulation() function triangulates for idealized surfaces and do not works for sensitive surfaces. Think about the surface of stone, there are a lot of irregularity on it.
Then I have tried DelaunayTri() function. It worked for sphere object, triangulated only surface. But when tried more complex (stone) shape, the function just idealized the shape and not considered all protrusions (irregularity) of stone.
tri = DelaunayTri(x,y,z);
[FBtri,FBpoints] = freeBoundary(tri);
figure
trisurf(FBtri,FBpoints(:,1),FBpoints(:,2),FBpoints(:,3), ...
'FaceColor','cyan','FaceAlpha', 0.8);
When freeBoundary() function removed from above code, result is same (idealized) and triangulation made inside the object, which is not acceptable.
Finally, I have used the delaunay() function. Which is even not correctly triangulated the sphere.
tri = delaunay(x,y,z);
figure
trisurf(tri,x,y,z,'FaceColor','cyan','FaceAlpha', 0.8);
Question: With which function and how (please detailed) can I make the detailed 3D triangulation of any shaped stone surface? What was my mistakes above codes?
None of the pure delaunay-based methods you tried will do what you want -- they all tessellate the full convex-hull of the input, not just the surface.
Reconstructing general surfaces from point-clouds is not a straight-forward task. I don't believe it can be done directly using a built-in MATLAB function.
You may wish to look into other options, such as the surface-reconstruction tools provided by CGAL.
I have a surface by the code below and another surface which is created by the exact same code. I want to see the height differences in another figure. How am I able to do that? Already operated with the Minus-operator but this won't work.
Furthermore the matrices have NOT the same size!
Appreciate your help!
x1 = Cx1;
y1 = Cy1;
z1 = Cz1;
tri1 = delaunay(x1,y1);
fig1 = figure%('units','normalized','outerposition',[0 0 1 1]);
trisurf(tri1,x2,y2,z2)
xlabel('x [mm] ','FontSize',30)
ylabel('y [mm] ','FontSize',30)
zlabel('z [mm] ','FontSize',30)
The simplest way to solve this problem is to interpolate from one mesh onto the other one. Such an approach works well when one is more highly resolved than the other, or when you're not as concerned with results at individual nodes, but rather the overall pattern across elements. If that's not the case, then you have a very complicated problem because you need to create a polygonal surface that fully captures all nodes and edges of both triangulations. Consider the following pair of triangular patterns:
A surface that captured all the variations would need to have all the vertices and edges that make up both of them, which is not a purely triangular surface. So, let us instead assume the easier case. To map results from one triangulation to the other, you simply need to formulate functions that define how the values vary along the triangles, which are more broadly called basis functions. It is often assumed that values betweeen the nodes (i.e. vertices) of the triangles vary linearly along the surfaces of the triangles. You can do it differently if you want, it just requires defining new basis functions. If we go for linear functions, then the equations in 2D are pretty simple. Let's say you make an array trimap that has which triangle each of the vertices of the other triangulation is inside of. This can be accomplished using the info here. Then, we set the coordinates of the vertices of the current triangle to (x1,y1), (x2,y2), and (x3,y3), and then do the math:
for cnt1=1,npoints
x1=x(tri1(trimap(cnt1),1));
x2=x(tri1(trimap(cnt1),2));
x3=x(tri1(trimap(cnt1),3));
y1=y(tri1(trimap(cnt1),1));
y2=y(tri1(trimap(cnt1),2));
y3=y(tri1(trimap(cnt1),3));
delta=x2*y3+x1*y2+x3*y1-x2*y1-x1*y3-x3*y2;
delta1=(x2*y3-x3*y2+xstat(cnt1)*(y2-y3)+ystat(cnt1)*(x3-x2));
delta2=(x3*y1-x1*y3+xstat(cnt1)*(y3-y1)+ystat(cnt1)*(x1-x3));
delta3=(x1*y2-x2*y1+xstat(cnt1)*(y1-y2)+ystat(cnt1)*(x2-x1));
weights(cnt1,1)=delta1/delta;
weights(cnt1,2)=delta2/delta;
weights(cnt1,3)=delta3/delta;
z1=z(tri1(trimap(cnt1),1));
z2=z(tri1(trimap(cnt1),2));
z3=z(tri1(trimap(cnt1),3));
valinterp(cnt1)=sum(weights(cnt1,:).*[z1,z2,z3]);
end
valinterp is the interpolated value for each point. Here and here are some nice slides explaining the mathematics behind all this. Note that I've not tested any of this code. Note also that you will need to do something to assign to values outside of the triangulation. Perhaps a null value, or an inverse-distance weighted value.
I have a 3D data set of a surface that is not a function graph. The data is just a bunch of points in 3D, and the only thing I could think of was to try scatter3 in Matlab. Surf will not work since the surface is not a function graph.
Using scatter3 gave a not so ideal result since there is no perspective/shading of any sort.
Any thoughts? It does not have to be Matlab, but that is my go-to source for plotting.
To get an idea of the type of surface I have, consider the four images:
The first is a 3D contour plot, the second is a slice in a plane {z = 1.8} of the contour. My goal is to pick up all the red areas. I have a method to do this for each slice {z = k}. This is the 3rd plot, and I like what I see here a lot.
Iterating this over z give will give a surface, which is the 4th plot, which is a bit noisy (though I have ideas to reduce the noise...). If I plot just the black surface using scatter3 without the contour all I get is a black indistinguishable blob, but for every slice I get a smooth curve, and I have noticed that the curves vary pretty smoothly when I adjust z.
Some fine-tuning will give a much better 4th plot, but still, even if I get the 4th plot to have no noise at all, the result using scatter3 will be a black incomprehensible blob when plotted alone and not on top of the 3D contour. I would like to get a nice picture of the full surface that is not plotted on top of the 3D contour plot
In fact, just to compare and show how bad scatter3 is for surfaces, even if you had exact points on a sphere and used scatter3 the result would be a black blob, and wouldn't even look like a sphere
Can POV-Ray handle this? I've never used it...
If you have a triangulation of your points, you could consider using the trisurf function. I have used that before to generate closed surfaces that have no boundary (such as polyhedra and spheres). The downside is that you have to generate a triangulation of your points. This may not be ideal to your needs but it definitely an option.
EDIT: As #High Performance Mark suggests, you could try using delaunay to generate a triangulation in Matlab
just wanted to follow up on this question. A quick nice way to do this in Matlab is the following:
Consider the function d(x, y, z) defined as the minimum distance from (x, y, z) to your data set. Make sure d(x, y, z) is defined on some grid that contains the data set you're trying to plot.
Then use isosurface to plot a (some) countour(s) of d(x, y, z). For me plotting the contour 0.1 of d(x, y ,z) was enough: Matlab will plot a nice looking surface of all points within a distance 0.1 of the data set with good lighting and all.
In povray, a blob object could be used to display a very dense collection of points, if you make them centers of spheres.
http://www.povray.org/documentation/view/3.6.1/71/
If you want to be able to make slices of "space" and have them colored as per your data, then maybe the object pattern (based on a #declared blob object) might do the trick.
Povray also has a way to work with df3 files, which I've never worked with, but this user appears to have done something similar to your visualization.
http://paulbourke.net/miscellaneous/df3/
I am trying to plot a 3d view of a very large CT dataset. My data is in a 3d matrix of 2000x2000x1000 dimension. The object is surrounded by air, which is set to NaN in my matrix.
I would like to be able to see the greyscale value of the surface of the object (no isosurface) but I cannot quite work out how to do that in Matlab. Can anyone help me please?
Given that I a dealing with a huge matrix and I am only interested in the surface of the object, does anyone know a good trick how to reduce the size of my dataset?
The function surf(X,Y,Z) allows you to plot 3d data, where (X,Y) gives the coordinates in the x-y-plane while Z gives the z-coordinate and the surface color.
By default the function does not plot anything for the NaN entries, so you should be good to go with the surf function.
To set the surf-function to use a grayscale plotting use:
surf(matrix3d);
colormap(gray);
This plots the matrix in a surface plot and sets the colormap to grayscale.
In addition, as I understand your data, you might be able to eliminate entire plane-segments in your matrix. If for instance the plane A(1,1:2000,1:1000) is NaN in all entries you could eliminate all those entries (thus the entire Y,Z-plane in entry X=1). This will however require some heavy for loops, which might be over the top. This depends on how many data matrices you have compared to how many different plot you want for each matrix.
I will try to give you some ideas. I assume lack of a direct 3D "surface detector".
Since you have a 3D matrix where XY-planes are CT scan slices and each slice is an image, I would try to find edges of each slice say with edge. This would require some preprocessing like first thresholding each slice image. Then I can either use scatter3 to display the edge data as a 3D point cloud or delaunay3 to display the edge data as a surface.
I hope this will help you achieve what you are asking for.
I managed to get it working:
function [X,Y,Z,C] = extract_surface(file_name,slice_number,voxel_size)
LT = imread(file_name);%..READ THE 2D MAP
BW = im2bw(LT,1);%..THRESHOLD TO BINARY
B = bwboundaries(BW,8,'noholes');%..FIND THE OUTLINE OF THE IMAGE
X = B{1}(:,1);%..EXTRACT X AND Y COORDINATES
Y = B{1}(:,2);
indices = sub2ind(size(LT),X,Y);%..FIND THE CORRESPONDING LINEAR INDICES
C = LT(indices);%..NOW READ THE VALUES AT THE OUTLINE POSITION
Z = ones(size(X))*slice_number;
I can then plot this with
figure
scatter3(X,Y,Z,2,C)
Now the only thing I could improve is to have all these points in the scatter plot connected with a surface. #upperBound you suggested delaunay3 for this purpose - I cannot quite figure out how to do this. Do you have a tip?
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