How to plot a surface with a texture map - matlab

I want to plot a surface with a texture map on it, but the conditions are not the "ideal" ones.
first lets explain what I have.
I have a set of points (~7000) that are image coordinates, in a grid. This points do NOT define perfect squares. IT IS NOT A MESHGRID. For the sake of the question, lets assume that we have 9 points. Lets ilustrate what we have with an image:
X=[310,270,330,430,410,400,480,500,520]
Y=[300,400,500,300,400,500,300,400,500]
Lets say we can get the "structure" of the grid, so
size1=3;
size2=3;
points=zeros(size1,size2,2)
X=[310,270,330;
430,410,400;
480,500,520]
Y=[300,400,500;
300,400,500;
300,400,500]
points(:,:,1)=X;
points(:,:,2)=Y;
And now lets say we have a 3rd dimension, Z.
EDIT: Forgot to add a piece if info. I triangulate the points in the image and get a 3D correspondence, so when displayed in a surface they don't have the X and Y coords of the image, for a simplification of the given data lets say X=X/2 Y=Y/3
And we have:
points=zeros(size1,size2,3)
Z=[300,330,340;
300,310,330;
290,300,300]
surf(points(:,:,1)/2,points(:,:,2)/3,points(:,:,3))
What I want is to plot the surface in 3D with the image texture. Each element should have the texture piece that have in the first image.
This needs to work for huge datasheets. I don't specially need it to be fast.
related post (but I has a meshgrid as initial set of points) : Texture map for a 2D grid
PD: I can post original images + real data if needed, just posted this because i think it is easier with small data.

You can use the texturemap property of surf which works with rectangular meshes as well as with non-rectangular ones.
Creating non-rectangular data points
% creating non-rectangular data points
[X, Y] = meshgrid(1:100, 1:100);
X = X+rand(size(X))*5;
Y = Y+rand(size(X))*5;
which results in the following data points:
Generating height data:
Z = sin(X/max(X(:))*2*pi).*sin(Y/max(Y(:))*2*pi);
Loading picture:
[imageTest]=imread('peppers.png');
and mapping it as texture to the mesh:
surf(X,Y,Z, imageTest, ...
'edgecolor', 'none','FaceColor','texturemap')
Note that, for the sake of demonstration, this non-rectangular grid is quite sparsely populated which results in a rather jagged texture. With more points, the result gets much better, irrespective of the distortion of the grid points.
Note also that the number of grid points does not have to match the number of pixels in the texture image.
~edit~
If X and Y coordinates are only available for parts of the image, you can adjust the texture accordingly by
minX = round(min(X(:)));
maxX = round(max(X(:)));
minY = round(min(Y(:)));
maxY = round(max(Y(:)));
surf(X,Y,Z, imageTest(minX:maxX, minY:maxY, :), ...
'edgecolor', 'none','FaceColor','texturemap')

