50 GLSCで動画作り

(工事中)

実は僕は GLSC で動画作りをやったことがほとんどなかった (自分のスピーチに動画が必要になることは少なく、 やろうとしていてつまづいている学生の相手をしているときに対症療法しただけ)。 今回、動画作りをやったことのない学生に「やって下さい」と言ったため、 結局はやり方をイチから説明することになった。 その記録。


上山先生は g_dump() という関数の利用を勧めていたようだけど、 今だと GLSC 3.8.6 を使うことにして、 g_caputure() で記録しましょう、となるのかな。


Part 1a: 画像ファイルを作る (g_dump() バージョン)

まずは g_dump() でやってみる。 うちの多くの学生は
簡易バージョン
void g_dump(char *fname, Display *display, Window wid)
{
        char command[256];

        sprintf(command, "import -window %lu %s", wid, fname);
        system(command);
}

...
    /* ウィンドウを出してから Display, Window を調べる (一度で良い?) */
    Display *display;
    Window wid; // Window ID
...
    display = g_get_display();
    wid = g_get_window();
...
    /* 画像を保存したいところで g_dump() */
    g_dump("myimage.png", display, wid);
みたいなことをしている (オープンできるファイル数の制限にひっかかって苦しんでいる人もいた -- 「Macのulimit)。


次のようなコードも残っているけれど、うまくコンパイル・リンク出来なかった。

いつ、どこで入手したのだっけ? (現在動かないけれど参考まで残す)
#include <magick/api.h>

void
g_dump(char *fname, Display *display, Window wid)
{
  ImageInfo *image_info;
  ExceptionInfo ex;
  int i;
  int argc = 5;
  char **argv;

  argv = (char **)malloc(sizeof(char *)*argc);
  for(i = 0; i < argc; i++)
    {
      argv[i] = (char *)malloc(sizeof(char)*256);
    }
  sprintf(argv[0], "glsc");
  sprintf(argv[1], "-silent");
  sprintf(argv[2], "-window");
  sprintf(argv[3], "%d", wid);
  sprintf(argv[4], "%s", fname);

  InitializeMagick(*argv);
  GetExceptionInfo(&ex);
  image_info = CloneImageInfo((ImageInfo *) NULL);

  ImportImageCommand(image_info, argc, argv, (char **)NULL, &ex);
  //image_info = DestroyImageInfo(image_info);

  for(i = 0; i < argc; i++)
    {
      free(argv[i]);
    }

  free(argv);
}

Part 2の ffmpeg で処理するためには、ファイル名を “連番” で作る。 大体こんな感じ。
   int id;
   char filename[512];
   ...
   // ファイルに保存
   id = 0;
   sprintf(filename, "wave%04d.png", id++);
   g_dump(filename, display, window);
   ...
   // 時間に関するループ
   for (n = 1; n <= nmax; n++) {
     ...
     if (n % skip == 0) {
        // 描画
        ...
        // ファイルに保存
        sprintf(filename, "wave%04d.png", id++);
        g_dump0(filename, display, window);
     }
   }


Part 1b: 画像ファイルを作る (g_capture() バージョン)

GLSC 3.8.6 で用意されたのは g_capture() という関数である。 さすがに後から作っただけに、この方が簡単に使える。

ディクトリィを用意して、 その下に “%06d.jpg” という形式のファイル名で画像を保存していく (なぜ JPEG 固定なのだろう??)。

   system("mkdir wave386"); // ディレクトリィを準備
   g_capture_set("wave386");
   ...
   g_capture(); // 画像を保存したいタイミングで


Part 2: ffmpeg で動画ファイルを作る

(静止画像から動画ファイルを使うために、ずっと以前は苦労したけれど、 今では ffmpeg を使えばOK。)


ffmpeg でオプション無指定で作った動画ファイルは、 Mac の QuickTime で再生できなかったので、 Mac 用のおまじない -pix_fmt yuv420p をつける (こうするものだそうです)。 それと画像の縦横のサイズが奇数になると文句を言われることの対策をする (-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" 2で割って切り捨ててから2をかける…ひどいバッドノウハウ)。
こんな感じ (wave0000.png, wave0001.png, wave0002.png, … から wave.mp4 を作る)
ffmpeg -i wave%04d.png -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
 -pix_fmt yuv420p wave.mp4


Part 3: 実例

「公開プログラムのページ」 に、1次元波動方程式の初期値境界値問題をシミュレートする wave1d-glsc-v2.c というプログラムがある。 画像ファイルを保存するように修正して (ファイル名を wave1d-glsc-v3.c, wave1d-glsc386-v3.c とする)、 そうして作った画像ファイルから動画ファイルを作ってみた。

curl -O http://nalab.mind.meiji.ac.jp/~mk/program/fdm/wave1d-glsc-v3.c
cglsc wave1d-glsc-v3.c
./wave1d-glsc-v3
N=100
λ=1
Tmax=2
0: 定常波(1山), 1: 定常波(3山), 2: 割れる山, 3: 右に進行, 4: ぶつかる, 5: ホイヘンスの原理反例
nfunc(初期値の種類 0..5)=4
ffmpeg -i wave%04d.png -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -pix_fmt yuv420p wave.mp4

curl -O http://nalab.mind.meiji.ac.jp/~mk/program/fdm/wave1d-glsc386-v3.c
cglsc386 wave1d-glsc386-v3.c
./wave1d-glsc386-v3
N=100
λ=1
Tmax=2
0: 定常波(1山), 1: 定常波(3山), 2: 割れる山, 3: 右に進行, 4: ぶつかる, 5: ホイヘンスの原理反例
nfunc(初期値の種類 0..5)=4
ffmpeg -i wave386/%06d.jpg -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -pix_fmt yuv420p wave386.mp4

出来た動画ファイルを参考までに置いておきます (どちらも 130KB 程度なので軽いです)。
curl -O http://nalab.mind.meiji.ac.jp/~mk/program/fdm/wave.mp4
curl -O http://nalab.mind.meiji.ac.jp/~mk/program/fdm/wave386.mp4


Part 4: その他

そもそも QuickTime で再生するというのを前提にしてしまっている (?)。 自分用ならば、再生ソフトを探してみることも出来る。 Windows ほどではないけれど、Mac でも色々選択肢はある。 以下試してみたもの。

桂田 祐史
2020-04-05