Isosurface from X,Y,Z,V ordered data in Matlab/Octave - matlab

I have a set of 3d data points for each of which the value V of a certain quantity is associated. The data are organized in ordered columns X Y Z V, where the spatial coordinates are distributed on a grid. I have 21 points in every direction, so that the lengths of the several columns is 21x21x21=9261. How can I transform the data into a meshgrid that can be used by isosurface in Octave/Matlab? My code is the following
a=load("data.txt");
X=reshape(a(:,1), 21,21,21);
Y=reshape(a(:,2), 21,21,21);
Z=reshape(a(:,3), 21,21,21);
V=reshape(a(:,2), 21,21,21);
fv=isosurface (X,Y,Z,V,0.9);
patch(fv)
But the result is not meaningful (i get two flat surfaces located at x=0.9 and 1.). The data can be downloaded here.

Here's a way to create a proper meshgrid of your data:
a = load('data.txt');
[X, Y, Z] = meshgrid(unique(a(:, 1)), unique(a(:, 2)), unique(a(:, 3)));
V = zeros(21, 21, 21);
for i = 1:numel(V)
idx = find(a(:, 1) == X(i) & a(:, 2) == Y(i) & a(:, 3) == Z(i));
V(i) = a(idx, 4);
end
fv = isosurface (X,Y,Z,V,0.9);
p = patch(fv);
set(p,'FaceColor','red','EdgeColor','none');
camlight;
lighting gouraud;
xlabel('x');
ylabel('y');
zlabel('z');
If you have much larger data and this is too slow, I can probably come up with a way of reshaping the original data to avoid the for-loop I've used above.

Related

Volumetric 3D data plotting from 2D map in MATLAB?

I have a heat map
and want to convert this 2D matrix to a 3D volume/shape/surface data points for further processing. Not simply display it in 3D using surf.
What would be a good way to do this?
With a lot of help from this community I could come closer:
I shrunk the size to 45x45 px for simplicity.
I = (imread("TESTGREYPLASTIC.bmp"))./2+125;
Iinv = 255-(imread("TESTGREYPLASTIC.bmp"))./2-80;%
for i = 1:45
for j = 1:45
A(i, j, I(i,j) ) = 1;
A(i, j, Iinv(i,j) ) = 1;
end
end
volshow(A)
Its not ideal but the matrix is what I wanted now. Maybe the loop can be improved to run faster when dealing with 1200x1200 points.
How do I create a real closed surface now?
Following your conversation with #BoilermakerRV, I guess you are looking for one of the following two results:
A list of 3d points, where x and y are index of pixels in the image, and z is value of corresponding pixels. The result will be an m*n by 3 matrix.
An m by n by 256 volume of zeros and ones, that for (i,j)-th pixel in the image, all voxels of the (i, j)-the pile of the volume are 0, except the one at I(i, j).
Take a look at the following example that generates both results:
close all; clc; clear variables;
I = rgb2gray(imread('data2.png'));
imshow(I), title('Data as image')
% generating mesh grid
[m, n] = size(I);
[X, Y] = meshgrid(1:n, 1:m);
% converting image to list of 3-d points
P = [Y(:), X(:), I(:)];
figure
scatter3(P(:, 1), P(:, 2), P(:, 3), 3, P(:, 3), '.')
colormap jet
title('Same data as a list of points in R^3')
% converting image to 256 layers of voxels
ind = sub2ind([m n 256], Y(:), X(:), I(:));
V = zeros(m, n, 256);
V(ind) = 1.0;
figure
h = slice(V, [250], [250], [71]) ;
[h.EdgeColor] = deal('none');
colormap winter
camlight
title('And finally, as a matrix of 0/1 voxels')
The contour plot that is shown can't be generated with "2D" data. It requires three inputs as follows:
[XGrid,YGrid] = meshgrid(-4:.1:4,-4:.1:4);
C = peaks(XGrid,YGrid);
contourf(XGrid,YGrid,C,'LevelStep',0.1,'LineStyle','none')
colormap('gray')
axis equal
Where XGrid, YGrid and C are all NxN matrices defining the X values, Y values and Z values for every point, respectively.
If you want this to be "3D", simply use surf:
surf(XGrid,YGrid,C)

Rotating and working on MATLAB 3D objects

