How to plot an array using a for loop? - matlab

I want to plot inside a for loop. I tried the following code also used hold on but the plot is still blank. I don't know where I am getting wrong.
M2 = true(21, 6);
M2(1:2, 3:5) = false;
R = [0.5:0.1:2.5];
H=[0:5:25];
m=21,
n=6,
for i=1:m
for j=1:n
if M(i,j)==0
plot(H(i),R(j), 'color', 'r')
drawnow();
end
end
end

As Ander Biguri already said, the plot function actually does not draw anything when you provide only one point and do not specify any markers.
But, if you want to create some animated plot, that one of the points is displayed at a time:
h = scatter(0, 0, 'r');
xlim([min(H) max(H)]);
ylim([min(R) max(R)]);
for i=1:m
for j=1:n
if M(i,j)==0
set(h, 'xdata', H(i), 'ydata', R(j));
pause(0.5);
drawnow();
end
end
end
Or if you want to show all point together, you don't even need a loop:
[HH, RR] = meshgrid(H, R);
scatter(HH(~M), RR(~M), 'r');
xlim([min(H) max(H)]);
ylim([min(R) max(R)]);

Related

How to speed up a very slow animated plot in MATLAB

I'm trying to create an animated plot but my code is very slow, perhaps the method I'm using is too naive. In the below example, I have 4 subplots each with 3 lines, which I update in a 'time' loop.
clc;clear;close all;
state = {'$x-Position$','$x-Velocity$','$y-Position$','$y-Velocity$'};
ylabels = {'$x$','$\dot{x}$','$y$','$\dot{y}$'};
options1 = {'interpreter','latex'};
options2 = {'interpreter','latex','fontsize',20};
maxT = 300;
for pp = 1:4
hh1(pp)=subplot(2,2,pp);
xlabel('$t$',options2{:});
ylabel(ylabels{pp},options2{:});
title(state{pp},options1{:})
xlim([0 maxT])
hold on
end
x = randn(4,300);
z = randn(4,300);
x_est = randn(4,300);
for k = 2:maxT
for p = 1:4
plot(hh1(p),k-1:k,x(p,k-1:k),'b','linewidth',2)
plot(hh1(p),k-1:k,z(p,k-1:k),'m')
plot(hh1(p),k-1:k,x_est(p,k-1:k),':k','linewidth',2)
end
drawnow;
end
As can be seen from the profiler output, the drawnow is killing the time. Is there any way I can be more efficient in creating this animation?
Because you want an animation, there is no alternative to using drawnow to update the frame. However, it's not drawnow in particular which is slowing you down - the profiler can be misleading... drawnow simply updates all of the graphics changes since the last re-draw, which in your case is a dozen new plots!
You'll find that hold is pretty slowing. For instance if you're wiser about your holding, remove the existing hold on and only hold when actually plotting
% ... above code the same but without 'hold on'
for p = 1:4
hold(hh1(p), 'on');
% plots
hold(hh1(p), 'off');
end
This saves ~10% time on my PC (12.3sec down to 11.3sec).
The real speed up comes from removing hold entirely, along with all of the individual plot calls! This method also doesn't touch the line formatting which will help with speed. See a previous question about updating plot data here.
Simply update the plot data instead of adding plots. This gives me a speedup of ~68% (12.3sec down to 4.0sec).
% ... your same setup
% Initialise plot data
x = randn(4,300);
z = randn(4,300);
x_est = randn(4,300);
plts = cell(4,3);
hh1 = cell(4,1);
% Loop over subplots and initialise plot lines
for p = 1:4
hh1{p}=subplot(2,2,p);
xlabel('$t$',options2{:});
ylabel(ylabels{p},options2{:});
title(state{p},options1{:})
xlim([0 maxT])
% Hold on to make 3 plots. Create initial points and set line styles.
% Store the plots in a cell array for later reference.
hold on
plts{p,1} = plot(hh1{p},1:2,x(p,1:2),'b','linewidth',2);
plts{p,2} = plot(hh1{p},1:2,z(p,1:2),'m');
plts{p,3} = plot(hh1{p},1:2,x_est(p,1:2),':k','linewidth',2);
hold off
end
% March through time. No replotting required, just update XData and YData
for k = 2:maxT
for p = 1:4
set(plts{p,1}, 'XData', 1:k, 'YData', x(p,1:k) );
set(plts{p,2}, 'XData', 1:k, 'YData', z(p,1:k) );
set(plts{p,3}, 'XData', 1:k, 'YData', x_est(p,1:k) );
end
drawnow;
end
Now the plotting is pretty optimised. If you want the animation to be even quicker then just plot every 2nd, 3rd, ..., nth timestep instead of every timestep by using for k = 2:n:maxT.

How do i produce an animated GIF in MATLAB?

