Draw evenly-spaced height lines of a function in MATLAB - matlab

I would like to draw height lines of a function (represented by matrices, of course), using MATLAB.
I'm familiar with contour, but contour draws lines at even-spaced heights, while I would like to see lines (with height labels), in constant distance from one another when plotted.
This means that if a function grows rapidly in one area, I won't get a plot with dense height lines, but only a few lines, at evenly spaced distances.
I tried to find such an option in the contour help page, but couldn't see anything. Is there a built in function which does it?

There is no built-in function to do this (to my knowledge). You have to realize that in the general case you can't have lines that both represent iso-values and that are spaced with a fixed distance. This is only possible with plots that have special scaling properties, and again, this is not the general case.
This being said, you can imagine to approach your desired plot by using the syntax in which you specify the levels to plots:
...
contour(Z,v) draws a contour plot of matrix Z with contour lines at the data values specified in the monotonically increasing vector v.
...
So all you need is the good vector v of height values. For this we can take the classical Matlab exemple:
[X,Y,Z] = peaks;
contour(X,Y,Z,10);
axis equal
colorbar
and transform it in:
[X,Y,Z] = peaks;
[~, I] = sort(Z(:));
v = Z(I(round(linspace(1, numel(Z),10))));
contour(X,Y,Z,v);
axis equal
colorbar
The result may not be as nice as what you expected, but this is the best I can think of given that what you ask is, again, not possible.
Best,

One thing you could do is, instead of plotting the contours at equally spaces levels (this is what happens when you pass an integer to contour), to plot the contours at fixed percentiles of your data (this requires passing a vector of levels to contour):
Z = peaks(100); % generate some pretty data
nlevel = 30;
subplot(121)
contour(Z, nlevel) % spaced equally between min(Z(:)) and max(Z(:))
title('Contours at fixed height')
subplot(122)
levels = prctile(Z(:), linspace(0, 100, nlevel));
contour(Z, levels); % at given levels
title('Contours at fixed percentiles')
Result:
For the right figure, the lines have somewhat equal spacing for most of the image. Note that the spacing is only approximately equal, and it is impossible to get the equal spacing over the complete image, except in some trivial cases.

Related

Matlab - Plot difference between two graphs in specific points

I have two graphs, one is the exact graph of a solution, the other is a numerical approach. I have 4 specific points in my figure (t=0.25,0.5,0.75,1), where I want to illustrate the difference between the two graphs with a straight line. I found the errorbars function but i don't see any use there. Hope you can help me!
Edit:
this is the example figure:
t = [0:0.25:1];
y = t.*4;
x = t.^2+3;
plot(t,y,t,x)
I have 4 points now, t=0.25; t=0.5; t=0.75; t=1; At this points, I just want a vertical line between the two plots. I already have tried this: plot([t(1),y(1)],[t(1),x(1)])
but it just creates a line over the whole figure.
✶ It seems that you're not using hold on before using plot command the second time because otherwise you'd have got the desired result (which is actually not a correct way of plotting a vertical line).
✶ You're mixing up the values of x and y for plot(x,y). To plot a vertical line, it should be used like this: plot([x,x], [y1,y2])
For your case, you may not notice the difference between plot([t(1),y(1)],[t(1),x(1)]) (which is incorrect) and plot([t(1),t(1)],[x(1),y(1)]) (which is correct) because it is by chance that the values are same. Plot it for some other points and you'll realize the difference.
Fixed Code:
t = [0:0.25:1];
y = t.*4;
x = t.^2+3;
plot(t,y,t,x)
hold on
plot([t(1) t(1)],[x(1) y(1)])
% You have 't' on your horizontal axis and 'x'and 'y' values on the vertical axis
axis equal % just for better visualization
Output:

plot polar grey values in matrix without interpolating every for loop

