3D binary matrix/image from a surface mesh in Matlab - matlab

How could I create a 3D binary matrix/image from a surface mesh in Matlab?
For instance, when I create ellipsoid using:
[x, y, z] = ellipsoid(0,0,0,5.9,3.25,3.25,30);
X, Y and X are all 2D matrix with size 31 x 31.
Edited based on suggestion of #Magla:
function Create_Mask_Basedon_Ellapsoid3()
close all
SurroundingVol = [50, 50, 20];
%DATA
[MatX,MatY,MatZ] = meshgrid(-24:1:25, -24:1:25, -9:1:10);
[mask1, x, y, z] = DrawEllipsoid([0, -10, 0], [6, 3, 3], MatX,MatY,MatZ);
[mask2, x2, y2, z2] = DrawEllipsoid([15, 14, 6], [6, 3, 3], MatX,MatY,MatZ);
mask = mask1 + mask2;
%Surface PLOT
figure('Color', 'w');
subplot(1,2,1);
%help: Ideally I would like to generate surf plot directly from combined mask= mask1 + mask2;
s = surf(x,y,z); hold on;
s2 = surf(x2,y2,z2); hold off;
title('SURFACE', 'FontSize', 16);
view(-78,22)
subplot(1,2,2);
xslice = median(MatX(:));
yslice = median(MatY(:));
zslice = median(MatZ(:));
%help: Also how do I decide correct "slice" and angles to 3D visualization.
h = slice(MatX, MatY, MatZ, double(mask), xslice, yslice, zslice)
title('BINARY MASK - SLICE VOLUME', 'FontSize', 16);
set(h, 'EdgeColor','none');
view(-78, 22)
%az = 0; el = 90;
%view(az, el);
end
function [mask, Ellipsoid_x, Ellipsoid_y, Ellipsoid_z] = DrawEllipsoid(CenterEllipsoid, SizeEllipsoid, MatX, MatY, MatZ)
[Ellipsoid_x, Ellipsoid_y, Ellipsoid_z] = ellipsoid(CenterEllipsoid(1), CenterEllipsoid(2), CenterEllipsoid(3), SizeEllipsoid(1)/2 , SizeEllipsoid(2)/2 , SizeEllipsoid(3)/2 ,30);
v = [Ellipsoid_x(:), Ellipsoid_y(:), Ellipsoid_z(:)]; %3D points
%v = [x(:), y(:), z(:)]; %3D points
tri = DelaunayTri(v); %triangulation
SI = pointLocation(tri,MatX(:),MatY(:),MatZ(:)); %index of simplex (returns NaN for all points outside the convex hull)
mask = ~isnan(SI); %binary
mask = reshape(mask,size(MatX)); %reshape the mask
end

There you go:
%// Points you want to test. Define as you need. This example uses a grid of 1e6
%// points on a cube of sides [-10,10]:
[x y z] = meshgrid(linspace(-10,10,100));
x = x(:);
y = y(:);
z = z(:); %// linearize
%// Ellipsoid data
center = [0 0 0]; %// center
semiaxes = [5 4 3]; %// semiaxes
%// Actual computation:
inner = (x-center(1)).^2/semiaxes(1).^2 ...
+ (y-center(2)).^2/semiaxes(2).^2 ...
+ (z-center(3)).^2/semiaxes(3).^2 <= 1;
For the n-th point of the grid, whose coordinates are x(n), y(n), z(n), inner(n) is 1 if the point lies in the interior of the ellipsoid and 0 otherwise.
For example: draw the interior points:
plot3(x(inner), y(inner), z(inner), '.' , 'markersize', .5)

