Animate .gif images in R / ImageMagick

21Nov10

Yesterday I surfed the web looking for 3D wireframe examples to explain linear models in class. I stumbled across this site where animated 3D wireframe plots are outputted by SAS.  Below I did something similar in R. This post shows the few steps of how to create an animated .gif file using R and ImageMagick. Here I assume that you have ImageMagick installed on your computer. As far as I know it is also possible to produce animated .gif files using R only, e.g. with write.gif() from the caTools package. But using ImageMagick is straighforward, gives you control over the conversion and .gif production and is the free standard program for conversion.

First a simple countdown example. To be sure not to overwrite anything I will create a new folder and set the working directory to the new folder.

dir.create("examples")
setwd("examples")

# example 1: simple animated countdown from 10 to "GO!".
png(file="example%02d.png", width=200, height=200)
  for (i in c(10:1, "G0!")){
    plot.new()
    text(.5, .5, i, cex = 6)
  }
dev.off()

# convert the .png files to one .gif file using ImageMagick. 
# The system() function executes the command as if it was done
# in the terminal. the -delay flag sets the time between showing
# the frames, i.e. the speed of the animation.
system("convert -delay 80 *.png example_1.gif")

# to not leave the directory with the single jpeg files
# I remove them.
file.remove(list.files(pattern=".png"))

Above a loop is used to do the plotting. A new .png file for each plot is created automatically. The "%02d" part in the  filenamepart is a placeholder here for a two character counter (01,02 etc.). So we do not have to hard-code the filename each time.

Now I want a linear model to be visualized as a 3d mesh.  A 3D surface can easily be plotted using the wireframe() function from the lattice package (or other functions available in R; also see the rgl package for rotatable 3D output).

library(lattice)
b0 <- 10
b1 <- .5
b2 <- .3
g <- expand.grid(x = 1:20, y = 1:20)
g$z <- b0 + b1*g$x + b2*g$y
wireframe(z ~ x * y, data = g)

# to rotate the plot
wireframe(z ~ x * y, data = g,
          screen = list(z = 10, x = -60))

Now let’s create multiple files while changing the rotation angle. Note that wireframe() returns a trellis object which needs to be printed explicitly here using print(). As the code below produces over 150 images and merges them into one .gif file note that this may take a minute or two.

# example 2
png(file="example%03d.png", width=300, heigh=300)
  for (i in seq(0, 350 , 10)){
    print(wireframe(z ~ x * y, data = g,
              screen = list(z = i, x = -60)))
  }
dev.off()
# convert pngs to one gif using ImageMagick
system("convert -delay 40 *.png example_2_reduced.gif")

# cleaning up
file.remove(list.files(pattern=".png"))

Now I want the same as above but for a model with an interaction and I want to make the plot a bit more pretty. This time I use .pdf as output file. This is just to demonstrate that other formats than .png can be used. Note that the "%02d" part of the filename has disappeared as I only create one .pdf file with multiple pages, not multiple .pdf files.

# example 3
b0 <- 10
b1 <- .5
b2 <- .3
int12 <- .2
g <- expand.grid(x = 1:20, y = 1:20)
g$z <- b0 + b1*g$x + b2*g$y + int12*g$x*g$y

pdf(file="example_3.pdf", width=4, height=4)
  for (i in seq(0, 350 ,10)){
    print(wireframe(z ~ x * y, data = g,
              screen = list(z = i, x = -60),
              drape=TRUE))
  }
dev.off()
# convert pdf to gif using ImageMagick
system("convert -delay 40 *.pdf example_3_reduced.gif")
# cleaning up
file.remove(list.files(pattern=".pdf"))

The last example is a visual comparison of the interaction and a non-interaction model. Here we now have the models on the same scale. Before I did not specify the scale limits.

# example 4
b0 <- 10
b1 <- .5
b2 <- .3
int12 <- .2
g <- expand.grid(x = 1:20, y = 1:20)
z <- c( b0 + b1*g$x + b2*g$y,
        b0 + b1*g$x + b2*g$y + int12*g$x*g$y)
g <-rbind(g, g)
g$z <- z
g$group <- gl(2, nrow(g)/2, labels=c("interaction", "no interaction"))

png(file="example%03d.png", width=300, height=300)
  for (i in seq(0, 350 ,10)){
    print(wireframe(z ~ x * y, data = g, groups=group,
              screen = list(z = i, x = -60)))
  }
dev.off()
# convert pngs to one gif using ImageMagick
system("convert -delay 40 *.png example_4.gif")

# cleaning up
file.remove(list.files(pattern=".png"))