How do you rotate 3D objects around each of the three axes when coordinates are of MATLAB style (X, Y and Z kept in different arrays).
This code is a start. I think I have found the rotation matrix (for pi/2 rotation around x), here it is called rotX90_2. But how should rotX90_2 operate on X, Y, Z?
[X,Y,Z] = cylinder;
% rotX90_1 = makehgtform('xrotate',pi/2) gives
rotX90_1 = ...
[1 0 0 0;
0 0 -1 0;
0 1 0 0;
0 0 0 1];
rotX90_2 = rotX90_1(1:3, 1:3);
% Here rotX90_2 should operate on [X,Y,Z] in order to ...
% rotate it 90 degrees around x, but how is this done?
% == What code should be put here to rotate the cylinder? ==
surf(X,Y,Z);
I have just started using MATLAB. As I understand, the basic ways to manipulate 3D graphics are to either operate on the X, Y, Z, like here or, you can first run graphics routines like h = surf(X, Y, Z); and then operate on graphics objects, using f.ex. hgtransform.
It is convenient to translate and scale using X, Y, Z. - You just add and multiply by scalars. But I ask this question to understand how to rotate.
If you operate on the graphic objects, on the other hand, you can use the function hgtransform. But you must then first create other objects, since hgtransform does not operate directly on the graphic objects, as I understand. (Except functions like rotatex(h, angle). F.ex, I have not found a corresponding "translatex(h, distance)". That surprised me. Maybe I didn't look well enough.)
OK I am new to this. Any simple, practical pointers how to easily rotate, scale and translate MATLAB 3D coordinates/objects (around the coordinate system axes) would be appreciated.
Edit:
According to Prakhar's answer below, which works, the code needed to fill the gap above is the following. Thank you, Prakhar.
[row, col] = size(X);
coordinates = [reshape(X, [row*col, 1]), reshape(Y, [row*col, 1]), reshape(Z, [row*col, 1])];
rC = coordinates * rotX90_2;
X = reshape(rC(:, 1), [row, col]);
Y = reshape(rC(:, 2), [row, col]);
Z = reshape(rC(:, 3), [row, col]);
Let's say R is the appropriate 3x3 rotation matrix.
coordinates = [X Y Z];
rotatedCoordinates = coordinates * R;
(Assuming X, Y, and Z are column vectors of same size)
Now, you can get the new X, Y, and Z coordinates from rotatedCoordinates as rotatedCoordinates(:, 1), rotatedCoordinates(:, 2), and rotatedCoordinates(:, 3), respectively.
EDIT: Another alternative when X, Y, Z are 2D matrices:
[X, Y, Z] = cylinder;
[row, col] = size(X);
coordinates = [reshape(X, [row*col, 1]), reshape(Y, [row*col, 1]), reshape(Z, [row*col, 1])];
rC = coordinates*R;
Xn = reshape(rC(:, 1), [row, col]);
Yn = reshape(rC(:, 2), [row, col]);
Zn = reshape(rC(:, 3), [row, col]);

Matlab animation of several points simultaneously

I am trying to simulate the trajectories of a few particles in 2D on Matlab. I have the x- and y- coordinates of these particles as a function of time, which I store as matrix x and y. The column in both x and y corresponds to the time, while the row corresponds to the particle number: 1, 2, etc.
I know how to do the animation for one particle with pause, but I am not sure how to customize the code for multiple particles' trajectories. Basically, my idea is that on the initial plot, I have 3 markers which correspond to the initial position of the particles, say particle A, B and C. Then, I would like to follow the movement of these 3 markers, and here is where I encountered the problem: I don't know how to sort the subsequent points according to the particle identity. For example, I want to specify the first point I plot in the second time point as particle A, second point as particle B and third point in particle C.
I have done something similar to this, but in my simulation, the number of particles may be 100, which makes it impractical to create x1, x2, ..., x100, y1, y2, ..., y100 for the animation to work:
y = rand(3, 20); % Generate random sample data.
x = rand(size(y, 1), size(y, 2));
% Now we have x and y sample data and we can begin.
% Extract into separate arrays
x1 = sort(x(1,:));
x2 = sort(x(2,:));
x3 = sort(x(3,:));
y1 = y(1,:);
y2 = y(2,:);
y3 = y(3,:);
for k = 1 : length(x1)
plot(x1(1:k), y1(1:k), 'r*-', 'LineWidth', 2);
xlim([min(x(:)), max(x(:))]);
ylim([min(y(:)), max(y(:))]);
grid on;
hold on;
plot(x2(1:k), y2(1:k), 'g*-', 'LineWidth', 2);
plot(x3(1:k), y3(1:k), 'b*-', 'LineWidth', 2);
hold off;
fprintf('Plotted points 1 through %d\n', k);
pause(0.8);
end
Any ideas or suggestions will be greatly appreciated!
In order to plot all graphs at once, we might make an 2D array.
Below is an example.
y = rand(3, 20); % Generate random sample data.
x = rand(size(y, 1), size(y, 2));
% Now we have x and y sample data and we can begin.
% Extract into separate arrays
x = sort(x');
y=y';
M=size(x);
N=M(2);
for k = 1 : length(x)
if k==1;
zeroPad=zeros(1,N);
x0=[zeroPad;x(1,1:N)];
y0=[zeroPad;y(1,1:N)];
plot(x0(1:2,1:N), y0(1:2,1:N), '*', 'LineWidth', 2);
else
plot(x(1:k,1:N), y(1:k,1:N), '*-', 'LineWidth', 2);
end
xlim([min(x(:)), max(x(:))]);
ylim([min(y(:)), max(y(:))]);
grid on;
fprintf('Plotted points 1 through %d\n', k);
pause(0.8);
end
One trick was added.
At the first iteration, I added zeros before x and y.
Some unnecessary codes were removed.

Generate heatmap with coordinates and data stored in vectors

Let A be an n by 3 matrix, such that the first two columns are all ordered pairs of the form (5*i,5*i) for i from 1 to 200. The third column contains values from 0 to 1, which I will call intensities. I want to make a 1000 by 1000 plot so that the rectangle at (5*i,5*i) is shaded with intensity described by the third column entry.
I'm familiar with the heatmap function and imshow, but I don't see a way to include this "scaling by 5" to make a nice plot. And of course in general the x and y coordinates may not be scaled by the same amount.
Is there a nice way to do this in Matlab?
With imagesc it's actually pretty simple:
First some example data:
%// generate example data
ii = 1:200;
[xx,yy] = meshgrid(ii);
A(:,1) = 5*xx(:);
A(:,2) = 5*yy(:);
A(:,3) = randi([0,1],1,40000);
Actual answer
n = 200;
%// reshape data
D = reshape( A(:,3),n,n );
%// heatmap
imagesc(A(:,1),A(:,2),D)
colormap(gray)
caxis([0,1])
gives:
Important notice
If your coordinates are not sorted as required for imagesc you can sort them with:
A = sortrows(A,[2,1]);
Clown Example
%// original image
load clown
I = reshape(1:numel(X),size(X));
[R,C] = ind2sub(size(X),I);
A(:,1) = R(:);
A(:,2) = C(:);
A(:,3) = X(:);
D = reshape( A(:,3),200,320 );
figure(1)
subplot(1,3,1)
imagesc(A(:,1),A(:,2),D)
%// shuffled image -> shuffled data
shuffle = randperm(320*200);
A = A(shuffle,:);
D = reshape( A(:,3),200,320 );
subplot(1,3,2)
imagesc(A(:,1),A(:,2),D)
%// sorted image
A = sortrows(A,[2,1]);
D = reshape( A(:,3),200,320 );
subplot(1,3,3)
imagesc(A(:,1),A(:,2),D)
You see, even if your coordinates are sorted like a mess, you can rebuild the image with sortrows.
See this
function DrawHeatmap(X,Y,Z)
%DRAWHEATMAP Draw a 2D heatmap for (X,Y) coordinates whose values are in Z
% X, Y , Z must be columns
% By: Eng. Osama Talaat Abdel-Hafiz - PhD Student
% Egypt - Sept 2017
if size(X,2)==1 && size(Y,2)==1 && size(Z,2)==1
F = scatteredInterpolant(X,Y,Z); % create a function from interpolation
[X,Y] = meshgrid(min(X):0.1:max(X),min(Y):0.1:max(Y));
Z = F(X,Y);
contourf(X, Y, Z, linspace(floor(min(min(Z))),ceil(max(max(Z))),400), 'LineColor','none')
colorbar;
else
error('X, Y , Z must be columns')
end
end

Plotting MATLAB values in two different colors

Anyway, I wish to plot two column vectors, filled with random numbers with no negative values in them, on a 2D plot(x and y).
The 'x-vector' I can leave as it is, but with the 'y vector', I wish to plot that any y values that is equal to zero as a different color(Say red) to the other positive non-zero values(Say blue).
Please try to keep solution relatively simple, if possible, as I myself am relatively new to MATLAB as well as to this site.
I'm not sure what you mean by 2D plot but I assuming you mean just a normal curve. Try this:
x = rand(10, 1);
y = rand(10, 1);
y([5 8]) = 0; %Force a couple of values to be 0 for visualisation
t = 1:10;
plot(t, x);
hold on
plot(t, y, 'g');
ind = y == 0; %Make a logical index that masks all the value where y is not 0
plot(t(ind), y(ind), 'r*');