Animation of figure with subplots using VideoWriter in MATLAB - matlab

I'm trying to create an animation file in MATLAB from a figure with 2 subplots using the VideoWriter. However, the avi file that I get includes only one of the subplots.
Here is the code:
clc
clear
vidObj = VideoWriter('randdata');
open(vidObj)
figure (1)
for i = 1:100
clf
subplot(1,2,1)
imagesc(rand(100))
subplot(1,2,2)
imagesc(rand(100))
drawnow
CF = getframe;
writeVideo(vidObj,CF);
end
There must be something simple going wrong here but I don't know what. I would like to capture the entire figure.

The documentation for getframe states in the first line:
F = getframe captures the current axes
So it's capturing the axes, not figure.
You want to use it as also specified in the documentation
F = getframe(fig) captures the figure identified by fig. Specify a figure if you want to capture the entire interior of the figure window, including the axes title, labels, and tick marks. The captured movie frame does not include the figure menu and tool bars.
So your code should be
clc; clear
vidObj = VideoWriter('randdata');
open(vidObj);
figure(1);
for ii = 1:100
clf
subplot(1,2,1)
imagesc(rand(100))
subplot(1,2,2)
imagesc(rand(100))
drawnow;
% The gcf is key. You could have also used 'f = figure' earlier, and getframe(f)
CF = getframe(gcf);
writeVideo(vidObj,CF);
end
For ease, you might want to just get a File Exchange function like the popular (and simple) gif to create an animated gif.

Related

Showing multiple .fig files in one plot

I am trying to write a relatively simple function which allows me to plot any numbers of figures (previously saved as .fig files) one close to the other.
I have looked for the solution in the website, but it does not work for me.
Moreover, I am almost there with my code, since the outputs are almost what I want: indeed I get the two figures in the right position, but in two separate windows and a third window which is correctly merging the two inputs, but they look weird, with a lower resolution! So I get three outputs in total.
Here is my code, I hope you can help me.
(Try with your own .fig files and check if you also have the three wrong outputs like me).
function SubPlotFig (varargin)
for i = 1:nargin
hf = hgload(varargin{i});
ax(i) = findobj(hf,'Type','axes');
end
hc = figure;
for i = 1:nargin
subplot(1,2,i,ax(i));
copyobj(ax(i),hc);
end
Attachment_1
Attachment_2
When you call hgload, it will open a figure from a .fig file and display it. You are doing this inside of your first loop, so you will see a figure for each of the inputs. The figure that you see is exactly what you saved for each figure.
for i = 1:nargin
hf = hgload(varargin{i}); % <---- Creates a figure
ax(i) = findobj(hf,'Type','axes');
end
In the second loop, you create a subplot for each of the axes within the figures that you just opened. These of course are going to be smaller because you are now putting multiple axes within a figure that is the default size. They aren't really "low resolution", just smaller on the screen. If you want to make them bigger, then you'll want to increase your figure size.
% Create all of the subplots
hc = figure;
for i = 1:nargin
hax = subplot(1,2,i,ax(i));
copyobj(ax(i),hc);
colorbar(hax);
end
% Make sure we are using the jet colormap
colormap(jet)
% Get the current figure position
pos = get(hc, 'Position');
% Double the width since you now have two plots
set(hc, 'Position', [pos(1:2) pos(3)*2, pos(4)])
The problem was the colormap. Now it is solved. Here is the correct code, can be useful for others :)
function SubPlotFig (varargin)
for i = 1:nargin
hf = openfig(varargin{i},'reuse');
cm = colormap;
c(i) = findobj(hf,'Type','Colorbar');
ax(i) = findobj(hf,'Type','Axes');
end
hc = figure;
for i = 1:nargin
subplot(1,2,i,ax(i));
copyobj(ax(i),hc);
colormap(ax(i),cm);
copyobj([c(i),ax(i)],hc);
end

