Creating animation with multiple plots in Octave
Asked Answered
F

4

5

I'm using Octave to write a script that plots a function at different time periods. I was hoping to create an animation of the plots in order to see the changes through time.

Is there a way to save each plot for each time point, so that all plots can be combined to create this animation?

Fleecy answered 7/4, 2015 at 13:41 Comment(3)
In MATLAB, there is the getframe function. Does this also exist in Octave?Ammoniate
Use a loop and update the plot at each iteration?Jitters
I am doing that but I am simply plotting each function on the same plot (not animated). Also, this doesn't allow me to save the sequential plots as an animationFleecy
G
5

It's a bit of kludge, but you can do the following (works here with octave 4.0.0-rc2):

x = (-5:.1:5);
for p = 1:5
  plot (x, x.^p)
  print animation.pdf -append
endfor
im = imread ("animation.pdf", "Index", "all");
imwrite (im, "animation.gif", "DelayTime", .5)

Basically, print all your plots into a pdf, one per page. Then read the pdf's as images and print them back as gifs. This will not work on Matlab (its imread implementation can't handle pdf).

Glisten answered 7/4, 2015 at 15:56 Comment(7)
Hmm ok not quite what I'm looking for.... I'd like to save it into some sort of movie file as well to add to a presentationFleecy
@Fleecy define video file? You don't have sound, only image, so a gif should do well. It should also appear as an animation inside a presentation. Note that you can use LoopCount so it doesn't cycle the animation, and even use an array as DelayTime for different time intervals. What's your problem with the solution exactly?Glisten
What I found that seems to be an easy way to do this is using the two lines: drawnow; pause(1/20);Fleecy
@Fleecy yes. But that makes the plot appear as an animation during an octave session. That's easy and not what you asked. The question was to generate the animation file, the gif file that my answer creates, which then can be seen anywhere, with and without Octave.Glisten
Yes right, I just realised that. In this way though is it possible to save it to a gif? The gif suggested before really was too clunky and had an issue with orientationFleecy
@Fleecy after imread, you will get a 4d matrix. The 3rd dimension are the RGB channels, and the 4th is time. Just rotate it for how much you need. You can also adjust time, use 1/20 for DelayTime. Also, it works fine on my system so this may be an issue of your graphics_ttolkit or Octave version. I was using octave 4.0.0-rc2 and my graphics_toolkit is qt.Glisten
print animation.pdf -append causes Octave 4.2.1 to crush...this is weird.Tai
E
5

This creates an animated gif

data=rand(100,100,20); %100 by 100 and 20 frames

%data go from 0 to 1, so lets convert to 8 bit unsigned integers for saving
data=data*2^8;
data=uint8(data);

%Write the first frame to a file named animGif.gif
imwrite(data(:,:,1),'/tmp/animGif.gif','gif','writemode','overwrite',...
        'LoopCount',inf,'DelayTime',0);

%Loop through and write the rest of the frames
for ii=2:size(data,3)
     imwrite(data(:,:,ii),'/tmp/animGif.gif','gif','writemode','append','DelayTime',0)
end
Etem answered 13/5, 2016 at 18:58 Comment(0)
S
4

Had to come chime in here because this was the top Google result for me when I was looking for help with this. I had issues with both answers, and some other issues, too. Notably:

  1. For Rick T's answer, the code snippet doesn't write a plot figure, it just writes matrix data. Getting the plot window was a pain.
  2. For carandraug's answer, writing to a PDF took a very long time and made a gigantic PDF.
  3. On my own machine, I'm pretty sure I used apt-install to get Octave, but the getframe function I found referenced in other answers wasn't found. Turns out I had installed version 4.4, which was from 2018 (>3 years old).

I removed the old version of Octave sudo apt remove octave, then installed the new version with snap. If you try octave from a terminal without it installed it should prompt you to the snap install - be sure to include the # 6.4.0 or whatever is included in the command.

Once I had the current version installed, I got access to the getframe command, which is what lets you convert directly from a figure handle to image data - this bypasses the hackey (but previously necessary step) in @carandraug's answer where you had to write to PDF or some other image as a placeholder.

I used @RickT's answer to make my own MakeGif function, which I will share with you all here. Note that MakeGif stores the filename in a persistent variable, meaning it is retained across calls. If you change the filename it will make (or overwrite!!) the new file. If you need to overwrite the current file (i.e., running the same script multiple times and want new results) then you can use clear MakeGif between calls and that will reset the persistentFilename.

Here is the code for the MakeGif function; code to test it with is provided after this:

function MakeGif(figHandle, filename)
  persistent persistentFilename = [];
  if isempty(filename)
    error('Can''t have an empty filename!');
  endif
  if ~ishandle(figHandle)
    error('Call MakeGif(figHandle, filename); no valid figHandle was passed!');
  endif
  writeMode = 'Append';
  if isempty(persistentFilename)|(filename!=persistentFilename)
    persistentFilename = filename;
    writeMode = 'Overwrite';
  endif
  imstruct = getframe(figHandle);
  imwrite(imstruct.cdata, filename, 'gif', 'WriteMode',writeMode,'DelayTime',0);
endfunction

And here is the code to test the function. There's a commented-out call to clear MakeGif between the blue and green colors. If you leave it commented out it will append the green sine wave to the blue sine wave, resulting in alternating colors after every cycle - again the filename is persistent in the function. If you uncomment that call then the MakeGif function will treat the green's call as "new" and trigger the overwrite of the blue sine wave and all you'll see is green.

clear all;
time = 0:0.1:2*pi;
nSamples = numel(time);
figHandle = figure(1);
for i=1:nSamples
  plot(time,sin(time + time(i)),'Color','blue');
  drawnow;
  MakeGif(figHandle, 'test.gif');
endfor

% Uncomment the 'clear' command below to clear the MakeGif persistent
% memory, which will trigger the green sine wave to overwrite the blue.
% Default behavior is to APPEND a green sine wave because the filename
% is the same.
%clear MakeGif;

for i=1:nSamples
  plot(time,sin(time + time(i)),'Color','green');
  drawnow;
  MakeGif(figHandle, 'test.gif');
endfor

I spent several hours on this after being super dissatisfied with laggy screen captures so I really hope this helps someone in the future! Good luck and best wishes from the Age of Covid lol.

Selfconfessed answered 7/1, 2022 at 20:58 Comment(2)
Final note here (don't want to keep bumping the answer with edits) - the getframe function needs a figure handle, not a plot handle. Note the test frame function is using figHandle = figure(1) and not figHandle = plot(time, ....Selfconfessed
You could add code in your MakeGif function to verify the handle is of a figure, if not, fetch its parent.Uphill
H
0

@Chuck thanks for that code; I've been using it to save 1500-frame GIFs of simulation output, and I find that after maybe ~500 frames the time to save the next frame to the output during the call to MakeGif starts to become ... unnerving. I guess imwrite reads and writes the entirety of the output file at each call that includes the 'WriteMode','Append' pair. At frame 1500 my output is 480Mb so that becomes untenable.

An apparent rescue for this is hinted at in the doc for Octave 7.1.0's imwrite, with the suggestion that you can pass it a 4-dimensional array and write the entire image sequence with one call. I haven't been able to make this work, though: Calling imwrite that way seems to simply write the very first image in the sequence into every frame in the output file.

Help answered 16/8, 2022 at 20:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.