Above I chose a small image size (300 x 300 pts). Smaller steps for rotation and a bigger picture size increases file sizes for examples 2, 3 and 4 to 3-5mb which is far too big for a web format. I am not familiar with image optimization and I suppose a smaller file sizes for the .gif file can easily be achieved by some optimization flags in ImageMagick. Any hints are welcome!



17 Responses to “Animate .gif images in R / ImageMagick”

  1. Very nice post. I have yet to try this, but is there a way to make the transitions smoother — perhaps, many transition images?

    Also, have you attempted this with ggplot2 to see if the output is pleasing to the eye?

    Thanks.

    John Ramey

  2. Very nice Mark,

    For future reference, it might be worth to you to have a look at the “animation” package by Yihui.

    He also created a very nice gallery of R animations:
    http://animation.yihui.name/

    Cheers,
    Tal

  3. Okay, that’s useful. I just used it to create a transition between two plots, for use in talks.

    But something is odd. If I create an animation going via png images (http://astro.dur.ac.uk/~rjsmith/ages_anim_png.gif), the axes and labels seem to get “bolder” as the animation proceeds, as though they are being over-plotted every time.

    By contrast if I use jpeg for the intermediate files, this doesn’t happen, everything is fine (
    http://astro.dur.ac.uk/~rjsmith/ages_anim_jpg.gif)

    Is there any explanation for that?

  4. 4 markheckmann

    Hi Russell,
    I have not had this effect and am no expert at image processing or IM, so I am afraid I cannot help you with this.
    One thing I could imagine is that some default settings of ImageMagick might be different on your machine.
    As a casual user of IM I do not know more about that nor if default settings can be changed at all…
    Mark

  5. 5 Ben Bolker

    You might also want to check out movie3d() in the rgl package.

  6. 6 Mikael

    I noticed that if you are using OS X 10.6 and ImageMagick installed by the fink package management system, R can’t find the convert CL tool. You have to spell out the whole path, ie write

    system(“/sw/bin/convert -delay 20 *.png example.gif”)

    instead of

    system(“convert -delay 20 *.png example.gif”)

    Kind of odd..

  7. 7 markheckmann

    Thanks for the hint!
    I also use MacOS 10.6 but cannot remember how I installed it.
    Mark

  8. 8 markheckmann

    Hi John,
    the transistions will get smoother by rendering more images that change by a smaller angle. To achieve this just modify the loop:

    # example 2
    png(file="example%03d.png", width=300, heigh=300)
      for (i in seq(0, 360 , 2)){
        print(wireframe(z ~ x * y, data = g,
                  screen = list(z = i, x = -60)))
      }
    dev.off()
    

    Concerning ggplot2: AFAIK, it does not (yet) have 3D support.
    Mark

  9. 9 phalkun

    Thanks for posting the above animate gif images which are very interesting to me.
    i have tried the above code but i could not get the gif file, meaning R can’t find the convert. I did try to put the whole path of the convert.exe file. I have installed ImageMagick on my window vista and the convert.exe is in the Program Files. Here are code and pather i run:
    system(“C:/Program Files/ImageMagick-6.6.6-Q16/convert -delay 80 *.png example.gif”)

    Appreciate help on this issue.

    Pal

  10. 10 markheckmann

    Dear Pal,

    I am afraid I cannot help you with it. I work on Mac OS and am not sure how to get ImageMagick running on Windows. Maybe you can set a PATH variable so the convert command will be found without path (just an idea), actually I do not know.

    Mark

  11. 11 Phalkun

    hi Mark,

    thanks again for comments. I just found that for window i had to used “shell” instead of “system” and did not need to spell out the whole path.
    It works with all your codes except those in the example#3 (from PDF to GIF).
    I don’t know what was wrong with that. It may need more codes for Window. Without programing background, it was hard for me to understand the error message.

    thanks,

    Pal

  12. 12 James

    Hi,

    Nice work. How do I get the graph to stay the same size at every turn as it gets bigger to fill the plot when for example its straight on.

    Thanks, James

  13. 13 markheckmann

    Hi James,
    I asked myself the same when producing the plots. As I did not find it in the lattice documentation right away I decided to leave it. If you find out which parameters to adjust in the wirfeframe function, please let me know.
    — Mark

  14. When you creating GIF, If you are using Windows use “shell” instead of “system”. It works.
    shell(“convert -delay 40 *.png example_4.gif”)


  1. 1 R-ohjelmointi.org » Blog Archive » Vielä animaatioista
  2. 2 Como foi o meu #odhd aqui no Sertão « Beco Três
  3. 3 Animating neural networks from the nnet package – R is my friend