Here is a method for creating a binary mask from an ellipsoid. It creates a corresponding volume and sets to NaN the points outside the ellipsoid (ones inside).
It doesn't take into consideration the formula of the ellipsoid, but uses a convex hull. Actually, it works for any volume that can be correctly described by a 3D convex hull. Here, the convexhulln step is bypassed since the ellipsoid is already a convex hull.
All credits go to Converting convex hull to binary mask
The following plot
is produced by
%DATA
[x, y, z] = ellipsoid(0,0,0,5.9,3.25,3.25,30);
%METHOD
v = [x(:), y(:), z(:)]; %3D points
[X,Y,Z] = meshgrid(min(v(:)):0.1:max(v(:))); %volume mesh
tri = DelaunayTri(v); %triangulation
SI = pointLocation(tri,X(:),Y(:),Z(:)); %index of simplex (returns NaN for all points outside the convex hull)
mask = ~isnan(SI); %binary
mask = reshape(mask,size(X)); %reshape the mask
%PLOT
figure('Color', 'w');
subplot(1,2,1);
s = surf(x,y,z);
title('SURFACE', 'FontSize', 16);
view(-78,22)
subplot(1,2,2);
xslice = median(X(:));
yslice = median(Y(:));
zslice = median(Z(:));
h = slice(X, Y, Z, double(mask), xslice, yslice, zslice)
title('BINARY MASK - SLICE VOLUME', 'FontSize', 16);
set(h, 'EdgeColor','none');
view(-78,22)
Several ellipsoids
If you have more than one ellipsoid, one may use this masking method for each of them, and then combine the resulting masks with &.
Choice of slices and angle
"Correct" is a matter of personal choice. You can either
create the unrotated mask and rotate it after (Rotate a 3D array in matlab).
create a mask on already rotated ellipsoid.
create a mask on a slightly rotated ellipsoid (that gives you the choice of a "correct" slicing), and rotate it further to its final position.

Related

Applying 3D rotation matrix to x, y, z values obtained from surface function

Applying 3D rotation matrix to the x,y,z values obtained from surface function object. The error I get is due to the matrix not being nonconforment but how can I adjust the matrix correctly?
I know hgtransform / makehgtform can do rotations but I need to use rotation matrices since I plan on testing it using matrices created from quaternions.
I've created a little plane out of cylinders and the surface functions.
See code below:
clear all,clf
ax=axes('XLim',[-2 2],'YLim', [-2 10],'ZLim',[-1.5 1.5]);
grid on;
%axis equal;
xlabel('x');
ylabel('y');
zlabel('z');
ax
% rotate around
rot_mat = [.707 -.707 0;.707 .707 0; 0 0 1] %rotation matrix
[xc yc zc] = cylinder([0.1 0.0]); %cone
[x y z]= cylinder([0.2 0.2]);
h(1) = surface(xc,zc,-yc,'FaceColor', 'red'); %noise cone
h(2) = surface(z,y,0.5*x,'FaceColor', 'blue'); %right wing
h(3) = surface(-z,y,0.5*x,'FaceColor', 'yellow');%left wing
h(4) = surface(x,-1.5*z,0.5*y,'FaceColor', 'green'); %main body
h(5) = surface(xc,(1.5*yc)-1.3,z*.5,'FaceColor', 'red'); %tail
view(3);
x_temp = get(h(1),'xdata'); % get x values
y_temp = get(h(1),'ydata');
z_temp =get(h(1),'zdata');
xc_new=x_temp.*rot_mat;
%zc_new=
%yc_new=
I can get the x,y, and z value by using the commands
x_temp = get(h(1),'xdata');
y_temp = get(h(1),'ydata');
z_temp = get(h(1),'zdata');
The error I get is due to the matrix being nonconforment but how can I adjust the matrix correctly?
error: test_object_matrix_rot: product: nonconformant arguments (op1 is 2x21, op2 is 3x3).
The error is with the line xc_new=x_temp.*rot_mat;
PS: I'm using Octave 5.0.91 which is like Matlab
YOu are messing up a lot of things......in fact I would say, you have made your work complex. YOu should straight away work on matrices to rotate to new positons instead of arrays and picking them from the figure.
This line:
x_temp = get(h(1),'xdata'); % get x values
giving you a 2*21 array and your rot_mat is 3X3.....you cannot multiply them. YOu need to pick (x,y,z) and multiply this point with rotation matrix to get the point shifted. Check the below pseudo code.....yo can develop your logic with the below example code.
t = 0:0.1:1;
[X,Y,Z] = cylinder((t));
%% Rotation
th = pi/2 ;
Rx = [1 0 0 ; 0 cos(th) -sin(th) ; 0 sin(th) cos(th)] ;
P0 = [X(:) Y(:) Z(:)] ;
P1 = P0*Rx ;
X1 = reshape(P1(:,1),size(X)) ;
Y1 = reshape(P1(:,2),size(X)) ;
Z1 = reshape(P1(:,3),size(X)) ;
figure
hold on
surf(X,Y,Z)
surf(X1,Y1,Z1)
view(3)

