How can I smooth a surface in MATLAB? - matlab

I have a surface in matlab which is plotted using the following code:
[xi, yi] = meshgrid(S/K, days);
vq = griddata(S/K, days, rbf/K, xi, yi,'natural');
mesh(xi,yi,vq)
The resulting image is quite rough, and has lots of grid lines as there are roughly 200 data points in each vector. Is it possible to plot a mesh which has a smaller number of grid points (e.g. 20) which averages out an existing meshgrid, griddata surface?

One option is to use conv2 on your vq data to smooth, then downsample as #Ander suggested:
n = 5; % averaging size
vq_2 = conv2(vq, ones(n)/n.^2,'same');
mesh(xi(1:20:end,1:20,end),yi(1:20:end,1:20,end),vq_2(1:20:end,1:20,end))
There will be a bit of an edge effect as by default conv2 pads with zeros.

Related

Matlab how to make smooth contour plot?

I want to represent data with 2 variables in 2D format. The value is represented by color and the 2 variables as the 2 axis. I am using the contourf function to plot my data:
clc; clear;
load('dataM.mat')
cMap=jet(256); %set the colomap using the "jet" scale
F2=figure(1);
[c,h]=contourf(xrow,ycol,BDmatrix,50);
set(h, 'edgecolor','none');
xlim([0.0352 0.3872]);
ylim([0.0352 0.3872]);
colormap(cMap);
cb=colorbar;
caxis([0.7 0.96]);
% box on;
hold on;
Both xrow and ycol are 6x6 matrices representing the coordinates. BDmatrix is the 6x6 matrix representing the corresponding data. However, what I get is this:
The following is the xrow and yrow matices:
The following is the BDmatrix matices:
Would it be possible for the contour color to vary smoothly rather than appearing as straight lines joining the data points? The problem of this figure is the coarse-granularity which is not appealing. I have tried to replace contourf with imagec but it seems not working. I am using MATLAB R2015b.
You can interpolate your data.
newpoints = 100;
[xq,yq] = meshgrid(...
linspace(min(min(xrow,[],2)),max(max(xrow,[],2)),newpoints ),...
linspace(min(min(ycol,[],1)),max(max(ycol,[],1)),newpoints )...
);
BDmatrixq = interp2(xrow,ycol,BDmatrix,xq,yq,'cubic');
[c,h]=contourf(xq,yq,BDmatrixq);
Choose the "smoothness" of the new plot via the parameter newpoints.
To reduce the Color edges, you can increase the number of value-steps. By default this is 10. The following code increases the number of value-steps to 50:
[c,h]=contourf(xq,yq,BDmatrixq,50);
A 3D-surf plot would be more suitable for very smooth color-shading. Just rotate it to a top-down view. The surf plot is also much faster than the contour plot with a lot of value-steps.
f = figure;
ax = axes('Parent',f);
h = surf(xq,yq,BDmatrixq,'Parent',ax);
set(h, 'edgecolor','none');
view(ax,[0,90]);
colormap(Jet);
colorbar;
Note 1: Cubic interpolation is not shape-preserving. That means, the interpolated shape can have maxima which are greater than the maximum values of the original BDmatrix (and minima which are less). If BDmatrix has noisy values, the interpolation might be bad.
Note 2: If you generated xrow and yrow by yourself (and know the limits), than you do not need that min-max-extraction what I did.
Note 3: After adding screenshots of your data matrices to your original posting, one can see, that xrow and ycol come from an ndgrid generator. So we also must use this here in order to be consistent. Since interp2 needs meshgrid we have to switch to griddedInterpolant:
[xq,yq] = ndgrid(...
linspace(min(min(xrow,[],1)),max(max(xrow,[],1)),newpoints ),...
linspace(min(min(ycol,[],2)),max(max(ycol,[],2)),newpoints )...
);
F = griddedInterpolant(xrow,ycol,BDmatrix,'cubic');
BDmatrixq = F(xq,yq);

Undesirable results while interpolating

I'm trying to create a surface plot from (x,y,z) data on an irregular grid. The datasets are located along diagonal lines of positive gradient in the (x,y) plane. The method is illustrated below
xi = linspace (min(x), max(x), 1000);
yi = linspace (min(y), max(y), 1000);
zi = linspace (min(z), max(z), 400);
[XI YI]=meshgrid(xi,yi);
F = TriScatteredInterp (x,y,z);
Vi = F(XI,YI);
surf(Xi,Yi,Vi);
shading interp;
view(2)
An example result is shown below:
The data should be smooth in the x direction (so at y=860 there should be a single, continuous blue dip). I think the problem is that the interpolation is being carried out well along each dataset but poorly between them as the spacing between the datasets is far larger than between points within a dataset.
What would be the best way to deal with this?