I don't think you can do what you want with Matlab's built in commands and features. But using the technique from my other answer with a high-res version of the grid can do it for you.
By "high-res", I mean an interpolated version of the non-uniform grid with denser data points. That is used to sample the texture at denser data points so it can be drawn using the texturemap feature of surf. You can't use a normal 2D interpolation, however, because you need to preserve the non-uniform grid shape. This is what I came up with:
function g = nonUniformGridInterp2(g, sx, sy)
[a,b] = size(g);
g = interp1(linspace(0,1,a), g, linspace(0,1,sy)); % interp columns
g = interp1(linspace(0,1,b), g', linspace(0,1,sx))'; % interp rows
Note that you have to call this twice to interpolate the X and Y points independently. Here's an example of the original grid and an interpolated version with 10 points in each direction.
Here's how to use that high-res grid with interp2 and texturemap.
function nonUniformTextureMap
% define the non-uniform surface grid
X = [310,270,330; 430,410,400; 480,500,520];
Y = [300,400,500; 300,400,500; 300,400,500];
Z = [300,330,340; 300,310,330; 290,300,300];
% get texture data
load penny % loads data in variable P
% define texture grid based on image size
% note: using 250-550 so that a,b covers the range used by X,Y
[m,n] = size(P);
[a,b] = meshgrid(linspace(250,550,n), linspace(250,550,m));
% get a high-res version of the non-uniform grid
s = 200; % number of samples in each direction
X2 = nonUniformGridInterp2(X, s, s);
Y2 = nonUniformGridInterp2(Y, s, s);
% sample (map) the texture on the non-uniform grid
C = interp2(a, b, P, X2, Y2);
% plot the original and high-res grid
figure
plot(X(:),Y(:),'o',X2(:),Y2(:),'.')
legend('original','high-res')
% plot the surface using sampled points for color
figure
surf(X, Y, Z, C, 'edgecolor', 'none', 'FaceColor','texturemap')
colormap gray

I'm not sure I understand your question, but I think that what you need to do is sample (map) the texture at your grid's X,Y points. Then you can simply plot the surface and use those samples as colors.
Here's an example using the data you gave in your question. It doesn't look like much, but using more X,Y,Z points should give the result you're after.
% define the non-uniform surface grid
X = [310,270,330; 430,410,400; 480,500,520];
Y = [300,400,500; 300,400,500; 300,400,500];
Z = [300,330,340; 300,310,330; 290,300,300];
% get texture data
load penny % loads data in variable P
% define texture grid based on image size
% note: using 600 so that a,b covers the range used by X,Y
[m,n] = size(P);
[a,b] = meshgrid(linspace(0,600,n), linspace(0,600,m));
% sample (map) the texture on the non-uniform grid
C = interp2(a, b, P, X, Y);
% plot the surface using sampled points for color
figure
surf(X, Y, Z, C)
colormap gray

Related

Creating meshgrid of scattered Cartesian data for plotting on a sphere

I have a set of n=8000 cartesian coordinates X,Y and Z as vectors and also a vector V of same size which I want to use as values to create a heatmap on a sphere.
I saw this link (visualization of scattered data over a sphere surface MATLAB), but I don't understand how I convert this set of data into a meshgrid for plotting using surf.
Almost every example I saw uses meshgrids.
Right now, I am doing by plotting a sphere and then use scatter3 to plot my points as big balls and try to smooth them later. I looks like this:
I would like to get the figure as the plotting of the example in that link, where he uses:
k = 5;
n = 2^k-1;
[x,y,z] = sphere(n);
c = hadamard(2^k);
surf(x,y,z,c);
colormap([1 1 0; 0 1 1])
axis equal
EDIT:
(Sorry for taking so long to reply, the corona crises kept away from work)
What I am actually doing is:
for i=1:numel(pop0n)
ori(i,:)=ori(i,:)/norm(ori(i,:));
end
x = ori(:,1);
y = ori(:,2);
z = ori(:,3);
%// plot
m=100;
[aa,bb,cc] = sphere(m);
surf(aa,bb,cc,ones(m+1,m+1)*min(pop0n))
hold on
colormap jet;
scatter3(x,y,z,400,pop0n/norm(pop0n),'filled');
colorbar
shading interp
The array 'ori' is 8000x3, and contains the x, y and z coordinates of the points I want to plot and pop0n is a 8000 sized vector with the intensities of each coordinate.
My main question is how do I transform my x, y, z and pop0n, that are vectors, into 2D arrays (meshgrid) to use surf?
Because I cannot simply do surf(x,y,z,pop0n) if they are vectors.
Thanks in advance
As David suggested, griddata does the job.
What I did was:
for i=1:numel(pop0n)
ori(i,:)=ori(i,:)/norm(ori(i,:));
end
x = ori(:,1);
y = ori(:,2);
z = ori(:,3);
%// plot
m=100;
[aa,bb,cc] = sphere(m);
v = griddata(x,y,z,pop0n,aa,bb,cc,'nearest');
surf(aa,bb,cc,v)
colormap jet;
colorbar
shading interp

MATLAB: Create a colored surface based on scatter points

Using a dataset of the format [x,y,z,value], I can create a 3D scatter plot as shown in the images, where the color of each point at location (x,y,z) is based on value. Note that all of the images are of the same plot, just from different views. It is intended to be the surface of an octant of a sphere.
Is there a way I can do an interpolation of the colors in 3D such that we see a solid surface instead of individual points? I am looking for something along the lines of imagesc, but in 3D. I've tried a wide variety of functions, including scatteredInterpolant, patch, mesh, and surf, but those do not seem to work in 3 dimensions the way I'd like.
Based on your example data (with multiple shells), I put this program together. It uses trisurf so it doesn't need meshed data.
function PlotColoredSpheres(x,y,z)
tol = 0.01;
r = sqrt(x.^2 + y.^2 + z.^2);
data = num2cell(sortrows([r, x, y, z], [1,2,3,4]),1);
[R, X, Y, Z] = data{:};
figure(1)
RUnique = uniquetol(r, tol);
for Ru = RUnique(:)'
j = abs(R - Ru) < tol;
tri = delaunay(X(j),Y(j));
trisurf(tri, X(j), Y(j), Z(j));
lighting phong
shading interp
hold on
end
hold off
end

How to create a 2d color map plot from x and y coordinates and a vector of values?

I'm trying to plot a 2 dimensional signal on a specific plane in a 3d model. I have the matrix:
xyzp (nx3)
that contains all the points which are closest to the plane (e.g. when the plane is in the z direction, all the z coordinates are fairly similar).
and I have a vector:
signal (nx1)
that contains a value for each point in xyzp.
when I use:
"surf([xyzp(:,[1,2]),signal)" or "mesh([xyzp(:,[1,2]),signal])"
The plot I get doesn't look at all like the intersection of the plane with the model from any angle (I expected "view(2)" to show the signal in the Z direction), so I assume I didn't use the plot function correctly.
Can anyone show me an example? For instance - A circle on an xy plane with some random signal indicated by color
surf and mesh can be used when the points form a rectangular grid on the xy plane.
In the general case (points are arbitrarily placed), you can use scatter3. For purposes of illustration, consider the following example xyzp and signal:
[x y] = ndgrid(-1:.01:1);
x = x+.3*y; %// example values which do not form a rectangular grid
z = x+y; %// example z as a function of x, y
xyzp = [x(:) y(:) z(:)];
signal = z(:)+x(:)-y(:); %// example values
Then
scatter3(xyzp(:,1), xyzp(:,2), xyzp(:,3), 1, signal, '.');
produces the following figure.
Since scatter3 plots each point separately, the picture is not as smooth as it would be with surf. But this seems hard to improve if the coordinates do not a have any "structure" (as surf requires) .

Matlab - Trying to use vectors with grid coordinates and value at each point for a color plot

I'm trying to make a color plot in matlab using output data from another program. What I have are 3 vectors indicating the x-position, y-yposition (both in milliarcseconds, since this represents an image of the surroundings of a black hole), and value (which will be assigned a color) of every point in the desired image. I apparently can't use pcolor, because the values which indicate the color of each "pixel" are not in a matrix, and I don't know a way other than meshgrid to create a matrix out of the vectors, which didn't work due to the size of the vectors.
Thanks in advance for any help, I may not be able to reply immediately.
If we make no assumptions about the arrangement of the x,y coordinates (i.e. non-monotonic) and the sparsity of the data samples, the best way to get a nice image out of your vectors is to use TriScatteredInterp. Here is an example:
% samplesToGrid.m
function [vi,xi,yi] = samplesToGrid(x,y,v)
F = TriScatteredInterp(x,y,v);
[yi,xi] = ndgrid(min(y(:)):max(y(:)), min(x(:)):max(x(:)));
vi = F(xi,yi);
Here's an example of taking 500 "pixel" samples on a 100x100 grid and building a full image:
% exampleSparsePeakSamples.m
x = randi(100,[500 1]); y = randi(100,[500 1]);
v = exp(-(x-50).^2/50) .* exp(-(y-50).^2/50) + 1e-2*randn(size(x));
vi = samplesToGrid(x,y,v);
imagesc(vi); axis image
Gordon's answer will work if the coordinates are integer-valued, but the image will be spare.
You can assign your values to a matrix based on the x and y coordinates and then use imagesc (or a similar function).
% Assuming the X and Y coords start at 1
max_x = max(Xcoords);
max_y = max(Ycoords);
data = nan(max_y, max_x); % Note the order of y and x
indexes = sub2ind(size(data), max_y, max_x);
data(indexes) = Values;
imagesc(data); % note that NaN values will be colored with the minimum colormap value

Interpolating along the 2-D image slices

I have a set of 100 2-D image slices of the same size. I have used MATLAB to stack them to create a volumetric data. While the size of the 2-D slices is 480x488 pixels, the direction in which the images are stacked is not wide enough to visualize the volume in different orientation when projected. I need to interpolate along the slices to increase the size for visualization.
Can somebody please give me an idea or tip about how to do it?
Edit: Anotated projected microscopy-images
The figure 1 is the top-view of the projected volume.
The figure 2 is the side-view of the projected volume.
When I change the rotation-angle, and try to visualize the volume in different orientation, e.g. side-view (figure 2), is what I see as in figure 2.
I want to expand the side view by interpolating along the image slices.
Here is an adapted example from the MATLAB documentation on how to visualize volumetric data (similar to yours) using isosurfaces:
%# load MRI dataset: 27 slices of 128x128 images
load mri
D = squeeze(D); %# 27 2D-images
%# view slices as countours
contourslice(D,[],[],1:size(D,3))
colormap(map), view(3), axis tight
%# apply isosurface
figure
%#D = smooth3(D);
p = patch( isosurface(D,5) );
isonormals(D, p);
set(p, 'FaceColor',[1,.75,.65], 'EdgeColor','none')
daspect([1 1 .5]), view(3), axis tight, axis vis3d
camlight, lighting gouraud
%# add isocaps
patch(isocaps(D,5), 'FaceColor','interp', 'EdgeColor','none');
colormap(map)
MATLAB has a function interp3 that can be used for interpolation, assuming that the data is uniformly discretised.
Check out the documentation.
Hope this helps.
EDIT: The MATLAB function interp3 works as follows:
vi = interp3(x, y, z, v, xi, yi, zi);
I assume that your "stack" of slices defines the arrays x, y, z, v as 3D arrays, where x, y are the coordinates of the pixels in the plane, z is the "height" of each slice and v is the actual image slices, maybe as "intensity" values for the pixels.
If you want to interpolate new image slices at intermediate z values you could specify these levels in the zi array. The arrays xi, yi would again represent the coordinates of the pixels in the plane.
I created a function to interpolate along image slices. Below is the code:
function res = interp_along_slices( vol, scale )
% Interpolation along the image slices
% Get the size of the volume
[r c p] = size(vol);
% Pre-allocate the array:
% the third dimension is scale times the p
vol_interp = zeros(r,c,scale*p);
% interpolate along the image slices
for inr = 1:r;
for jnr = 1:c;
xi = vol(inr,jnr,:);
vol_interp(inr,jnr,:) = interp(xi, scale);
end;
end;
res = vol_interp;
end