Loading...
Logo
Processing Forum
I've included saveFrame() into my sketch in order to create an animated GIF from it. But that a. greatly slows down the sketch and b. results in an equally slow animated GIF. Is that normal ... ? Or how can this be avoided?


And here's the animated GIF:  http://tinypic.com/view.php?pic=rap0yo&s=5

Thank you!

Replies(6)

A very efficient technique is to create separate threads.
One to store the captured images and the other to save them to disk.

I've got an example which just does that from another post.
However, to see it working a 3rd library is necessary.

You can get the idea and adapt to yours. Check it out:
Copy code
    /**
     * QueuedCapturer (v1.47)
     * by  GoToLoop (2013-Jun)
     * for Olga075
     *
     * http://forum.processing.org/topic/save-frames-per-second
     * https://github.com/onformative/ScreenCapturer
     */
    
    import com.onformative.screencapturer.*;
    ScreenCapturer capturer;
    PImage captured;
    
    import java.util.Queue;
    import java.util.concurrent.ConcurrentLinkedQueue;
    final  Queue<PImage> images = new ConcurrentLinkedQueue();
    
    final static int FPS = 30, FREQ = 1000/FPS;
    
    final static String NAME = "Capture-", EXT = ".jpg";
    final static int DIGITS = 4, MAX_SAVES = 300;
    
    boolean isCapturing, isBusy, isClosingDown;
    int numbering;
    
    void setup() {
      size(800, 600);
      frameRate(FPS);
    
      capturer = new ScreenCapturer(width, height, FPS);
      mousePressed();  // starts threads
    
      while (captured == null)  delay(100); // awaits for 1st snap
    }
    
    void draw() {
      image(captured, 0, 0);  // displays latest snapshot
      displayInfo();
      if (numbering + images.size() >= MAX_SAVES)   exit();
    }
    
    void mousePressed() {
      if (isClosingDown)
        println("Program is closing down. Calm down!");
    
      else if (isBusy) {
        print("Sorry, still emptying buffer! ");
        println("Wait some more and re-click to resume...");
      }
    
      else if (isCapturing = !isCapturing) {
        thread("enqueueImages");
        thread("saveImages");
        println("Screen capturing has started!!!");
      }
    
      else {
        isBusy = true; // halts snapshot resume till queue's empty
        println("Capturing paused! Click again to resume...");
      }
    }
    
    void displayInfo() {
      frame.setTitle( "FPS: " + round(frameRate)
        + "\t\t\tSaved : " + numbering
        + "\t\tBuffer: " + images.size() );
    }
    
    void enqueueImages() {
      final Queue imgs = images; // local caching
    
      while (isCapturing) {
        imgs.add( captured = capturer.getImage() );
        delay(FREQ); // awaits for next snapshot
      }
    }
    
    void saveImages() {
      final Queue<PImage> imgs = images; // local caching
      PImage img;
      final String dir = dataPath(NAME);
    
      while (isCapturing || !imgs.isEmpty ()) {
        delay(100); // slows down choking thread-shared variables
    
        while ( (img = imgs.poll ()) != null )
          img.save(dir + nf(++numbering, DIGITS) + EXT);
      }
    
      delay(100); // avoids a racing modification of next line's var.
      isBusy = false; // queue's empty & threads are now free to resume
    }
    
    @Override void exit() {
      if (isClosingDown)  return; // exit() invoked already.
    
      isClosingDown = isBusy = true;
      isCapturing = false;
    
      println("Emptying queued image buffer! Please wait...");
      while (isBusy)  delay(200);
    
      println("Finished!!!");
      super.exit();
    }
    
saveFrame() does a disk I/O operation. Disk I/O is still a slow operation today, so it is normal to see a delay introduced in your sketch.
As GoToLoop shows, there is a workaround consisting in saving the frame in another thread: if it is fast enough (ie. about the same speed than building the next frame), it can help a bit; otherwise, you will still see some congestion, if the draw() loop must wait for the save thread to finish its work on the current frame before going on with the next one.

Basically, when you do saveFrame(), don't expect your program to be realtime. It is only appropriate for batch processing of the resulting images.
Ok, but otherwise, what's the best way to turn Processing sketches into animated GIFs? How do those who got all those smooth, fast-running aGIFs do it?
Basically I just played around with making GIFs. And it looks like problem is in the fact that neither browser neither my image-viewer are able to play GIF images with frame rate higher than let's say 7-9 FPS.

Here's link to the GIF i generated from the "red-black" sketch using ImageMagik

this is link to gif  with "0" specified as delay between frames.
http://languagekings.com/processing/redblack_nodelay.gif  ( 357 frames in total about 5.5Mb file)

this is link to gif with "10milliseconds" specified as delay between frames (equal to 100 FPS)


on my machine both of them (when viewing in Chrome) play at around 7FPS

So looks like the problem may be limitations of players rather than GIF-maker.

EDIT2: 

So I decided to try to use only 40 frames from the animation to make GIF. From what I can see on my machine, there's NO perfomance improvement. (At least no significant. Or maybe I am just tired of watching those circles for 1h)...





EDIT:
Here's link to Image Magik page of how to compress GIFs:

As to "fast-running GIFs" - i guess they're very low resolution, eg. 128x128 or smth. Or maybe on top of small dimensions, they have only 20-30 frames.


WHILST TRYING TO FIGURE OUT WHAT WAS WRONG I STARTED DRAFT OF THE POST, MAYBE SOME INFORMATION MAY BE USEFUL:

The Issue of saveImage() slowing down execution is ONLY related to interactive sketches or sketches which rely on clock-related animation.

The original "red/black" sketch on openprocessing is neither. It doesn't use millis()  and thus it's not "clock-related". It just adjusts it's position by frame.

Because of that if you just saveFrame() all of the sequence into a folder you will get perfect and smooth sequence of each and every frame without any animation being lost.

As far I can see the resulting GIF has two separate problems:
a) it's slower than the original sketch
To solve this one, it may be better to simply save frames with saveFrame() and after that just convert them into GIF with some GIF-making tool which allows to specify framerate. Eg. ImageMagik command line utility.

In case of this sketch the writing to disk is almost not a problem (because these are red/black images which can compress well), the problem is the converting them to GIF. I tried to use ImageMagik to compress like For me to compress 357 frames (less than 10 seconds of running of the sketch). And ImageMagik (a command line utility written in C++ tool over 30 seconds running all my CPU cores at 100%).  Maybe processing GIF-export library is not so sophisticated as ImageMagik, but we can anyways assume that converting to GIF (palette making, LZW compression) may take some time.


b) the colors are not as smooth as in the original sketch
This one comes from the fact that GIF was invented in 1987. It's VERY old image format, which somehow managed to survive till today.
It only allows to have 256 color-pallete and this is why your colors may not be as smooth as you want. Again there's no much of solution to this.
Also transparency is not represented as alpha/mask, but just as 1 color of 256 color palette, which is rendered as fully transparent dot. (in plain english: dot which is not rendered at all)




Hi there, thanks a lot for all that work and help! :)

I'm wondering right now if maybe I should only save, let's say, every third frame, wouldn't that speed up the animation too (at the expense of it being a bit more choppy)? Can't try it right now though.
Actually that seems totally right! I guess that's really how they create this effect of "fast-moving GIFs" which as our experiments have shown have limited framerate. Kudos!