How do i obtain a cross section from a 3D volume?

I want to obtain a 2D slice from the 3D volume in the example (slightly modified) How do I resolve this issue with 3D image visualization? as follows:
% create input image
imageSizeX = 10;
imageSizeY = 10;
imageSizeZ = 10
% generate 3D grid using voxel size = 0.5
[Xq, Yq, Zq] = ndgrid(1:0.5:imageSizeX-1, 1:0.5:imageSizeY-1, 1:0.5:imageSizeZ-1);
% obtain coordinates of all internal vertices, faces, and edges
allCoords = [Xq(:), Yq(:), Zq(:)]; % i need this bit for something very important but not shown in the question.
% Re-generate 3D grid using voxel size = 1
[columnsInImage, rowsInImage, pagesInImage] = ndgrid(1: imageSizeX-1, 1: imageSizeY-1, 1: imageSizeZ-1);
% create the sphere in the image.
centerY = imageSizeY/2;
centerX = imageSizeX/2;
centerZ = imageSizeZ/2;
diameter = 4;
radius = diameter/2;
sphereVoxels = flipud((rowsInImage - centerY).^2 ...
+ (columnsInImage - centerX).^2 + (pagesInImage - centerZ).^2 <= radius.^2);
% change image from logical to numeric labels.
Img = double(sphereVoxels);
for ii = 1:numel(Img)
if Img(ii) == 0
Img(ii) = 2; % intermediate phase voxels
end
end
% specify the desired angle
angle = 30;
% specify desired pixel height and width of solid
width = imageSizeX;
height = imageSizeY;
page = imageSizeZ;
% Find the row point at which theta will be created
y = centerY - ( radius*cos(angle * pi/180) )
% determine top of the solid bar
y0 = max(1, y-height);
% label everything from y0 to y to be = 3 (solid)
Img(1:width, y0:y, 1:page)=3;
%%%%%% Plot the surfaces
[X, Y, Z, C] = build_voxels(Img > 0);
hSurface = patch(X, Y, Z, Img(C),...
'AmbientStrength', 0.5, ...
'BackFaceLighting', 'unlit', ...
'EdgeColor', 'none', ...
'FaceLighting', 'flat');
colormap([1 0 0; 1 1 0]);
axis equal;
axis tight;
view(45, 45);
grid on;
xlabel('x-axis (voxels)');
ylabel('y-axis (voxels)');
zlabel('z-axis (voxels)');
light('Position', get(gca, 'CameraPosition'), 'Style', 'local');
zoom on;
hold on;
Vq = griddata(columnsInImage, rowsInImage, pagesInImage, Img, Xq, Yq, Zq);
figure
h1 = slice(permute(Xq, [2 1 3]),permute(Yq, [2 1 3]),permute(Zq, [2 1 3]), Vq, 5,2,5);
When i run the code, i get an Error message:
"The number of data point locations should equal the number of data point values.
Error in griddata>useScatteredInterp (line 188)
F = scatteredInterpolant(inargs{1}(:),inargs{2}(:),inargs{3}(:), ..."
I want to believe this is so because the size of columnsInImage and size of pagesInImage are not equal to size(P,1) and size(P,3), respectively.
Nonetheless, I also tried to use a vector as follows:
figure
h1 = slice(Img(:,1), Img(:,2), Img(:,3), Img, 5,2,5);
I however still end up with the error message:
"Error using griddedInterpolant
The grid was created from grid vectors that were not strictly monotonic increasing.
Error in interp3 (line 142)
F = griddedInterpolant(X, Y, Z, V, method,extrap);"
Please, guys i need suggestions/ideas on how i could remedy these. Many thanks in advance!..

How to find the area of an arbitrary shape contained within a circle using MATLAB

I have an arbitrary shape, of which the exterior boundary has been traced in MATLAB using bwboundaries. Using regionprops, I can calculate the total area enclosed by this shape.
However, I want to know the area for only the parts of the shape that fall within a circle of known radius R centered at coordinates [x1, y1]. What is the best way to accomplish this?
There are a few ways to approach this. One way you could alter the mask before performing bwboundaries (or regionprops) so that it only includes pixels which are within the given circle.
This example assumes that you already have a logical matrix M that you pass to bwboundaries.
function [A, boundaries] = traceWithinCircle(M, x1, y1, R);
%// Get pixel centers
[x,y] = meshgrid(1:size(M, 1), 1:size(M, 2));
%// Compute their distance from x1, y1
distances = sqrt(sum(bsxfun(#minus, [x(:), y(:)], [x1, y1]).^2, 2));
%// Determine which are inside of the circle with radius R
isInside = distances <= R;
%// Set the values outside of this circle in M to zero
%// This will ensure that they are not detected in bwboundaries
M(~isInside) = 0;
%// Now perform bwboundaries on things that are
%// inside the circle AND were 1 in M
boundaries = bwboundaries(M);
%// You can, however, get the area by simply counting the number of 1s in M
A = sum(M(:));
%// Of if you really want to use regionprops on M
%// props = regionprops(M);
%// otherArea = sum([props.Area]);
end
And as an example
%// Load some example data
data = load('mri');
M = data.D(:,:,12) > 60;
%// Trace the boundaries using the method described above
B = traceWithinCircle(M, 70, 90, 50);
%// Display the results
figure;
hax = axes();
him = imagesc(M, 'Parent', hax);
hold(hax, 'on');
colormap gray
axis(hax, 'image');
%// Plot the reference circle
t = linspace(0, 2*pi, 100);
plot(x1 + cos(t)*R, y1 + sin(t)*R);
%// Plot the segmented boundaries
B = bwboundaries(M);
for k = 1:numel(B)
plot(B{k}(:,2), B{k}(:,1), 'r');
end

Multi dimensional (2d better 3d) scatter-plot with different errorbars in matlab

I am trying to program scatterplot with specific errorbars. The only build in function i found is
errorbar()
but this only enables me to make a 2d plot with errorbars in y direction. What i am asking for is a method to plot this with errorbars in x and y direction.
At the end my goal is to make a 3D-scatter-plot with 3 errorbars.
Perfect would be if the resulting image would be a 3d-plot with 3d geometric shapes (coordinate x,y,z with expansion in the dimension proportional to the errorbars) as 'marker'.
I found this page while searching the internet: http://code.izzid.com/2007/08/19/How-to-make-a-3D-plot-with-errorbars-in-matlab.html
But unfortunately they use only one errorbar.
My data is set of 6 arrays each containing either the x,y or z coordinate or the specific standard derivation i want to show as errorbar.
The code you posted looks very easy to adapt to draw all three error bars. Try this (note that I adapted it also so that you can change the shape and colour etc of the plots as you normally would by using varargin, e.g. you can call plot3d_errorbars(...., '.r'):
function [h]=plot3d_errorbars(x, y, z, ex, ey, ez, varargin)
% create the standard 3d scatterplot
hold off;
h=plot3(x, y, z, varargin{:});
% looks better with large points
set(h, 'MarkerSize', 25);
hold on
% now draw the vertical errorbar for each point
for i=1:length(x)
xV = [x(i); x(i)];
yV = [y(i); y(i)];
zV = [z(i); z(i)];
xMin = x(i) + ex(i);
xMax = x(i) - ex(i);
yMin = y(i) + ey(i);
yMax = y(i) - ey(i);
zMin = z(i) + ez(i);
zMax = z(i) - ez(i);
xB = [xMin, xMax];
yB = [yMin, yMax];
zB = [zMin, zMax];
% draw error bars
h=plot3(xV, yV, zB, '-k');
set(h, 'LineWidth', 2);
h=plot3(xB, yV, zV, '-k');
set(h, 'LineWidth', 2);
h=plot3(xV, yB, zV, '-k');
set(h, 'LineWidth', 2);
end
Example of use:
x = [1, 2];
y = [1, 2];
z = [1, 2];
ex = [0.1, 0.1];
ey = [0.1, 0.5];
ez = [0.1, 0.3];
plot3d_errorbars(x, y, z, ex, ey, ez, 'or')

3D body plot in matlab ( volume visualization )

I have little or no experience with volumetric data in MATLAB,
I need to complete next task:
I have 3 vectors ( rows ):
x_ = vector(1:smpl:max_row,1);
y_ = vector(1:smpl:max_row,2);
z_ = vector(1:smpl:max_row,3);
that are samples from large 3 columns array vector with height max_row.
x_ , y_ , z_ are points of 3D figure - surface points of the figure ( volume ). They represent 3D body that should be drawn in matlab.
I created linear grid:
%linear grid
a = -1.1:step:(-1.1+step*(length(x_)-1));
b = -1.1:step:(-1.1+step*(length(y_)-1));
c = -1.1:step:(-1.1+step*(length(z_)-1));
[x,y,z] = meshgrid(-1.1:step:(-1.1+step*(length(x_)-1)));
and also I create array v length(x_)*length(x_)*length(x_) that contains '1' in cells that are of 3D body representation function points and '0' another.
I tryied to make interpolation:
vi = interp3(x,y,z,v,x,y,z,'nearest');
but then vi = v that I've already created.
Now I need to plot the v array on 3D and form 3D body like in
http://www.mathworks.com/help/techdoc/ref/isonormals.html
for example.
I make that next way:
%plotting:
figure
p = patch(isosurface(x,y,z,v,1e-5,'verbose'),'FaceColor','green','EdgeColor','none');
grid on;
isonormals(v,p);
daspect([1 1 1])
view(3);
axis tight;
camproj perspective;
camlight
lighting gouraud
colormap(hsv)
but I get then only small rectangles in place of function '1' that are not connected like in picture that is attached.
I expect solid body enclosed by that points to be plotted.
Does anybody know what is the problem , how to draw 3D body from the x,y,z,v arrays ?
Thanks in advance.
image:
http://imgur.com/Ulegj
Try this, which will be a nice plot (it interpolates a bit though):
x = vector(1:smpl:max_row,1);
y = vector(1:smpl:max_row,2);
z = vector(1:smpl:max_row,3);
% Settings
displaySurface = 1;
displayPoints = 0;
xres = 800; % Resolution, the higher, the smoother
yres = 800;
cm = 'default'; % Colormap
% Axes Limits
xmin = min(x);
ymin = min(y);
xmax = max(x);
ymax = max(y);
xi = linspace(xmin, xmax, xres);
yi = linspace(ymin, ymax, yres);
% Figure
myfig = figure('Position', [200 200 800 600]);
rotate3d off
[XI, YI] = meshgrid(xi, yi);
ZI = griddata(x, y, z, XI, YI, 'cubic');
mesh(XI,YI,ZI);
colormap(cm)
if(displaySurface == 1)
hold on;
surf(XI, YI, ZI, 'EdgeColor', 'none');
end
hold on;
xlabel('x');
ylabel('y');
zlabel('z');
title('Title', 'FontWeight', 'bold');
xlim([xmin xmax])
ylim([ymin ymax])
grid off;
if(displayPoints == 1)
hold on
plot3(x, y, z,'marker','p','markerfacecolor','w','linestyle','none')
hidden off
end