Two saved figures, want them to show in a single graph in MATLAB

Let's say I have two figures stored in separate files A.fig and B.fig which contain two separate plots. Is there a way to load A.fig and then do something like hold on and then load B.fig in the figure created for A.fig so that I have both plots in the same axes?
I think the question is not really a duplicate of this one. The OP does not ask for a way to extract the data but for a way to combine the two stored figures. Admittedly, he could extract the data and plot it again. But there is a more elegant solution...
The actual plots are children of axes which is a child of figure. Therefore you can achieve what you want by copying the children of the second axes into the first axes with copyobj. Before that, load the figures with openfig. This method has the advantage to copy different types of 'plots' (line, area, ...).
The code to copy from B.fig to A.fig is as follows and works starting from R2014b:
fig1 = openfig('A');
fig2 = openfig('B', 'invisible');
copyobj(fig2.Children.Children, fig1.Children);
If you have a Matlab version prior to R2014b, you need to use the set and get functions since you cannot use .-notation. More information can be found here. You can either use gca to get the current axes after loading the figure like this:
fig1 = openfig('A');
ax1 = gca;
fig2 = openfig('B', 'invisible');
ax2 = gca;
copyobj(get(ax2,'children'), ax1);
... or get them manually from the figure-handle like this:
fig1 = openfig('A');
fig2 = openfig('B', 'invisible');
copyobj(get(get(fig2,'children'),'children'), get(fig1,'children'));
The following script creates two figures and then applies the above code to combine them.
If you are have Matlab version R2013b or higher, replace hgsave with savefig as suggested in the documentation.
%% create two figure files
x = linspace(0,2*pi,100);
figure; hold on;
plot(x,sin(x),'b');
area(x,0.5*sin(x));
set(gca,'xlim',[0,2*pi]);
hgsave('A');
figure; hold on;
plot(x,cos(x),'r');
area(x,0.5*cos(x),'FaceColor','r');
hgsave('B');
%% clear and close all
clear;
close all;
%% copy process
fig1 = openfig('A');
fig2 = openfig('B', 'invisible');
copyobj(get(get(fig2,'children'),'children'), get(fig1,'children'));
close(fig2);
This gives the following result if manually combined in subplots:

Movie in Matlab is cropped and doesn't show the full figure

I am learning to make a movie in Matlab. I coped this piece of code from internet to make the movie. I am using OS X Mavericks and Matlab 2013a. I usually dock the figure windows. When I run the code I can see animation perfectly. When when I save the movie either by using movie2avi or VideoWriter command. It doesn't record the movie with full frame of figure. Instead I see a little bit of the figure window and my Matlab desktop view. Can anyone help me with this?
clear;
close all;
% Creates a 2D Mesh to plot surface
x=linspace(0,1,100);
[X,Y] = meshgrid(x,x);
N=100; % Number of frames
for i = 1:N
% Example of plot
Z = sin(2*pi*(X-i/N)).*sin(2*pi*(Y-i/N));
surf(X,Y,Z)
% Store the frame
M(i)=getframe(gca); % leaving gcf out crops the frame in the movie.
end
% myVideo = VideoWriter('wavemotion.avi');
% open(myVideo);
% writeVideo(myVideo,M)
% close(myVideo)
% Output the movie as an avi file
%movie2avi(M,'WaveMovie.avi');
Based on the documentation of both VideoWriter and movie2avi, you need to add a few more instructions to your code.
First, you need to set the renderer for a nice display and set the axes that the next plot will reset only the children and not the entire axes:
set(gca,'nextplot','replacechildren');
set(gcf,'Renderer','zbuffer');
There is also an advice to preallocate the frame buffer:
M(N) = struct('cdata',[],'colormap',[]);
Finally, you just need to call getframe without any argmuent, as gca is the default argument.
On my computer (with Matlab 2013b), with these modifications and using VideoWriter, it works fine. With movie2avi, it does not work properly; I think I need to specify a compression codec to fix that.

