9
$\begingroup$

I have hundred thousands of images from a numerical simulation and want to make a movie from them. This is easy done with

Export["movie.mov", imagelist]

However, if imagelist consists of hundred thousands of images, my computer simply needs ages or shuts down. I thought, to solve this problem, I make smaller movies, each consisting of e.g. 10.000 images. This runs comparatively fast. Then I end up with x numbers of short movies. Now I would like to stitch them together into one single movie. Is there a way to do this with Mathematica? If so, it would be great as I could then constantly add new frames from my running simulation (which takes days) to my movie.

$\endgroup$
8
  • 4
    $\begingroup$ I would recommend a non-Mathematica based solution like ffmpeg. $\endgroup$ Commented May 13, 2015 at 17:33
  • $\begingroup$ Sure, I can use iMovie, but it would be nice to keep it all under the hood of a single software. $\endgroup$ Commented May 13, 2015 at 17:34
  • 2
    $\begingroup$ I believe that any MMA-based solution would require importing the pieces of the movie all at once, then exporting them together: which is exactly what you say is giving you problems. FFMPEG will easily concatenate video files from the command line, so you should be able to use it via a system call from MMA if you so desire. $\endgroup$ Commented May 13, 2015 at 17:38
  • $\begingroup$ OK, I probably don't know how this works. If it should not be possible with MMA, I probably use iMovie. $\endgroup$ Commented May 13, 2015 at 17:41
  • 4
    $\begingroup$ MMA represents videos as lists of (uncompressed) frames, so the file will balloon out to tens of GB in memory when Imported. $\endgroup$ Commented May 13, 2015 at 17:51

2 Answers 2

10
$\begingroup$

The way I typically handle this type of situation is to export the individual images, then have software like FFmpeg handle the conversion to video. FFmpeg in particular is an extremely powerful tool (and is available for Windows, OS X, and most Linux distros).


First download the appropriate binary for your system. I placed the ffmpeg binary in the folder se-temp. Let's find it:

tempDir = FileNameJoin[{$HomeDirectory, "se-temp"}];
SetDirectory[tempDir];
FileNames[]

(* {"ffmpeg.exe"} *)

Yeah, I'm doing this from Windows. Don't worry though, the only change you should have to make if you're on Linux or OS X is to remove the .exe from the command name. In fact, let me do that for you:

command = First@FileNames["ffmpeg*"];

We can use the (new in v10) function RunProcess to check that we can call FFmpeg from Mathematica.

RunProcess[{command, "-version"}, "StandardOutput"]

(*
  ffmpeg version N-72086-g51f6455 Copyright (c) 2000-2015 the FFmpeg developers
  built with gcc 4.9.2 (GCC)
  configuration: --enable-gpl --enable-version3 --disable-w32threads ...
*)

If you're on an earlier version, you can use Run instead:

Run[command, "-version"]
(* 0 *)

0 tells us that the command was executed successfully (errors typically result in nonzero exit codes). If you're on Windows, a terminal window will flash open while the command is running.

Now let's generate some images:

r = 24;
l = 12;
imgs = Join @@ 
   Table[Graphics[{If[
       10 >= n > 2 && 
        f > 0, {{EdgeForm[
          Directive[Black, Thickness[1/200], JoinForm["Round"]]], 
         GrayLevel[0.5], 
         Disk[{0, 0}, 19, Pi/2 - {0, 2 Pi f/r}]}, {Black, 
         Thickness[1/500], 
         Line[{{{-16, 0}, {+16, 0}}, {{0, -9}, {0, 9}}}]}, {White, 
         Thickness[1/120], Circle[{0, 0}, 7], 
         Circle[{0, 0}, 8]}}], {Black, 
       Text[Style[n, 400], {0, 0}, {0, 0}]}}, 
     PlotRange -> {{-16, 16}, {-9, 9}}, 
     Background -> 
      If[10 >= n > 2 || (f == 0 && n >= 2), GrayLevel[0.8], Black], 
     ImageSize -> 1280], {n, Reverse@Range[l]}, {f, Range[r] - 1}];

r sets the framerate, and l sets the number of seconds. Just for fun, we can also generate some sound:

fs = 48000;
sound = Flatten[
   N@Table[Boole[n == 2 && f == 0] Sin[2 Pi*1000 s/fs], {n, 
      Reverse@Range[l]}, {f, Range[r] - 1}, {s, fs/r}]];

(Ok, it's not an aural masterpiece: less Tupac and more 2-pop.) The parameters are the same as before, except now we have the sampling rate fs.

Let's export:

imgName[i_Integer] := imgName @ IntegerString[i, 10, 4]
imgName[s_String]  := "img" <> s <> ".png"
soundName = "audio.wav";
MapIndexed[Export[imgName[First@#2], #1] &, imgs];
Export[soundName, ListPlay[sound, SampleRate -> fs]];

Finally we can call ffmpeg to join our images into a video:

RunProcess[{command, "-y", "-r", ToString@r, "-f", "image2", "-i", imgName["%04d"], "-i", soundName, vidName}];

Or using Run:

Run[command, "-y", "-r", r, "-f", "image2", "-i", imgName["%04d"], "-i", soundName, vidName];

This runs the following command:

ffmpeg
 -y                # overwrite output if already exists
 -r 24             # declares input frame rate
 -f image2         # declares input format
 -i "img%04d.png"  # input image name
 -i audio.wav      # input audio name
 video.mp4         # output name

Finally, we should clean up after ourselves by deleting the image files:

DeleteFile @* imgName /@ Range[Length[imgs]];

If you have a bunch of already-existing video files that you want to concatenate, FFmpeg will do that too.

First we need to create a temporary file with the list of videos to concatenate:

listName = "list.txt";
videos = {"video.mp4", "video.mp4"};
Export[listName, 
  StringJoin[
   Riffle["file \"" <> # <> "\"" & /@ 
     StringReplace[videos, {"\"" -> "\\\"", "\\" -> "\\\\"}], "\n"]]];

Then we run the command:

vidName = "videoDoubled.mp4";
RunProcess[{command, "-y", "-f", "concat", "-i", listName, "-c", "copy", vidName}];

Since our list had "video.mp4" twice, the new video is just the old video repeated twice; but you get the idea.

Finally we clean up:

DeleteFile /@ videos;
DeleteFile @ listName;
$\endgroup$
1
$\begingroup$

Windows users can use my MathMF package for this. There's no need to export the frames as images, you just write them directly to a video stream as you create them. It doesn't have the flexibility of third party tools like ffmpeg but it's good for a quick and easy video export without storing all frames in memory. Example code:

Needs["MathMF`"]

{w, h} = {300, 200};
frame[i_] := Rasterize[i, "Data", ImageSize -> {w, h}]

MFInitSinkWriter["C:\\Users\\Simon\\Desktop\\example.wmv", w, h, "FrameRate" -> 25]

Do[MFSendFrame @ frame[i], {i, 1, 100}]

MFFinaliseSink[]
$\endgroup$
1
  • $\begingroup$ +1 I take this opportunity to tell you that your package works great for me. I've been importing a lot of videos, with zero problems! THANK YOU $\endgroup$
    – P. Fonseca
    Commented May 14, 2015 at 22:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.