Generating Animations in Matlab (AVI files) withou

2019-07-04 03:08发布

问题:

I have been trying to generate animation files (.AVI files) in parallel remotely on a cluster via shell script with Matlab. So far this has been an impossible task. I have all but given up, and have just accepted that I will need to generate the animations on my laptop overnight for 8 hours or so. But I’m hoping that someone out there knows how to fix this, or that I can stop someone from wasting a day of trial and error.

I have generated a large number of two dimensional probability distributions numerically (via Matlab) and I want to plot these surfaces as a function of time. I know that I could probably try to output a bunch of .jpegs to file and use another program such as MEncoder or something, but dammit, this is something Matlab should be able to do.

I have access to a nice cluster with lots of nodes and want to generate animations in parallel via shell script:

/usr/local/MATLAB/R2013b/bin/matlab -nojvm -nodisplay -r “TestFile1”

I know that the switch -nojvm will kill Java, so I omit this:

/usr/local/MATLAB/R2013b/bin/matlab -nodisplay -r “TestFile1”

I will go through the progression of my code below, first running code on my laptop (new MacBook Pro) successfully and then failing when trying to run the same code on the cluster. Hopefully someone with more experience with Matlab will notice my error and set me on the correct path.

First I have had success doing this with movie2avi, a simple example for which is shown below:

%%————————————————————————%%
clear Mov
clear rect
rect = get(gcf,'Position');
rect(1:2) = [0 0];
figure(1)
for ii=1:100
clf;
meshc(((ii+75)/100).*peaks)
grid on
axis([0 50 0 50 -20 20 -10 10])
colorbar
shading interp 
Mov(:,ii) = getframe(gcf,rect);
end 
movie2avi(Mov, 'test0.avi', 'compression','None','fps',30); % fps = frames per second
%%————————————————————————%%

The problem with this method is that it uses getframe which requires the plots to be generated on the screen.

Second After a lot of trial and error I was able to generate this .AVI file on my laptop without displaying the figure using figure('visible','off') and addframe.

%%————————————————————————%%
aviobj=avifile('test1.avi','compression','None');  
hf= figure('visible','off');                              
for ii=1:100
clf;
meshc(((ii+75)/100).*peaks)
grid on
axis([0 50 0 50 -20 20 -10 10])
colorbar
shading interp 
aviobj=addframe(aviobj,hf);
end  
aviobj=close(aviobj); 
close(hf);
exit      
%%————————————————————————%%

As I mentioned above, this works quite nicely on my laptop, but is met with the Matlab message:

Warning: AVIFILE will be removed in a future release. Use VIDEOWRITER instead.

When I try to run this on the cluster the error message is:

Error using avifile/addframe>ValidateFrame (line 287) Frame must be 660 by 520.

So I go back to my laptop and print aviobj for each loop iteration and see that the frame size is always Width: 560, Height: 419. At this point I figure maybe Matlab just doesn’t like AVIFILE so instead I should try to use VIDEOWRITER.

Third I found some examples on the web (via the Matlab website and stackoverflow) that generated animations with VIDEOWRITER, but for the life of me I cannot work out how to use this function without also using getframe, so a figure is always displayed. One example that works on my laptop, showing the figure before the .AVI file is generated is shown below:

%%————————————————————————%%
writerObj = VideoWriter('test2.avi');
open(writerObj);
Z = peaks; surf(Z); 
axis tight
set(gca,'nextplot','replacechildren');
set(gcf,'Renderer','zbuffer');
for k = 1:100 
   surf(sin(2*pi*k/20)*Z,Z)
   frame = getframe;
   writeVideo(writerObj,frame);
end
close(writerObj);
%%————————————————————————%%

Of course when I run this on the cluster I get the expected error:

getframe requires a valid figure window

I’ve seen plenty of examples on stackoverflow of people attempting to use VIDEOWRITER with im2frame instead of getframe, but none of these work and I beginning to think that VIDEOWRITER is incompatible with im2frame.

