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.
Related
I have a big FEM model from where I can get the "surface" of the model, say the elements and vertex that define the surface of that FEM model. For plotting purposes (nice plots are always a win!) I want to plot it nicely. My approach is just to use
lungs.Vertex=vtx;
lungs.Faces=fcs;
patch(lungs,'facecolor','r','edgecolor','none')
NOTE: I need edgecolor none, as this is 4D data and different FEM have different triangulation, if edges are plotted user will be dizzy.
However this will output everything in a really plain red color, which is not nice (as it can not show the complexity of the figure, which are lungs, for the attentive to details).
therefore I decided to use ligthing:
camlight; camlight(-80,-10); lighting phong;
But again, this is not entirely correct. Actually it seems that the patch nromals are not computed correctly by Matlab.
My supposition is that maybe the patches are not always defined counter-clockwise and therefore some normals go to the wrong direction. However that is something its not straightforward to check.
Anyone has a similar problem, or ahint of how should I aproach this problem in order to have a nice surface ploted here?
EDIT
Just for the shake of plotting, here is the result obtained with #magnetometer answer:
If your model gives you outward oriented normals, you can re-sort the faces of your model so that Matlab can properly calculate it's own normals. The following function works if you have triangular faces and outward oriented normals:
function [FaceCor,nnew]=SortFaces(Faces,Normals,Vertices)
FaceCor=Faces;
nnew=Normals*0;
for jj=1:size(Faces,1)
v1=Vertices(Faces(jj,3),:)-Vertices(Faces(jj,2),:);
v2=Vertices(Faces(jj,2),:)-Vertices(Faces(jj,1),:);
nvek=cross(v2,v1); %calculate normal vectors
nvek=nvek/norm(nvek);
nnew(jj,:)=nvek;
if dot(nvek,Normals(jj,:))<0
FaceCor(jj,:)=[Faces(jj,3) Faces(jj,2) Faces(jj,1)];
nnew(jj,:)=-nvek;
end
end
If your FEM model doesn't give you outward directed normals, one way could be to reconstruct the surface using e.g. a crust algorithm that gives you outward directed normals or correctly oriented patches.
EDIT:
As you don't have normals, the only solution that comes to my mind is to reconstruct the surface. This implementation of the crust algorithm has worked well for me in the past. All you need to do is:
[FacesNew,NormalsNew]=MyRobustCrust(Vertices);
If I remember correctly, FacesNew are not yet oriented counterclockwise, but you can use the SortFaces algorithm I posted above to correct for this, as you now have correctly oriented face normals, i.e. run:
[FaceCor,~]=SortFaces(FacesNew,NormalsNew,Vertices)
If you use Matlab's reducepatch (e.g. reducedmodel=reducepatch(fullmodel,reduction); ) to reduce the number of vertices, you will have to reconstruct the surface again, as reducepatch doesn't seem to keep the correct orientation of the patches.
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.
Anyone have any starting tips for me? I want to learn from this (ie Don't want to be lazy and have someone answer this for me).
I would like to develop my understanding of mathematical 3D surfaces. My own personal project is to produce a 3D surface/graph of the concourse structure in MATLAB.
I found a link with good pictures of its geometry here. I am not expecting to get it 100% perfectly but I'd like to come close!
At the end of this exercise I would like to have a mathematical definition of the geometry as well as a visual representation of the surface. This can involve cartesian equations, parametric equations, matrices, etc.
Any help would be very much appreciated!
To give some specific advice for MATLAB:
I would load in the 'section' image from the web page you have linked, and display this in a MATLAB figure window. You can then try plotting lines over the top until you find one that fits nicely. So you might do something like:
A = imread('~/Desktop/1314019872-1244-n364-1000x707.jpg');
imshow(A)
hold on
axis on
%# my guess at the function - obviously not a good fit
x = [550:900];
plot(x, 0.0001*x.^2 + 300)
Of course, you might want to move the position of the origin or crop the picture and so on.
As an arguably better alternative to this trial-and-error method, you could trace the outline of the section (e.g by clicking points with something like ginput), and then use one of MATLAB's curve-fitting tools (e.g. fit) to fit a function to the data.
The final 3D shape looks to me (at a casual glance) to be a 3D revolution of the section shape around a central axis. Use of a cylindrical coordinate system could therefore be a good idea.
The final plotting of your 3D shape could be done with a function such as surf or mesh.
I would start by defining a function that defines for each x, y coordinate whether there is a point z, and if so with which altitude.
The shape reminds me a bit of a log or a square root.
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 have a formula that depends on theta and phi (spherical coordinates 0<=theta<=2*pi and 0<=phi<=pi). By inserting each engle, I obtained a quantity. Now I have a set of data for different angles and I need to plot the surface. My data is a 180*360 matrix, so I am not sure if I can use SURF or MESH or PLOT3. The figure should be a surface that include all data and the axes should be in terms of the quantity, not the quantity versus the angles. How can I plot such a surface?
I see no reason why you cannot use mesh or surf to plot such data. Another option I tend to use is that of density plots. You basically display the dependent variable (quantity) as an image and include the independent variables (angles) along the axis, much like you would with the aforementioned 3D plotting functions. This can be done with imagesc.
Typically you would want your axes to be the dependent variables. Could you elaborate more on this point?
If I understand you correctly you have calculated a function f(theta,phi) and now you want to plot the surface containing all the points with the polar coordinated (r,theta,phi) where r=f(theta,phi).
If this is what you want to do, the 2D version of such a plot is included in MATLAB under the name polar. Unfortunately, as you pointed out, polar3 on MatlabCentral is not the generalization you are looking for.
I have been able to plot a sphere with the following code, using constant r=1. You can give it a try with your function:
phi1=0:1/(3*pi):pi; %# this would be your 180 points
theta1=-pi:1/(3*pi):pi; % your 360 points
r=ones(numel(theta1),numel(phi1));
[phi,theta]=meshgrid(phi1,theta1);
x=r.*sin(theta).*cos(phi);
y=r.*sin(theta).*sin(phi);
z=r.*cos(theta);
tri=delaunay(x(:),y(:),z(:));
trisurf(tri,x,y,z);
From my tests it seems that delaunay also includes a lot of triangles which go through the volume of my sphere, so it seems this is not optimal. So maybe you can have a look at fill3 and construct the triangles it draws itself: as a first approximation, you could have the points [x(n,m) x(n+1,m) x(n,m+1)] combined into one triangle, and [x(n+1,m) x(n+1,m+1) x(n+1,m+1)] into another...?