Plot a grid of Gaussians with Matlab

With the following code I'm able to draw the plot of a single 2D-Gaussian function:
x=linspace(-3,3,1000);
y=x';
[X,Y]=meshgrid(x,y);
z=exp(-(X.^2+Y.^2)/2);
surf(x,y,z);shading interp
This is the produced plot:
However, I'd like to plot a grid having a specified number x of these 2D-Gaussians.
Think of the following picture as an above view of the plot I'd like to produce (where in particular the grid is made of 5x5 2D-Gaussians). Each Gaussian should be weighed by a coefficient such that if it's negative the Gaussian is pointing towards negative values of the z axis (black points in the grid below) and if it's positive it's as in the above image (white points in the grid below).
Let me provide some mathematical details. The grid corresponds to a mixture of 2D-Gaussians summed as in the following equation:
In which each Gaussian has its own mean and deviation.
Note that each Gaussian of the mixture should be put in a determined (X,Y) coordinate, in such a way that they are equally distant from each other. e.g think of the central Gaussian in (0,0) then the other ones should be in (-1,1) (0,1) (1,1) (-1,0) (1,0) (-1,-1) (0,-1) (1,-1) in the case of a grid with dimension 3x3.
Can you provide me (and explain to me) how can I do such a plot?
Thanks in advance for the help.
Indeed you said yourself, put (as an example just for the means)
[X,Y]=meshgrid(x,y); % //mesh
g_centers = -3:3;
[x_g,y_g] = meshgrid(g_centers,g_centers); % //grid of centers (coarser)
mu = [x_g(:) , y_g(:)]; % // mesh of centers in column
z = zeros(size(X));
for i = 1:size(mu,1)
z= z + exp(-((X-mu(i,1)).^2+(Y-mu(i,2)).^2)/( 2* .001) );
end
surf(X,Y,z);shading interp

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

Representing three variables in a three dimension plot

