I am trying to test the function movie2avi using simple codes in R2014a as follows:
clear; close all;
figure;
Z = peaks;
surf(Z);
axis tight manual;
ax = gca;
ax.NextPlot = 'replaceChildren';
loops = 40;
F(loops) = struct('cdata',[],'colormap',[]);
for j = 1:loops
X = sin(j*pi/10)*Z;
surf(X,Z);
drawnow;
F(j) = getframe(gcf);
end
movie(F);
movie2avi(F, 'myPeaks.avi', 'compression', 'None');
It seems the movie(F) works well but the avi file created contains the toolbar and background instead of just showing the graph. Also the avi file just show stationary picture as follow:
Using quicktime to open it would produce same result:
https://www.dropbox.com/s/fd8vw7jvll5xkpw/b.png?dl=0
There is also a warning:
Warning: Struct field assignment overwrites a value with class "double". See MATLAB R14SP2 Release Notes, Assigning Nonstructure
Variables As Structures Displays Warning, for details.
Please help. Thanks.
Have you tried
F(j) = getframe(gca); %// Gets the current axis
This will capture the axis rather than the entire figure window.
Related
I am trying to save an animation as animated GIF.
My plot is similar to the given code below.
I created it with animated line too.
The problem is that:
When I defined my figure as f=figure or figure(1) it creates the .gif file properly.
However, instead of plotting my figure in separate screen using "figure" command, I have to plot in an axes on MATLAB GUI axes as the given figure.
I tried it with: f=(handles.axes_threeDOF);, But when I use this function, the gif file creates different part of the screen.
Could you help me to solve my problem?
numpoints = 500;
x = linspace(0,4*pi,numpoints);
y = square(x);
y2 = 3 +square(x+1);
f = figure
h = animatedline('Color','b','LineWidth',2);
h2 = animatedline('Color','r','LineWidth',2);
grid on;
axis([0,12,-3,+6])
for k = 1:numpoints
addpoints(h,x(k),y(k))
addpoints(h2,x(k),y2(k))
drawnow
% Capture the plot as an image
frame = getframe(f);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
% Write to the GIF File
if k == 1
imwrite(imind,cm,'test.gif','gif', 'Loopcount',inf);
else
imwrite(imind,cm,'test.gif','gif','WriteMode','append');
end
end
I want to create a gif of this animation:
But it creates as given below with this function " f=(handles.axes_threeDOF)"
I think I found the problem:
f = handles.axes_threeDOF gets the handle of the axes instead of getting the handle of the figure.
Since I don't know the name of your figure, I can't give a perfect solution.
You may try the following options:
1.Find the name of the figure, and use something like: f = handles.figure_threeDOF;
2. Use f = gcf();, assuming there is only one figure.
3. Use f = handles.axes_threeDOF.Parent; assuming the figure is the "Parent" of the axes.
Update:
After im = frame2im(frame);, you need to crop the relevant part of the image:
Something like: im = im(36:884, 928:1800, :);
There are more robust solution than using fixed indices, but it requires some knowledge about the internal structure of the figure.
Here is a code that reproduce the problem (instead of figure handle, f gets axes handle):
numpoints = 500;
x = linspace(0,4*pi,numpoints);
y = square(x);
y2 = 3 +square(x+1);
f = figure;
h = animatedline('Color','b','LineWidth',2);
h2 = animatedline('Color','r','LineWidth',2);
grid on;
axis([0,12,-3,+6])
for k = 1:numpoints
addpoints(h,x(k),y(k))
addpoints(h2,x(k),y2(k))
drawnow
%%% Test %%%
%The following code demonstrates the problem.
%f = gca(), returns a handle to the axes, instead of the figure.
%You should remove the following line for the code to work properly...
f = gca();
%%% Test %%%
% Capture the plot as an image
frame = getframe(f);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
% Write to the GIF File
if k == 1
imwrite(imind,cm,'test.gif','gif', 'Loopcount',inf);
else
imwrite(imind,cm,'test.gif','gif','WriteMode','append');
end
end
Result of correct code (without f = gca();):
Result of wrong code (with f = gca();) - getting axes handle instead of figure handle:
figure;
ax1 = axes;
figure;
ax2 = axes;
x = 0; y = 0;
while ishandle(ax1) && ishandle(ax2)
x = x + 1;
y = y + 1;
figure(1)
scatter(x,y, 'MarkerEdgeColor', 'red')
hold on
figure(2)
scatter(x,y, 'MarkerEdgeColor', 'blue')
hold on
end
In my script I have multiple figures, which are going to be updated in a loop. The figures have to be displayed, while the script is running. Unfortunately the currently updated figure is always popping in the foreground, which makes it impossible to monitor a certain figure. I understand that the calling of figure(1) and figure(2) causes this behaviour, but I how can I plot to these figures, without bringing the window into foreground?
As mikkola suggested in a comment, you can specify to which axes scatter or plot add data points. However, there is a better method: create a single line object, and update its xdata and ydata properties. This is both faster and more memory efficient. Your code would become:
x = 0; y = 0;
figure;
h1 = plot(x,y,'ro');
figure;
h2 = plot(x,y,'bo');
while ishandle(h1) && ishandle(h2)
x = x + 1;
y = y + 1;
h1.XData(end+1) = x;
h1.YData(end+1) = y;
h2.XData(end+1) = x;
h2.YData(end+1) = y;
drawnow
pause(0.1)
end
I keep a set of rules of thumb for when working with MATLAB handle graphics. These are relevant to this question:
Use figure only to create a new figure, or to bring an existing figure to the front (which you want to avoid in general, but sometimes is necessary).
Always specify with which figure or axes you want to work, by keeping and using their handles. I never rely on gcf or gca (not explicitly nor implicitly). Using the current figure is useful when typing on the command line, but in a script or a function there is the real danger than someone clicks randomly on windows while the function is executing. Creating a window then writing to gcf could end up writing to a different figure (really, I click on random things all the time).
Don't create more objects than necessary. Creating a new line object for every point you plot is wasteful.
Note also that plot(...'o') is equivalent to scatter(...) unless you specify a different color or size for each point. But using the dot size or color to specify additional information is not a good way to convey that information. Read Tufte's "The visual display of quantitative information" if you're interested in learning about effective communication through graphs.
The relevant part can be found in the part of the documentation of scatter that includes the input ax:
scatter(ax,___) plots into the axes specified by ax instead of into
the current axes.
This allows the user to specify an axis handle pointing to which axes should be used for drawing the scatter plot. Thus if you skip using figure in your code and use the ax input instead, you avoid the "bring to front" behavior associated with figure.
You can modify your code as follows:
figure;
ax1 = axes;
figure;
ax2 = axes;
x = 0; y = 0;
while ishandle(ax1) && ishandle(ax2)
x = x + 1;
y = y + 1;
scatter(ax1, x,y, 'MarkerEdgeColor', 'red')
hold on
scatter(ax2, x,y, 'MarkerEdgeColor', 'blue')
hold on
end
I have a sequence of MATLAB figures which I want to convert to a video. The figures are composed of 2 subplots, such that each subplot contains an imshow with 2 different plots (red and green) overlayed on it, like the following:
How do I get an image of all the data contained inside each figure, so that next I can convert the sequence of images to a video with VideoWriter?
The key is:
im = frame2im(getframe(h));
Where h is the handle to your plot or axis
You can also pass an optional argument 'rect' to getframe to specify the area to capture.
Here is an example from the MathWorks on how to use getframe in a loop to record the frames. For more information type doc getframe
Z = peaks;
surf(Z)
axis tight manual
ax = gca;
ax.NextPlot = 'replaceChildren';
loops = 40;
F(loops) = struct('cdata',[],'colormap',[]);
for j = 1:loops
X = sin(j*pi/10)*Z;
surf(X,Z)
drawnow
F(j) = getframe(gcf);
end
% Play back the movie two times.
fig = figure;
movie(fig,F,2)
I have written the following code to try and retrieve ONLY the axes and its plot from my MATLAB GUI.
F = getframe(gca);
figure();
image(F.cdata);
saveas(gcf,'PlotPic','png');
close(gcf);
I noticed, however, that this method does not include ANY of my axis labels or title. Is there any way which I can get the getframe function to include the axis labels and title?
I tried the following code but it did exactly the same
pl = plot(x,y);
xlabel('x')
ylabel('y')
ftmp = figure;
atmp = axes;
copyobj(pl,atmp);
saveas(ftmp,'PlotPic.png');
delete(ftmp);
I would do it using the rect option of the getframe function.
Basically you can provide a 2nd input argument to getframe, which then captures the content of the rectangle specified as argument. The nice thing is that you can use the handles to an axes, so it does not capture your whole GUI figure but rather a specific axes.
For example, using this line:
F = getframe(gca,RectanglePosition);
Concretely, you could set the coordinates of the rectangle such that they span both axis labels and the title as well. Here is a sample code. The pushbutton callback executes getframe and opens a new figure with the content of F.cdata:
function GUI_GetFrame
clc
clear
close all
%// Create GUI components
hFigure = figure('Position',[100 100 500 500],'Units','Pixels');
handles.axes1 = axes('Units','Pixels','Position',[60,90,400,300]);
handles.Button = uicontrol('Style','Push','Position',[200 470 60 20],'String','Get frame','Callback',#(s,e) GetFrameCallback);
%// Just create a dummy plot to illustrate
handles.Period = 2*pi;
handles.Frequency = 1/handles.Period;
handles.x = 0:pi/10:2*pi;
handles.y = rand(1)*sin(handles.Period.*handles.x);
plot(handles.x,handles.y,'Parent',handles.axes1)
title('This is a nice title','FontSize',18);
guidata(hFigure,handles); %// Save handles structure of GUI.
function GetFrameCallback(~,~)
handles = guidata(hFigure);
%// Get the position of the axes you are interested in. The 3rd and
%// 4th coordinates are useful (width and height).
AxesPos = get(handles.axes1,'Position');
%// Call getframe with a custom rectangle size.You might need to change this.
F = getframe(gca,[-30 -30 AxesPos(3)+50 AxesPos(4)+80]);
%// Just to display the result
figure()
imshow(F.cdata)
end
end
The GUI looks like this:
And once I press the pushbutton, this is the figure that pops up:
So the only trouble you have is to figure out the dimensions of the rectangle you need to select to capture the axis labels and the title.
Hope that solves your problem!
So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.