I want to produce a animated gif of a solution to a partial differential equation. That is the gif should show the solution at specific time.
Currently I can only make pictures in which all times are plotted.
Below is my entire program, with figure(3) being my attempt of making a gif.
clear all;
close all;
%%%%%%%%%%%%
% For slide 27 of Diffusion 1D
% The equation to be graphed in latex form is
% u(x,t)=\frac{1}{L}+\frac{2}{L}\sum^{\infty}_{n=1}cos(\frac{n\pi x_0}{L})cos(\frac{n\pi x}{L})e^{-k(\frac{n\pi}{L})^2t}
%%%%%%%%%%%%
%define constants
%note that the constants listed in the file are arbitrary
L = 2; %length of the rod
k= 0.01; % Diffusivity, which is assume to be constant but can be a function of x
x0 = 1; %location of the inital condition i.e. f(x)=delta(x-x0)
tmax= 50; %maximum amount of time the simulation runs
nmax = 200; % maximum value for n, increase to accuracy
tgrid = 21; %The number of points to be evaluated in the time domain
xgrid = 51; %The number of points to be evaluated in the space domain
%initialize variables
u=zeros(tgrid,xgrid); %preallocate array used for storing values of the solution
t=linspace(0,tmax,tgrid);%We assume that time is evenly distributed
x=linspace(0,L,xgrid); %We assume that space is evenly distributed
%Plotting variables
figure(1);
hold on;
axis([0 L -inf inf]);
xlabel('x');
ylabel('u(x,t)');
%Calculation,
for i=1:tgrid
for j=1:xgrid
seriesSum=0;
%Calculate the fourier series up to nmax for each point u(x,t)
for n= 1:nmax
seriesSum= seriesSum + cos(n*pi*x0/L)*cos(n*pi*x(j)/L)*exp(-k*t(i)*(n*pi/L)^2);
end
%Finish calcuation for solution at a specific point
u(i,j)= 1/L+(2/L)*seriesSum;
end
%After we have calculated all points at time t, we graph it for time t
plot(x,u(i,:),'linewidth',4);
end
saveas(gcf,'PDE_sol.png')%Save figure as png in current directory
%run a second loop that does not include the initial condition to get a
%better view of the long term behaviour.
%Plotting variables
figure(2);
hold on;
axis([0 L -inf inf]);
xlabel('x');
ylabel('u(x,t)');
for i=2:tgrid
plot(x,u(i,:),'linewidth',4);
end
saveas(gcf,'PDE_sol_without_inital.png')%Save figure as png in current directory
%Create a gif verison of figure 2
figure(3);
axis([0 L -inf inf]);
xlabel('x');
ylabel('u(x,t)');
filename = 'PDE_sol.gif';
for i=2:tgrid
plot(x,u(i,:),'linewidth',4);
drawnow
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if i == 2;
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end
The output gif that I get is
which is clearly not animated.
Note: If you think there is a better place to post this question please direct me to it. As my issue is with the MATLAB programming language and not the math involved I thought this would be the best place to post my question.
The first input to getframe is the handle of the figure that you'd like to take a screenshot of. As you have it written, you are grabbing figure 1 which is actually referring to the first figure that you create that you aren't updating within your loop.
You have assigned a numeric handle of 3 to the figure that you create right before your last loop so you'll want to tell getframe to use that figure instead.
Additionally, I would create one plot object and update the XData and YData rather than continuously creating new plot objects. The issue with calling plot continuously is that it's slow AND it completely resets all of your axes settings such as x and y labels as well as x and y limits.
% Store the handle to the figure in hfig
hfig = figure(3);
% Create the initial plot object
hplot = plot(NaN, NaN, 'LineWidth', 4);
axis([0 L 0 2]);
xlabel('x');
ylabel('u(x,t)');
filename = 'PDE_sol.gif';
for i=2:tgrid
% Update the plot appearance
set(hplot, 'XData', x, 'YData', u(i,:));
drawnow
% Get a screenshot of THIS figure
frame = getframe(hfig);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if i == 2;
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end

Add centroid to plot - Matlab

In the following script I get the coordinates of and coins in an image, later they are plotted. How can I also add to the plot the centroid calculated using stat (in red, marked as X)?
Script:
clc;
clear;
I = imread('coins.png');
imshow(I)
BW = im2bw(I);
BW_filled = imfill(BW,'holes');
stat = regionprops(BW_filled,'centroid');
boundaries = bwboundaries(BW_filled);
for k=1:10
b = boundaries{k};
plot(b(:,2),b(:,1),'g','LineWidth',3);
hold on;
end
Add
plot(stat(k).Centroid(1), stat(k).Centroid(2), 'rx');
after
plot(b(:,2), b(:,1), 'g', 'LineWidth', 3);
hold on;
You can also apply any additional customisations to the centroid point like
plot(stat(k).Centroid(1), stat(k).Centroid(2), 'rx', 'LineWidth', 3);
Explanation
stat(k) will get the kth element of stat. stat(k).Centroid will extract the centroid as [x, y] and we can then reference the x coordinate of the centroid as stat(k).Centroid(1) and the y as stat(k).Centroid(2).
Alternative Improvements
Some improvements to your code that I would suggest are
Put close all at the top of the script. This will close all currently open figures
Add figure; hold on; before the for loop and remove hold on from within the for loop. Calling hold on; multiple times is redundant.

