Interpolating 2D Matrix Data - matlab

I have a two-dimensional matrix whose elements represent data that can be colormapped and represented as an image. I want to interpolate them, but I get strange behavior at some of the boundaries that I can't explain.
Here is the original image, the image after 3 iterations of the interpolation routine, and the image after 10 interpolation iterations.
Here's my code:
close all
ifactor = 0.9; % Interpolation factor
% Cut out the meaningless stuff at the edges
bshift_i = bshift(1:16, 1:50);
[m, n] = size(bshift_i);
% Plot the initial data using colormapping
figure()
imagesc(bshift_i, [7.5 10.5])
colorbar()
% Main processing loop
for ii = 1:10
% Every iteration, we generate a grid that has the same size as the
% existing data, and another one that whose axis step sizes are
% (ifactor) times smaller than the existing axes.
[b_x, b_y] = meshgrid(1:ifactor^(ii - 1):n, 1:ifactor^(ii - 1):m);
[b_xi, b_yi] = meshgrid(1:ifactor^ii:n, 1:ifactor^ii:m);
% Interpolate our data and reassign to bshift_i so that we can use it
% in the next iteration
bshift_i = interp2(b_x, b_y, bshift_i, b_xi, b_yi);
end
% Plot the interpolated image
figure()
imagesc(bshift_i, [7.5 10.5])
colorbar()
I'm mainly wondering why the blue artifacts at the bottom and right edges occur, and if so, how I can work around/avoid them.

The problem is how you define the x and y ranges for the interpolation.
Let's take a look at 1:ifactor^(ii - 1):m:
For the first iteration, you get 16 values, from 1 to 16.
For the second iteration, you get 17 values, from 1 to 15.4
For the third iteration, you get 19 values, from 1 to 15.58
An this is enough to illustrate the problem. With the second iteration, everything is fine, because your upper interpolation bound is inside the value range. However, for the third iteration, your upper bound is now outside the value range (15.58 > 15.4).
interp2 does not extrapolate, it returns a NaN (after the 3rd iteration, bshift_i(end,end) is NaN). These NaNs get plotted as 0, hence blue.
To fix this, you have to ensure that your x and y range always include the last value. One way to do this could be:
[b_x, b_y] = meshgrid(linspace(1,n,n./(ifactor^(ii - 1))), ...
linspace(1,m,m./(ifactor^(ii - 1))));
[b_xi, b_yi] = meshgrid(linspace(1,n,n./(ifactor^(ii))), ...
linspace(1,m,m./(ifactor^(ii))));
linspace always includes the first and the last element. However, the third input is not the increment, but the number of elements. That's why you'd have to adapt that condition to resemble your approach.

Related

Matlab - plotting function with a for loop over a matrix

I have an assignment which sounds like:
"“Grades per assignment”: A plot with the assignments on the x-axis and the grades on the y-axis. The x-axis must show all assignments from 1 to M, and the y-axis must show all grade −3 to 12. The plot must contain:
Each of the given grades marked by a dot. You must add a small random number (between -0.1 and 0.1) to the x- and y-coordinates of each dot, to be able tell apart the different dots which otherwise would be on top of each other when more than one student has received the same grade in the same assignment.
The average grade of each of the assignments plotted as a line"
For now i have created this function:
function gradesPlot(grades)
figure(2);
n_assignments=size(grades,2);
hold on; % Retain current plot when adding new plots.
for i = 1:n_assignments % Loop through every assignment.
% Scatter plot of assignment vs grades for that assignment.
% One assignment on every iteration.
n_assignments2=([1:size(grades,2)]);
scatter(n_assignments2,grades(:,i)'jitter', 'on', 'jitterAmount', 0.1)
hold off; % Set the hold state to off.
end
%Titles to the plot
title('Grades per assignment');
xlabel('Assignment');
ylabel('Given grades');
break;
end
when i run the code it says that the vectors must be same length.
And it looks like it doesn't loop over the matrix more than ones.
The test grades i am using as input is looking like this:
grades=[[-3,4,10];[7,4,12];[7,10,12];[0,4,4];[2,2,2];[2,2,2]]
I hope some of you guys can help me get this function to work - maybe in an easier way?
Thank you in advance
You should not turn off hold since it is tells MatLab to plot everything in the current active plot, roughly speaking. You can find a possible solution to your problem down below: I added some explanations in comments.
function gradesPlot(grades)
figure(2);
% Extract the relevant information: number of assignements, number of grades
[n_assignments,n_grades] = size(grades);
hold on; % Retain current plot when adding new plots.
for i = 1:n_assignments % Loop through every assignment.
% Scatter plot of assignment vs grades for that assignment.
% One assignment on every iteration
% For Scatter, you have to provide 2 vectors of the same size: in this
% way, we are putting al the dots corresponding to the grades of the
% i-th assignement in correspondence of the i-th coordinate on the x
% axis. We are also temporary saving in h the handle to the attributes
% of the dots, in order to retrieve the color.
h = scatter(i*ones(n_grades,1),grades(i,:),'jitter', 'on', 'jitterAmount', 0.1);
% This plots the horizontal line corresponding to the average of the
% grades related to the i-th assignement
l = line([i-0.5 i+0.5],[1,1]*mean(grades(i,2:end)));
% For using the same color as the dots.
l.Color = h.CData;
end
%Titles to the plot
title('Grades per assignment');
xlabel('Assignment');
ylabel('Given grades');
axis([0 n_assignments+1 -4 13])
end
Remember that the break command must be used inside a loop, not for exiting a function. Use return if you desire.