MATLAB getframe captures whatever is on screen

I am trying to create a movie from my MATLAB plot. When I call getframe, it "usually" captures the plot image, but sometimes if there is something else active on the screen (which is normal if I continue to use the computer) it captures whatever window is active. Is there another way to grab the image of the active figure?
e.g.
fig = figure;
aviobj = avifile('sample.avi','compression','None');
for i=1:t
clf(fig);
plot(...); % some arbitrary plotting
hold on;
plot(...); % some other arbitrary plotting
axis([0 50 0 50]);
aviobj = addframe(aviobj, getframe(fig));
end
aviobj = close(aviobj);
OK, found the solution; instead of
aviobj = addframe(aviobj, getframe(fig));
sending the figure handle directly to addframe is enough:
aviobj = addframe(aviobj, fig);
The Matlab people are apparently phasing out the avifile and addframe functions in future releases, replacing them with VideoWriter and writeVideo respectively. Unfortunately, this means that the accepted answer will no longer work, since writeVideo doesn't accept the figure handle as the argument.
I've played around with it a bit, and for future reference, the same thing can be accomplished using the undocumented hardcopy function. The following code works perfectly for me, with the added benefit of not even having a plot window pop up, so it does everything completely in the background:
fig = figure('Visible','off');
set(fig,'PaperPositionMode','auto');
writerobj = VideoWriter('sample.avi','Uncompressed AVI');
open(writerobj);
for i=1:t
clf(fig);
plot(...); % some arbitrary plotting
hold on;
plot(...); % some other arbitrary plotting
axis([0 50 0 50]);
figinfo = hardcopy(fig,'-dzbuffer','-r0');
writeVideo(writerobj, im2frame(figinfo));
end
close(writerobj);
You can pass the handle of the desired figure or axis to GETFRAME to ensure that it doesn't capture another window.
I may depend on the renderer you're using. If it's 'painters', then you should be OK, but if it's anything else, such as 'OpenGL', then I think it has to get the frame data from the graphics card, which means that if you have something overlapping the window, then that may end up in the output of getframe.
As someone has already stated you don't have to use getframe, however if you insist on using it you can use
set(fig,'Renderer','zbuffer')
and this should fix your issue.
If you use getframe in many subplots, try to add at the end:
I think the get frame works fine it just the rendering a bit was unpositioned.
clf(fig)
% use 1st frame to get dimensions
[h, w, p] = size(frames(1).cdata);
hf = figure;
% resize figure based on frame's w x h, and place at (150, 150)
set(hf,'Position', [150 150 w h]);
axis off
% Place frames at bottom left
movie(hf,frames,4,30,[0 0 0 0]);
close(gcf)

Show more than one figure one by one in MATLAB

I have 20 figures to display one after another like slide show. Can I do it using Imshow in Matlab? Any sort of help will be appreciated.
Couple options:
Open a figure for each plot
Open and close a figure for each plot
Reuse one figure
Open a figure for each plot
for i=1:20
h = figure;
%plot here
pause
end
Open and close a figure for each plot
for i=1:20
h = figure;
%plot
pause
close gcf
end
Reuse one figure
h=figure
for i=1:20
clf(h);
%plot
pause
end
OR depending on what you are plotting, you can use the refreshdata method.
If you use #Jonas' method, and if you have dual monitors, you have to force the figure to the main monitor for getframe to actually work, as per. You can do this via:
ff=figure;
movegui(ff)
You can use MOVIE to display plots/images one after the other. For this, you create the figures, capture them via GETFRAME, and then you can call movie. See this example from the help for getframe
Z = peaks; surf(Z)
axis tight
set(gca,'NextPlot','replacechildren');
for j = 1:20
surf(sin(2*pi*j/20)*Z,Z)
F(j) = getframe;
end
movie(F,20) % Play the movie twenty times