Is there anyone out there that has solved a similar issue, and if so can you give me a simple example to try to run remotely?

EDIT:

Thanks nkjt, with your advice I was able to create an .AVI file from a series of figures that were saved to disk. I have put my code below for anyone that might be interested. This worked on both my laptop and the cluster I have access to. Though the cluster had some issues with the size of the figure, something I was not able to resolve. Also, when I used this code to write the figures to disk for the actual case I was interesting in, I got the error:

Segmentation fault (core dumped),

something that seems quite common when generating figures with the -nodisplay switch. So I have given up and have instead generated my animations overnight on my laptop. It may be time to start using python instead of Matlab…

To write the plots to file:

%%————————————————————————%%
[X,Y] = meshgrid(-3:0.1:3);
Z = peaks(X,Y);
set(gcf,'Visible','off');
for ii=1:100
filename1 = [ 'WTest', num2str(ii),'a.jpg' ];
clf;
meshc(X,Y,((ii+75)/100).*Z)
grid on
axis([-3 3 -3 3 -20 20 -10 10])
colorbar
saveas(gcf,filename1,'jpg');
end
%%————————————————————————%%

To take the saved plot files and make an .AVI file:

%%————————————————————————%%
% Take plots to file and make .AVI file
vidObj = VideoWriter('Wtest.avi');
vidObj.Quality = 100;   % 0 -- 100
vidObj.FrameRate = 10;  % fps
open(vidObj);
set(gcf,'Visible','off');
for ii = 1:100
    clf;
filename1 = [ 'WTest', num2str(ii),'a.jpg' ];
    img = imread(filename1);
    writeVideo(vidObj,img);
end
close(vidObj);
%%————————————————————————%%

回答1:

If you are able to write your individual frames to images in headless mode*, VideoWriter will take image data directly, there's no need to use im2frame. However, you will have to loop over the written out images, read them in, add them to the movie, and so on, so slightly clunky.

Assuming here that fnames contains all the frames in order (frame001.jpg and so on) and output is your video already created with VideoWriter:

for n = 1:length(fnames)
    img = imread(fnames(n,:));
    writeVideo(output,img);
end
close(output);

*sidenote: not always as easy as it first appears. This may be the source of your issue with running avifile/addframe on the cluster. If I were you, I would first make sure I could satisfactorily replicate a single image written out on the laptop with display, on the laptop without display, and on the cluster. Default figure properties may change between machines and the choice of renderer (including hardware vs. software OpenGL) can introduce some weird effects.



回答2:

The figure must exist, but I use set(f,'Visible', 'off') to hide the plotting, which does speed things up. I have not tested this on a cluster yet because it runs pretty fast on my local machine, but it seems pretty similar to what o.p. got working on the cluster. If you are having too much trouble rendering, I would suggest trying to reduce the number of data points to something reasonable for your video resolution.
As a bonus, this version has not seg faulted on me, and it should run with less memory and time than it takes to save a bunch of separate pictures.

f=figure;
set(f,'Visible', 'off');%'animating in background
a=gca;
set(a,'DataAspectRatio',[1 1 1])
daspect('manual')
axis manual
hold on
ylabel('y');
title('title');
writerObj = VideoWriter(fullfile(PATH,FILENAME),'Motion JPEG AVI');
writerObj.Quality = 100;
writerObj.FrameRate = RATE;%frames per sec
open(writerObj);    
for j = 1:NUMBER_OF_FRAMES
    cla(a);
    axis([x1 x2 y1 y2])%useful to specify sometimes    
    %%PLOT YOUR STUFF NOW
    DYNAMIC_LABEL = sprintf('%s\n%s',LABEL,FOOTNOTE)
    xlabel(DYNAMIC_LABEL)%I like to use this as a changing footnote
    drawnow;
    frame = getframe(f);
    writeVideo(writerObj,frame);   
end
close(f)
daspect('auto')
close(writerObj);