I have a matrix with grey values between 0 and 1. For every entry in the matrix, there are certain polar coordinates that indicate the position of the grey values. I already have either Theta and Rho values (polar) ,both in separate 512×960 matrices. And grayscale values (in a matrix called C) for every Theta and Rho combination. I have the same for X and Y, as I just use pol2cart for the transformation. The problem is that I cannot directly plot these values, as they do not yet fit in the 'bins' of the new matrix.
What I want: to put the grey values in a square matrix of size 1024×1024. I cannot do this directly, because the polar coordinates fall in between the grid of this matrix. Therefore, we now use interpolation, but this is extremely time consuming and has to be done separately for every dataset, although the transformation from the original matrices to this final one will always be the same. Therefore, I'd like to solve this matrix once (either analytically or numerically) and use a matrix multiplication or something similar to apply the manipulation efficiently in every cycle of the code.
One example of what one of these transformations could look like this:
The zeros in the first matrix are the grid, and the value 1 (in between the grid) is the grey value that falls in between four grid points, then I'd like to transform to the second matrix (don't mind the visual spacing between the points).
For every dataset, I have hundreds of these matrices, so I would like to make the code more efficient.
Background: I'm using TriScatteredInterp now for the interpolation. We tried scatteredInterpolant as well, but that is slower. I also posted a related question, but decided to split the two possible solutions, because the solution I ask for here is also applicable to non-MATLAB code and will probably be faster and makes for a smoother (no continuous popping up of figures) execution of the code.
Using the image processing toolbox
Images work a bit differently than the data you have. However, it's fairly straightforward to map one representation into the other.
There is only one problem I see: wrapping. Obviously, θ = 2π = 0, but MATLAB does not know that. AFAIK, there is no easy way to tell MATLAB that.
Why does this matter? Well, simply put, inter-pixel interpolation uses information from the nearest N neighbors to find intermediate colors, with N depending on the interpolation kernel. When doing this somewhere in the middle of the image there is no problem, but at the edges MATLAB has to know that the left edge equals the right edge. This is not standard image processing, and I'm not aware of any function that is capable of this.
Implementation
Now, when disregarding the wrapping problem, this is one way to do it:
function resize_polar()
%% ORIGINAL IMAGE
% ==========================================================================
% Some random greyscale data
C = double(rgb2gray(imread('stars.png')))/255;
% Your current size, and desired size
sz_x = size(C,2); new_sz_x = 1024;
sz_y = size(C,1); new_sz_y = 1024;
% Ranges for teat and rho;
% replace with your actual values
rho_start = 0; theta_start = 0;
rho_end = 10; theta_end = 2*pi;
% Generate regularly spaced grid;
theta = linspace(theta_start, theta_end, sz_x);
rho = linspace(rho_start, rho_end, sz_y);
[theta, rho] = meshgrid(theta,rho);
% Make plot of generated data
plot_polar(theta, rho, C, 'Original image');
% Resize data
[theta,rho,C] = resize_polar_data(theta, rho, C, [new_sz_y new_sz_x]);
% Make plot of generated data
plot_polar(theta, rho, C, 'Rescaled image');
end
function [theta,rho,data] = resize_polar_data(theta,rho,data, new_dims)
% Create fake RGB image cube
IMG = cat(3, theta,rho,data);
% Rescale as if theta and rho are RG color data in the RGB
% image cube
IMG = imresize(IMG, new_dims, 'nearest');
% Split up the data again
theta = IMG(:,:,1);
rho = IMG(:,:,2);
data = IMG(:,:,3);
end
function plot_polar(theta, rho, data, label)
[X,Y] = pol2cart(theta, rho);
figure('renderer', 'opengl')
clf, hold on
surf(X,Y,zeros(size(X)), data, ...
'edgecolor', 'none');
colormap gray
title(label);
end
The images used and plotted:
Le awesomely-drawn 512×960 PNG image
Now, the two look the same (couldn't really come up with a better-suited image), so you'll have to believe me that the 512×960 has indeed been rescaled to 1024×1024, with nearest-neighbor interpolation.
Here are some timings for the actual imresize() operation for some simple kernels:
nearest : 0.008511 seconds.
bilinear: 0.019651 seconds.
bicubic : 0.025390 seconds. <-- default kernel
But this depends strongly on your hardware; I believe imresize offloads a lot of work to the GPU, so if you have a crappy one, it'll be slower.
Wrapping
If the wrapping problem is really important to you, you can modify the function above to do the following:
first, rescale the image with imresize() like before
horizontally concatenate the second half of the grayscale data and the first half. Meaning, you swap the first and second halves to make the left and right edges (0 and 2π) touch in the middle.
rescale this intermediate image with imresize()
Extract the central vertical strip of the rescaled intermediate image
split that up in two equal-width strips
and replace the edge strips of the output image with the two strips you just created
Now, this is kind of a brute force approach: you are re-scaling an image twice, and most of the pixels of the second image round will be discarded. If performance is a problem, you can of course apply the rescale to only the central strip of that intermediate image. But, well, that will be a bit more complicated.

MeshGrid for Triangle Elements

I want to build a contourf plot of a certain aspect in my Plate. The plate is divided in triangle elements, which I have the coordinates (x,y) of each knot of the triangle.
So, How can I make a meshgrid for my knots so I can make my contourf plot?? I have the coordinates of everything and have the value of my function Z in each knot. (I'm a beginner in Matlab, sorry for this "basic" question)
If your goal is just to visualise the triangles then there is another way that's probably simpler and more robust (see the end of this post).
If you definitely need to generate contours then you will need to interpolate your triangular mesh over a grid. You can use the scatteredInterpolant class for this (documentation here). It takes the X and Y arguments or your triangular vertices (knots), as well as the Z values for each one and creates a 'function' that you can use to evaluate other points. Then you create a grid, interpolate your triangular mesh over the grid and you can use the results for the countour plot.
The inputs to the scatteredInterpolanthave to be linear column vectors, so you will probably need to reshape them using the(:)`notation.
So let's assume you have triangular data like this
X = [1 4; 8 9];
Y = [2 3; 4 5];
Z = [0.3 42; 16 8];
you would work out the upper and lower limits of your range first
xlimits = minmax(X(:));
ylimits = minmax(Y(:));
where the (:) notation serves to line up all the elements of X in a single column.
Then you can create a meshgrid that spans that range. You need to decide how fine that grid should be.
spacing = 1;
xqlinear = xlimits(1):spacing:xlimits(2);
yqlinear = ylimits(1):spacing:ylimits(2);
where linspace makes a vector of values starting at the first one (xlimits(1)) and ending at the third one (xlimits(2)) and separated by spacing. Experiment with this and look at the results, you'll see how it works.
These two vectors specify the grid positions in each dimension. To make an actual meshgrid-style grid you then call meshgrid on them
[XQ, YQ] = meshgrid(xqlinear, yqlinear);
this will produce two matrices of points. XQ holds the x-coordinates of every points in the grid, arranged in the same grid. YQ holds the y-coordinates. The two need to go together. Again experiment with this and look at the results, you'll see how it works.
Then you can put them all together into the interpolation:
F = scatteredInterpolant(X(:), Y(:), Z(:));
ZQ = F(XQ, YQ);
to get the interpolated values ZQ at each of your grid points. You can then send those data to contourf
contourf(XQ, YQ, ZQ);
If the contour is too blocky you will probably need to make the spacing value smaller, which will create more points in your interpolant. If you have lots of data this might cause memory issues, so be aware of that.
If your goal is just to view the triangular mesh then you might find trimesh does what you want or, depending on how your data is already represented, scatter. These will both produce 3D plots with wireframes or point clouds though so if you need contours the interpolation is the way to go.

Making an accurate colorbar for a simple plot

I am trying to make a simple plot (for this example doing a plot of y=x^2 will suffice) where I want to set the colors of the points based on their magnitude given some colormap.
Following along my simple example say I had:
x = 1:10;
y = x.^2;
Use gscatter(x,y,jet(10)); legend hide; colorbar which produces a plot with the points colored but the colorbar does not agree with the colored values. (Can't post picture as this is my first post). Using a caxis([1,100]) command gives the right range but the colors are still off.
So I have two questions:
(1) How can I fix the colors to fit to a colorbar given a range? In my real data, I am looking at values that range from -50 to 50 in some instances and have many more data points.
(2) I want to create a different plot with the same points (but on different axes) and I want the colors of each point on this new plot to have the same colors as their counterparts in the previous plot. How can I, programmatically, extract the color from each point so I can plot it on two different sets of axes?
I would just move the points into a matrix and do an imagesc() command but they aren't spaced as integers or equally so simple scaling wouldn't work either.
Thanks for any help!
Regarding you first question, you need to interpolate the y values into a linear index to the colormap. Something like:
x = 1:10;
y = x.^4;
csize = 128;
cmap = jet(csize);
ind = interp1(linspace(min(y),max(y),csize),1:csize,y,'nearest');
scatter(x,y,14,cmap(ind,:),'filled')
colorbar
caxis([min(y) max(y)])
Using interp1 in this case is an overkill; you could calculate it directly. However, I think in this way it is clearer.
I think it also answers your 2nd question, since you have the index of the color of each data point, so you can use it again in the same way.

Contouring a mesh and assigning magnitude arrows in Matlab

I want to assign vector to a contourf graph, in order to show the direction and magnitude of wind.
For this I am using contourf(A) and quiver(x,y), where as A is a matrix 151x401 and x,y are matrices with the same sizes (151x401) with magnitude and direction respectively.
When I am using large maps i get the position of the arrows but they are to densily placed and that makes the graph look bad.
The final graph has the arrows as desired, but they are to many of them and too close, I would like them to be more scarce and distributed with more gap between them, so as to be able to increase their length and at the same time have the components of the contour map visible.
Can anyone help , any pointers would be helpful
i know its been a long time since the question was asked, but i think i found a way to make it work.
I attach the code in case someone encounters the same issues
[nx,ny]= size(A) % A is the matrix used as base
xx=1:1:ny; % set the x-axis to be equal to the y
yy=1:1:nx; % set the y-axis to be equal to the x
contourf(xx,yy,A)
hold on, delta = 8; %delta is the distance between arrows)
quiver(xx(1:delta:end),yy(1:delta:end),B(1:delta:end,1:delta:end),C(1:delta:end,1:delta:end),1) % the 1 at the end is the size of the arrows
set(gca,'fontsize',12);, hold off
A,B,C are the corresponding matrices ones want to use