Make histogram of every column of a matrix but without zero elements

I'm running a simulation of particles in a box. When a particle leaves the box, its kinetic energy becomes zero (for time > t escape). So I want to make a histogram of how Wkinet (which is a function of nP=number of particles, ntM=time steps) evolves through time, but I do not want to take into account the zero values of every column. Is there a way to code it so it can find the optimum number of bins?
This is what I've tried:
nbins = 1000;
for j = 1:ntM/5
Wkinet(Wkinet==0) = NaN;
y = Wkinet(:,j).*erg2eV;
end
histfit(y,nbins)
Logical indexing is often rather fast and intuitive once you get the hang of the syntax.
myTolerance=1e-7; % in erg units.
nbins=1000;
for j=1:ntM/5
%Wkinet(Wkinet==0)=NaN;
% y=Wkinet(:,j).*erg2eV; % An extra assigment is costly and probably not needed.
H = histfit(Wkinet(abs(Wkinet(:,j))>myTolerance, j) * erg2ev, nbins);
% Select from column j all rows in column j whose absolute values are greater than the tolerance.
% Assumption; erg2ev is just a scalar, otherwise select its entries with erg2ev(abs(Wkinet(:,j))>myTolerance)
H(1).delete; Remove bins, only keep the fit.
set(gca, 'YScale', 'log'); % Make logarithmic Y
set(gca, 'XScale', 'log'); % Make logarithmic X
pause
end
If you need explicit limiting of axis, use
xlim([lowerBound upperBound]); ylim(etc...
... or sometimes it is helpful to make use of the axis command for precise control, e.g.
ax=axis; ax(3)=min( 8ax(3) maxAllowedY]); axis(ax);
The "pause" (for interactive use) may be replaced by a print command to save plots to disk. E.g.
print(sprintf('My_plot_%02d',j),'-dpng');
Or save the figure:
savefig(sprintf('My_fig_%02d',j));
If are sure that number of plots are less than, say 16, you could put a subplot command in the loop. Replace the pause by
subplot(4,4,j);
Final note; if your intent is to plot a normal distribution fitted to your nonzero data you may get better results replacing the histfit function using
myFit = fitdist(Wkinet(Wkinet(:,j)>myTolerance, j) * erg2ev), 'Normal');
maxEv = max(Wkinet(Wkinet(:,j)>myTolerance, j) * erg2ev);
myX = [myTolerance; maxEv/100; maxEv]; % Alter for different plot X-axis
myY = pdf(myFit, myX);
plot(myX, myY);
I checked and there is a difference between the fitdist and histdist, probably caused by the bin discretization.

Matlab: trouble with taking derivative of image?