I have a problem dealing with 3rd dimension plot for three variables.
I have three matrices: Temperature, Humidity and Power. During one year, at every hour, each one of the above were measured. So, we have for each matrix 365*24 = 8760 points. Then, one average point is taken every day. So,
Tavg = 365 X 1
Havg = 365 X 1
Pavg = 365 X 1
In electrical point of veiw, the power depends on the temperature and humidity. I want to discover this relation using a three dimensional plot.
I tried using mesh, meshz, surf, plot3, and many other commands in MATLAB but unfortunately I couldn't get what I want. For example, let us take first 10 days. Here, every day is represented by average temperature, average humidity and average power.
Tavg = [18.6275
17.7386
15.4330
15.4404
16.4487
17.4735
19.4582
20.6670
19.8246
16.4810];
Havg = [75.7105
65.0892
40.7025
45.5119
47.9225
62.8814
48.1127
62.1248
73.0119
60.4168];
Pavg = [13.0921
13.7083
13.4703
13.7500
13.7023
10.6311
13.5000
12.6250
13.7083
12.9286];
How do I represent these matrices by three dimension plot?
The challenge is that the 3-D surface plotting functions (mesh, surf, etc.) are looking for a 2-D matrix of z values. So to use them you need to construct such a matrix from the data.
Currently the data is sea of points in 3-D space, so, you have to map these points to a surface. A simple approach to this is to divide up the X-Y (temperature-humidity) plane into bins and then take the average of all of the Z (power) data. Here is some sample code for this that uses accumarray() to compute the averages for each bin:
% Specify bin sizes
Tbin = 3;
Hbin = 20;
% Create binned average array
% First create a two column array of bin indexes to use as subscripts
subs = [round(Havg/Hbin)+1, round(Tavg/Tbin)+1];
% Now create the Z (power) estimate as the average value in each bin
Pest = accumarray(subs,Pavg,[],#mean);
% And the corresponding X (temp) & Y (humidity) vectors
Tval = Tbin/2:Tbin:size(Pest,2)*Tbin;
Hval = Hbin/2:Hbin:size(Pest,1)*Hbin;
% And create the plot
figure(1)
surf(Tval, Hval, Pest)
xlabel('Temperature')
ylabel('Humidity')
zlabel('Power')
title('Simple binned average')
xlim([14 24])
ylim([40 80])
The graph is a bit coarse (can't post image yet, since I am new) because we only have a few data points. We can enhance the visualization by removing any empty bins by setting their value to NaN. Also the binning approach hides any variation in the Z (power) data so we can also overlay the orgional point cloud using plot3 without drawing connecting lines. (Again no image b/c I am new)
Additional code for the final plot:
%% Expanded Plot
% Remove zeros (useful with enough valid data)
%Pest(Pest == 0) = NaN;
% First the original points
figure(2)
plot3(Tavg, Havg, Pavg, '.')
hold on
% And now our estimate
% The use of 'FaceColor' 'Interp' uses colors that "bleed" down the face
% rather than only coloring the faces away from the origin
surfc(Tval, Hval, Pest, 'FaceColor', 'Interp')
% Make this plot semi-transparent to see the original dots anb back side
alpha(0.5)
xlabel('Temperature')
ylabel('Humidity')
zlabel('Power')
grid on
title('Nicer binned average')
xlim([14 24])
ylim([40 80])
I think you're asking for a surface fit for your data. The Curve Fitting Toolbox handles this nicely:
% Fit model to data.
ft = fittype( 'poly11' );
fitresult = fit( [Tavg, Havg], Pavg, ft);
% Plot fit with data.
plot( fitresult, [xData, yData], zData );
legend( 'fit 1', 'Pavg vs. Tavg, Havg', 'Location', 'NorthEast' );
xlabel( 'Tavg' );
ylabel( 'Havg' );
zlabel( 'Pavg' );
grid on
If you don't have the Curve Fitting Toolbox, you can use the backslash operator:
% Find the coefficients.
const = ones(size(Tavg));
coeff = [Tavg Havg const] \ Pavg;
% Plot the original data points
clf
plot3(Tavg,Havg,Pavg,'r.','MarkerSize',20);
hold on
% Plot the surface.
[xx, yy] = meshgrid( ...
linspace(min(Tavg),max(Tavg)) , ...
linspace(min(Havg),max(Havg)) );
zz = coeff(1) * xx + coeff(2) * yy + coeff(3);
surf(xx,yy,zz)
title(sprintf('z=(%f)*x+(%f)*y+(%f)',coeff))
grid on
axis tight
Both of these fit a linear polynomial surface, i.e. a plane, but you'll probably want to use something more complicated. Both of these techniques can be adapted to this situation. There's more information on this subject at mathworks.com: How can I determine the equation of the best-fit line, plane, or N-D surface using MATLAB?.
You might want to look at Delaunay triangulation:
tri = delaunay(Tavg, Havg);
trisurf(tri, Tavg, Havg, Pavg);
Using your example data, this code generates an interesting 'surface'. But I believe this is another way of doing what you want.
You might also try the GridFit tool by John D'Errico from MATLAB Central. This tool produces a surface similar to interpolating between the data points (as is done by MATLAB's griddata) but with cleaner results because it smooths the resulting surface. Conceptually multiple datapoints for nearby or overlapping X,Y coordinates are averaged to produce a smooth result rather than noisy "ripples." The tool also allows for some extrapolation beyond the data points. Here is a code example (assuming the GridFit Tool has already been installed):
%Establish points for surface
num_points = 20;
Tval = linspace(min(Tavg),max(Tavg),num_points);
Hval = linspace(min(Havg),max(Havg),num_points);
%Do the fancy fitting with smoothing
Pest = gridfit(Tavg, Havg, Pavg, Tval, Hval);
%Plot results
figure(5)
surfc(XI,YI,Pest, 'FaceColor', 'Interp')
To produce an even nicer plot, you can add labels, some transparancy and overlay the original points:
alpha(0.5)
hold on
plot3(Tavg,Havg,Pavg,'.')
xlabel('Temperature')
ylabel('Humidity')
zlabel('Power')
grid on
title('GridFit')
PS: #upperBound: Thanks for the Delaunay triangulation tip. That seems like the way to go if you want to go through each of the points. I am a newbie so can't comment yet.
Below is your solution:
Save/write the Myplot3D function
function [x,y,V]=Myplot3D(X,Y,Z)
x=linspace(X(1),X(end),100);
y=linspace(Y(1),Y(end),100);
[Xt,Yt]=meshgrid(x,y);
V=griddata(X,Y,Z,Xt,Yt);
Call the following from your command line (or script)
[Tavg_new,Pavg_new,V]=Myplot3D(Tavg,Pavg,Havg);
surf(Tavg_new,Pavg_new,V)
colormap jet;
xlabel('Temperature')
ylabel('Power/Pressure')
zlabel('Humidity')