Draw log graph curve on Matlab by clicking?

I'd like to draw a curve on an empty (semilog-y) graph by clicking the points I want it to run through, on the X-Y plane.
Is there a function for this?
edit: I'm trying to do this by obtaining the position of last pointer click -
axis([0 3000 0 1000]);
co=get(gcf, 'CurrentPoint');
It seems to return the cursor position at the time of execution, but it does not change later.
edit2: Here's what works for me. The actual drawing I can do by using the arrays of points collected.
clear
clc
h=plot(0);
grid on;
xlim([0 3000]);
ylim([0 1000]);
datacursormode on;
% Enlarge figure to full screen.
screenSize = get(0,'ScreenSize');
set(gcf, 'units','pixels','outerposition', screenSize);
hold on;
% Print the x,y coordinates - will be in plot coordinates
x=zeros(1,10); y=zeros(1,10);
for p=1:10;
[x(p),y(p)] = ginput(1) ;
% Mark where they clicked with a cross.
plot(x(p),y(p), 'r+', 'MarkerSize', 20, 'LineWidth', 3);
% Print coordinates on the plot.
label = sprintf('(%.1f, %.1f)', x(p), y(p));
text(x(p)+20, y(p), label);
end
Not really, but now there is:
function topLevel
%// parameters
xrange = [0 100];
yrange = [1e-4 1e4];
%// initialize figure, plot
figure, clf, hold on
plot(NaN, NaN);
axis([xrange yrange]);
set(gca, 'YScale', 'log')
t = text(sum(xrange)/2, sum(yrange)/2, ...
'<< Need at least 3 points >>',...
'HorizontalAlignment', 'center');
%// Main loop
xs = []; p = [];
ys = []; P = [];
while true
%// Get new user-input, and collect all of them in a list
[x,y] = ginput(1);
xs = [xs; x]; %#ok<AGROW>
ys = [ys; y]; %#ok<AGROW>
%// Plot the selected points
if ishandle(p)
delete(p); end
p = plot(xs, ys, 'rx');
axis([xrange yrange]);
%// Fit curve through user-injected points
if numel(xs) >= 3
if ishandle(t)
delete(t); end
%// Get parameters of best-fit in a least-squares sense
[A,B,C] = fitExponential(xs,ys);
%// Plot the new curve
xp = linspace(xrange(1), xrange(end), 100);
yp = A + B*exp(C*xp);
if ishandle(P)
delete(P); end
P = plot(xp,yp, 'b');
end
end
%// Fit a model of the form y = A + B·exp(C·x) to data [x,y]
function [A, B, C] = fitExponential(x,y)
options = optimset(...
'maxfunevals', inf);
A = fminsearch(#lsq, 0, options);
[~,B,C] = lsq(A);
function [val, B,C] = lsq(A)
params = [ones(size(x(:))) x(:)] \ log(abs(y-A));
B = exp(params(1));
C = params(2);
val = sum((y - A - B*exp(C*x)).^2);
end
end
end
Note that as always, fitting an exponential curve can be tricky; the square of the difference between model and data is exponentially much greater for higher data values than for lower data values, so there will be a strong bias to fit the higher values better than the lower ones.
I just assumed a simple model and used a simple solution, but this gives a biased curve which might not be "optimal" in the sense that you need it to be. Any decent solution really depends on what you want specifically, and I'll leave that up to you ^_^

Several graphs in 1 loop, each iteration adds a line on every figure

Trying to engineer the following in Matlab:
** loop start;
y(:,i) = function of x;
z(:,i) = function of x;
plot(x,y(:,i)) on figure 1, hold all;
plot(x,z(:,i)) on figure 2, hold all;
** loop end;
add title, legend, etc for figure 1 (NB: we have multiple lines);
add title, legend, ets for figure 2 (NB: same, have multiple lines for the legend);`
Tried multiple combinations without much luck. Managed to get 2 figures but only the 2-nd displays multiple lines, not the first. And can't figure how to add legends to these 2 correctly.
Save a handle to each figure, and to each axis object:
fh1 = figure;
hold all;
ah1 = gca;
fh2 = figure;
hold all;
ah2 = gca;
for i=1:N
y(:,i) = function of x;
z(:,i) = function of x;
plot(ah1, x, y(:,i)); %# tell it which axis to use (ah1)
plot(ah2, x, z(:,i)); %# (ah2)
end
legend(ah1, ...) %# legend options here
legend(ah2, ...) %# and the other legend
%# note: you can set figure properties for each using fh1, fh2 handles.
You can do this:
figHandle1 = figure(1);
figHandle2 = figure(2);
Then when you want to plot on that figure do this:
figure(figHandle1) %Plot on figure 1
%ie plot(someStuff)
figure(figHandle2) %Plot on figure 2
Also its the same for the title and stuff, you jjust need to identify which figure by doing:
figure(handle);
Hope this helps.