So I need to take the derivative of an image in the x-direction for this assignment, with the goal of getting some form of gradient. My thought is to use the diff(command) on each row of the image and then apply a Gaussian filter. I haven't started the second part because the first is giving me trouble. In attempting to get the x-derivative I have:
origImage = imread('TightRope.png');
for h = 1:3 %%h represents color channel
for i = size(origImage,1)
newImage(i,:,h) = diff(origImage(i,:,h)); %%take derivative of row and translate to new row
end
end
The issue is somewhere along the way I get the error 'Subscripted assignment dimension mismatch.'.
Error in Untitled2 (line 14)
newImage(i,:,h) = diff(origImage(i,:,h));
Does anyone have any ideas on why that might be happening and if my approach is correct for getting the gradient/gaussian derivative?
Why not use fspecial along with imfilter instead?
figure;
I = imread('cameraman.tif');
subplot 131; imshow(I); title('original')
h = fspecial('prewitt');
derivative = imfilter(I,h','replicate'); %'
subplot 132; imshow(derivative); title('derivative')
hsize = 5;
sigma = 1;
h = fspecial('gaussian', hsize, sigma) ;
gaussian = imfilter(derivative,h','replicate'); %'
subplot 133; imshow(gaussian); title('derivative + gaussian')
The result is the following one:
If your goal is to use diff to generate the derivative rather than to create a loop, you can just tell diff to give you the derivative in the x-direction (along dimension 2):
newImage = diff(double(origImage), 1, 2);
The 1 is for the first derivative and 2 is for the derivative along the second dimension. See diff.
As #rayryeng mentions in his answer, it's important to cast the image as double.
Given a N element vector, diff returns a N-1 length vector, so the reason why you are getting an alignment mismatch is because you are trying to assign the output of diff into an incorrect number of slots. Concretely, supposing that N is the total number of columns, you are using diff on a 1 X N vector which thus returns a 1 x (N - 1) vector and you are trying to assign this output as a single row into the output image which is expected to be 1 x N. The missing element is causing the alignment mismatch. diff works by taking pairs of elements in the vector and subtracting them to produce new elements, thus the reason why there is one element missing in the final output.
If you want to get your code working, one way is to pad each row of the image or signal vector with an additional zero (for example) as input into diff. Something like this could work. Take note that I'll be converting your image to double to allow the derivative to take on negative values:
origImage = imread('...'); %// Place path to image here and read in
origImage = im2double(origImage); %// Change - Convert to double precision
newImage = zeros(size(origImage)); %// Change - Create blank new image and populate each row per channel manually
for h = 1:3 %%h represents color channel
for ii = 1:size(origImage,1) %// Change - fixed for loop iteration
newImage(ii,:,h) = diff([0 origImage(ii,:,h)]); %// Change
end
end
Take note that your for loop was incorrect since it didn't go over every row... just the last row.
When I use the onion.png image that's part of the image processing toolbox:
...and when I run this code, I get this image using imshow(newImage,[]);:
Take note that the difference filter was applied to each channel individually and I changed the intensities per channel so that the smallest value gets mapped to 0 and the largest value gets mapped to 1. How you can interpret this image is that any areasthat have a non-black colour have some non-zero differences and hence there is some activity going on in those areas and any areas that have a dark / black colour means that there is no activity going on in those areas. Take note that we applied a horizontal filter, so if you wanted to do this vertically, you'd simply repeat the behaviour but apply this column-wise instead of row-wise as you did above.

Plotting rows of points in Matlab

So I'm still getting used to Matlab and am having a bit of trouble with plotting. I have a cell which contains a list of points in each row. I want to plot each row of points in a different colour on the same graph so I can compare them. The catch is that I need to make this work for an unknown number of points and rows (ie the number of points and rows can change each time I run the program).
So for example, I might have my cell array A:
A = {[0,0], [1,2], [3,4]; [0,0] [5,6], [9,2]}
and I want to plot the points in row 1 against their index (so a 3D graph) and then have the points in row 2 on the same graph in a different colour. The rows will always be the same length. (Each row will always have the same number of points). I've tried a few different for loops but just can't seem to get this right.
Any help in sending me in the right direction would be greatly appreciated!
The fact that the number of points and rows can change with each iteration should not pose much of a problem. I would suggest using the size function before your plot loops (size(A,1) and size(A,2)) to get the dimensions of the matrix.
Once you have the size of the matrix, loop through the dimensions and plot the lines on the same plot using holdon, and then finally just make the line color a function of the dimensions as you loop through so that you always have a different line color
You could just convert it to a matrix and plot it directly:
% Some dummy data - format a little different from your example
% to allow for different numbers of elements per row
A = {[0,0, 1,2, 3,4]; [0,0, 5,6]};
% Figure out how many columns we need in total
maxLen = max(cellfun(#length, A));
% Preallocate
Amat = NaN(size(A, 1), maxLen);
% Copy data
for n = 1:size(A, 1)
curA = A{n};
Amat(n, 1:length(curA)) = curA;
end
% Generate 1:N vector repeated the correct number of times (rows)
x = repmat(1:size(Amat, 2), size(Amat, 1), 1);
plot(x, Amat)
Edit: You mentioned a 3D graph at some point in your post. The above won't plot a 3D graph, so here's something that will:
% Generate Amat as above
% Then:
[X, Y] = meshgrid(1:size(Amat, 1), 1:size(Amat, 2));
surf(X, Y, Amat.'); % OR: plot3(X, Y, Amat.');
I'm not sure this is exactly what you want, but your question is slightly unclear on exactly what kind of graph you want out of this. If you just want coloured lines on your plot, you can use plot3 instead of surf, but IMHO surf will probably give you a clearer plot for this kind of data.

MATLAB Piecewise Functions + Vector Manipulation

I would like to write a program which plots the points on top of a semicircle on a certain interval and a straight line everywhere else. Something like this: __n__.
I defined a time domain, which was stored as a vector (t = 0:0.01:5). I assumed that I could define the points on the top of the semicircle using elements of the time vector:
if t>=2|t<=2.3
y = sqrt(.15^2-(t-2.15)^2);
but MATLAB produced an error message saying only square matrices can be squared.
I tried to utilize indices to show that I wanted to square an element of the t vector and not the whole vector:
i = [200:230];
for t(200:230)
y = sqrt(.15^2-(t(i)-2.15)^2);
After these failures, I noticed that squaring a square matrix with one column of non-zero elements would produce a new square matrix with a column of the first matrix's elements squared. If there is some way to eliminate the extra columns of zeros after squaring the matrix, I could use that property of matrices to square the values of the t vector.
What is the simplest and most effective way to address this problem?
It sounds like you want to draw a horizontal line with a semicircular "bump" on it. Here's how you can do this:
t = 0:0.01:5; % Create the time vector
y = zeros(size(t)); % Create a zero vector the same size as t
index = find((t >= 2) & (t <= 2.3)); % Find a set of indices into t
y(index) = sqrt(.15^2-(t(index)-2.15).^2); % Add the "bump" to y
y(1:index(1)) = y(index(1)); % Add the line before the "bump"
y(index(end):end) = y(index(end)); % Add the line after the "bump"
In the above solution, the lines before and after the "bump" could be slightly higher or lower than one another (depending on where your samples in t fall). If you want to make sure they are at the same height, you can instead do the following:
index = (t >= 2) & (t <= 2.3); % Find a set of logical indices
y(index) = sqrt(.15^2-(t(index)-2.15).^2); % Add the "bump" to y
% OPTION #1:
y(~index) = y(find(index,1,'first')); % Use the first circle point as the height
% OPTION #2:
y(~index) = y(find(index,1,'last')); % Use the last circle point as the height
Finally, you can plot the line:
plot(t,y);
Hold on, so your question is, you want to square each element of a vector? All you have to do is:
t.^2
The . represents an element-wise operation in MATLAB on a vector or an array.
And secondly, if I understood your problem currently, you want to create a vector y, which contains a function of elements of t such that t>=2 | t <=2.3?
If so, all you have to do is this:
y = sqrt(0.15^2-(t( (t>=2|t<=2.3) )-2.15).^2));
Essentially, I created a logical index (t>=2 | t<=2.3) and used to access only those elements (which I wanted) in t.
Also, I didn't fully understand what you wanted to achieve. Do you want to plot the topmost point (maxima) of a semicircular curve?