I have 3D flow data of the velocity of a fluid through a tube. I know the diameter of the tube and have looked at the velocity field and found the centre of the field for an xy plane at both ends of the tube. So I essentially have a line through the centre axis of the tube. I want to NaN all data points that are outside of the diameter. For this I am using an equation that gives the distance to a point from a line in 3D which I found here mathworld.wolfram.com/Point-LineDistance3-Dimensional.html. I then created an if statement which states points smaller than diameter will be NaN.
I am new to matlab so I don't know how I would now plot this.
%%
diff_axis = end_axis-start_axis;
diff_axis_mag = (diff_axis(1)^2 + diff_axis(2)^2 + diff_axis(3)^2)^0.5;
[rw col pl] = size(X);
for j = 1:col
for i = 1:rw
for k = 1:pl
x_curr = X(i,j,k);
y_curr = Y(i,j,k);
z_curr= Z(i,j,k);
x0 = [x_curr y_curr z_curr]
t = - dot((start_axis-x0),(diff_axis))./(diff_axis_mag)^2;
d = sqrt(((start_axis(1) - x0(1)) + (end_axis(1) - start_end(1))*t)^2 + ((start_axis(2)-x0(2))+(end_axis(2)-start_end(2))*t)^2+((start_axis(3)-x0(3))+(end_axis(3)-start_end(3))*t)^2);
if (d > D)
x_curr=NaN
y_curr=NaN
z_curr=NaN
end
end
end
end
It were nice to have explanatory names for your X, Y, and Z. I am guessing they are flow components, and diff_axis are axis coordinates? It is a very cumbersome notation.
what you do in your loops is you take point values (X,Y,Z), copy them to temporary constants and then set them to NaN if they fall out. But the problem is that usually you do not plot point-by-point in MATLAB. So these temorary guys like x_curr will be lost.
Also, the most optimal way to do things in MATLAB is to avoid loops whenever possible.
What you can do is to create first a mask
%// remember to put a dot like in `.^` for entrywise array operations
diff_axis_mag = sqrt(diff_axis(1).^2 + diff_axis(2).^2 + diff_axis(3).^2);
%// are you sure you need to include the third axis?
%// then it is a ball, not a tube
%// create a binary mask
mask = diff_axis_mag < tube_radius
X(~mask) = NaN;
Y(~mask) = NaN;
Z(~mask) = NaN;
Then you can plot your data with quiver3 or
stream3
Related
I've found this answer, but I can't complete my work. I wanted to plot more precisely the functions I am studying, without overcoloring my function with black ink... meaning reducing the number of mesh lines. I precise that the functions are complex.
I tried to add to my already existing code the work written at the link above.
This is what I've done:
r = (0:0.35:15)'; % create a matrix of complex inputs
theta = pi*(-2:0.04:2);
z = r*exp(1i*theta);
w = z.^2;
figure('Name','Graphique complexe','units','normalized','outerposition',[0.08 0.1 0.8 0.55]);
s = surf(real(z),imag(z),imag(w),real(w)); % visualize the complex function using surf
s.EdgeColor = 'none';
x=s.XData;
y=s.YData;
z=s.ZData;
x=x(1,:);
y=y(:,1);
% Divide the lengths by the number of lines needed
xnumlines = 10; % 10 lines
ynumlines = 10; % 10 partitions
xspacing = round(length(x)/xnumlines);
yspacing = round(length(y)/ynumlines);
hold on
for i = 1:yspacing:length(y)
Y1 = y(i)*ones(size(x)); % a constant vector
Z1 = z(i,:);
plot3(x,Y1,Z1,'-k');
end
% Plotting lines in the Y-Z plane
for i = 1:xspacing:length(x)
X2 = x(i)*ones(size(y)); % a constant vector
Z2 = z(:,i);
plot3(X2,y,Z2,'-k');
end
hold off
But the problem is that the mesh is still invisible. How to fix this? Where is the problem?
And maybe, instead of drawing a grid, perhaps it is possible to draw circles and radiuses like originally on the graph?
I found an old script of mine where I did more or less what you're looking for. I adapted it to the radial plot you have here.
There are two tricks in this script:
The surface plot contains all the data, but because there is no mesh drawn, it is hard to see the details in this surface (your data is quite smooth, this is particularly true for a more bumpy surface, so I added some noise to the data to show this off). To improve the visibility, we use interpolation for the color, and add a light source.
The mesh drawn is a subsampled version of the original data. Because the original data is radial, the XData and YData properties are not a rectangular grid, and therefore one cannot just take the first row and column of these arrays. Instead, we use the full matrices, but subsample rows for drawing the circles and subsample columns for drawing the radii.
% create a matrix of complex inputs
% (similar to OP, but with more data points)
r = linspace(0,15,101).';
theta = linspace(-pi,pi,101);
z = r * exp(1i*theta);
w = z.^2;
figure, hold on
% visualize the complex function using surf
% (similar to OP, but with a little bit of noise added to Z)
s = surf(real(z),imag(z),imag(w)+5*rand(size(w)),real(w));
s.EdgeColor = 'none';
s.FaceColor = 'interp';
% get data back from figure
x = s.XData;
y = s.YData;
z = s.ZData;
% draw circles -- loop written to make sure the outer circle is drawn
for ii=size(x,1):-10:1
plot3(x(ii,:),y(ii,:),z(ii,:),'k-');
end
% draw radii
for ii=1:5:size(x,2)
plot3(x(:,ii),y(:,ii),z(:,ii),'k-');
end
% set axis properties for better 3D viewing of data
set(gca,'box','on','projection','perspective')
set(gca,'DataAspectRatio',[1,1,40])
view(-10,26)
% add lighting
h = camlight('left');
lighting gouraud
material dull
How about this approach?
[X,Y,Z] = peaks(500) ;
surf(X,Y,Z) ;
shading interp ;
colorbar
hold on
miss = 10 ; % enter the number of lines you want to miss
plot3(X(1:miss:end,1:miss:end),Y(1:miss:end,1:miss:end),Z(1:miss:end,1:miss:end),'k') ;
plot3(X(1:miss:end,1:miss:end)',Y(1:miss:end,1:miss:end)',Z(1:miss:end,1:miss:end)','k') ;
pointA=[9.62579 15.7309 3.3291];
pointB=[13.546 25.6869 3.3291];
pointC=[23.502 21.7667 -3.3291];
pointD=[19.5818 11.8107 -3.3291];
points=[pointA' pointB' pointC' pointD'];
fill3(points(1,:),points(2,:),points(3,:),'r')
grid on
alpha(0.3)
This code will show a filled plane(Cant add images yet T.T)
Now here is my problem. On a spreadsheet, I have x,y,z coordinates of thousands of points. The 4 consecutive points form a plane like the one shown. How do I make a code such that for every 4 consecutive points, it makes a filled plane.
Basically, if I have 400 points, I want the code to plot 100 planes.
Assuming your data are a matrix, m = (400,3)
m = rand(400,3);
for i = 1:length(m);
m2 = m'; % Transpose
end
Create a 3-D matrix in which 'j' represents each set of points:
m3=[];
%Not the most elegant way to cycle through every four points but it works!
z = 0:(length(m2)/4); z1 = (z*4)+1; z1 = z1(:,1:length(z)-1);
for j = 1:length(z1);
m3(:,:,j) = m2(:,z1(j):(z1(j)+3));
end
'j' now has a total length = 100 - representing the amount planes;
fill3(m3(1,:,1),m3(2,:,1),m3(3,:,1),'r');
% Cycle through planes- make a new figure for each plane;
for j = 1:length(z1);
fill3(m3(1,:,j),m3(2,:,j),m3(3,:,j),'r');
end
clear all, close all, clc
pointA=rand(99,1);
pointB=rand(99,1);
pointC=rand(99,1);
pointD=rand(99,1);
pointAmat = reshape(pointA,3,1,[]);
pointBmat = reshape(pointB,3,1,[]);
pointCmat = reshape(pointC,3,1,[]);
pointDmat = reshape(pointD,3,1,[]);
points=[pointAmat pointBmat pointCmat pointDmat];
for i = 1:size(points,3)
fill3(points(1,:,i),points(2,:,i),points(3,:,i),'r')
hold all
end
grid on
alpha(0.3)
Hope this helps.
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.
I know the locations of spheres (center and radius) in a box. I want to extract cross sections. I am able to plot the spheres placed in a cube using the following Matlab code:
[X,Y,Z] = sphere;
for SpNum = 1:NumSpheres
surf( X*Radius(SpNum)+Center(SpNum,1), Y*Radius(SpNum)+Center(SpNum,2), Z*Radius(SpNum)+Center(SpNum,3), ...
'FaceColor','r' );
%shading interp;
hold on;
end
axis tight; daspect([1 1 1]);
In the above code, each sphere could have different radius and they do not overlap (so the centers are also different).
The above code does not however generate cross sections. I want to extract cross sections similar to what we get from say X-ray CT data: a series of images in the Z-direction. I think 'interp2/interp3' and 'slice' functions are the relevant functions, but I am not sure how to use them to generate the cross sections. I would appreciate if anyone could give pointers or provide some sample code for my problem?
-- Thanks in advance.
Update:
I tried using meshgrid to generate the grid points followed by the function F(X,Y,Z) as follows:
[X,Y,Z] = meshgrid(1:100,1:100,1:100);
F = zeros(size(X),'uint8');
for SpNum = 1:NumSpheres
F( sqrt((X - Center(SpNum,1)).^2 + (Y - Center(SpNum,2)).^2 + (Z - Center(SpNum,3)).^2) <= Radius(SpNum) ) = 1;
end
surf(F);
followed by:
z = 1;
I = interp3(X, Y, Z, X*Radius(SpNum)+Center(SpNum,1), Y*Radius(SpNum)+Center(SpNum,2), Z*Radius(SpNum)+Center(SpNum,3), z, 'spline');
figure, imshow(I);
I know that interp3 is the function to use since it interpolates the values of the function F(X,Y,Z) which represent the spheres at different location within a bounded box (say 1:100, 1:100, 1:100). The interpolated values at particular 'z' (= 1, 2, 3... 100) should give me 100 cross sections (in the form of 2-D images).
The flaw is in the function F itself, since 'surf' throws an error saying that F should be an array - "CData must be an M-by-N matrix or M-by-N-by-3 array".
Can anyone please help.
I finally figured it. For the benefit of others, here is the code.
% A 3-D matrix 'F' which has its value at particular coordinate set to 255 if it belongs to any one of the spheres and 0 otherwise.
[X,Y,Z] = meshgrid(1:100,1:100,1:100);
F = zeros(size(X));
for SpNum = 1:NumSpheres
F( sqrt((X - Center(SpNum,1)).^2 + (Y - Center(SpNum,2)).^2 + (Z - Center(SpNum,3)).^2) <= Radius(SpNum) ) = 255;
end
% Extract cross sections from F using interp3 function along the z-axis.
I = zeros(size(X));
for z = 1:100
I(:,:,z) = interp3(X, Y, Z, F, 1:100, (1:100)', z, 'spline');
end
implay(I,4);
You could test and visualize the output by setting Center (a 3-D vector) and Radius of each sphere (some arbitrary NumSpheres) to some random values. The above code will display a window with cross-sections.
Previously, I was trying to use 'surf' to render the spheres which is not right. To render, you have to use the first code snippet. Another mistake I made was using a row vector for the 6th argument instead of column vector.
Hope this helps.
--
Cheers,
Ram.
I have a 3-D geometrical shape which I have to convert into a point cloud.
The resultant point cloud can be considered equivalent to a point cloud output from a Laser Scan of the object.
No mesh generation is neeeded
The points generated may be evenly spaced, or maybe just randomly spaced - doesn't matter
The 3-D shape can be provided in the form of a 3-D mathematical formula
This has to be done using MATLAB
It's difficult to answer without an example but it sounds like you just want to perform a montecarlo simulation?
Lets say your shape is defined by the function f and that you have X, Y limits stored in two element vector e.g. xlim = [-10 10] i.e. all possible x values of this shape lie between x = -10 and x = 10 then I would suggest that you make f return some sort of error code if there is no value for a specific x-y pair. I'm going to assume that will be NaN. So f(x,y) is a function you are writing that either returns a z if it can or NaN if it can't
n= 10000;
counter = 1;
shape = nan(n, 3)
while counter < n
x = rand*diff(xlim) + mean(xlmin);
y = rand*diff(ylim) + mean(ylim);
z = f(x,y)
if ~isnan(z)
shape(counter, :) = [x, y, z];
counter = counter + 1
end
end
So the above code will generate 10000 (non unique, but that's easily adapted for) points randomly sample across your shape.
Now after typing this I realise that perhaps your shape is actually not all that big and maybe you can uniformly sample it rather than randomly:
for x = xlim(1):xstep:xlim(2)
for y = ylim(1):ystep:ylim(2)
shape(counter, :) = [x, y, f(x,y)];
end
end
or if you write f to be vectorized (preferable)
shape = [(xlim(1):xstep:xlim(2))', (ylim(1):ystep:ylim(2))', f(xlim(1):xstep:xlim(2), ylim(1):ystep:ylim(2));
and then either way
shape(isnan(shape(:, 3), :) = []; %remove the points that fell outside the shape
Here is the code to create a Cloud image with a Depth image from a PrimeSense Camera.
The input/Ouput of this function :
-inputs
depth -depth map
topleft -topleft coordinates of the segmented image in the whole image
-outputs
pclouds -3d point clouds
MatLab code :
depth = double(depth);
% Size of camera image
center = [320 240];
[imh, imw] = size(depth);
constant = 570.3;
% convert depth image to 3d point clouds
pclouds = zeros(imh,imw,3);
xgrid = ones(imh,1)*(1:imw) + (topleft(1)-1) - center(1);
ygrid = (1:imh)'*ones(1,imw) + (topleft(2)-1) - center(2);
pclouds(:,:,1) = xgrid.*depth/constant;
pclouds(:,:,2) = ygrid.*depth/constant;
pclouds(:,:,3) = depth;
distance = sqrt(sum(pclouds.^2,3));
Edit : This source is from this current article http://www.cs.washington.edu/rgbd-dataset/software.html
You can find some other Cloud function in MatLab and C++ that can be interest you.