FuseFSを使ってFFmpegでGnuplotの動画を作成

以前「GnuplotをRubyから操作 - notes plastiques」で紹介した方法を使うと Gnuplot で gif アニメを作成できる。しかし、このgifアニメ、ファイルサイズがものすごく大きくなってしまう上に、他の動画ファイルに変換する方法が見当たらない。 Windows だと Giam で変換できるようだが、ファイルサイズが大きいせいか上手く動作しなかった。
そこで別の方法を考える。

できれば、最終的な動画ファイルを作成するまで、中間ファイルをディスクに作成しない方法が良い。 Gnuplot 側で連番で画像ファイルを作成し、それを FFmpeg で動画に変換することは可能だが、一旦画像ファイルを作成するので容量が必要だし、ディスク律速になってしまう可能性もある。

追記(8/15): この段落に記して方法は、別記事にまとめました。

まず考えたのは、 Gnuplot では標準出力に画像を出力し、外部プログラムでそれを受け取ってストリーミングできる動画形式にオンザフライで変換し、それを FFmpeg に渡すという方法。この方法の場合にやらなくてはいけないことは、受け取った画像データを解釈し、動画の1コマとして組み込むこと。画像データを解釈するプログラムを自前で書くのはかなり面倒だ。 ImageMagickC++ bindings である Magick++ を使えば、生のピクセルデータに変換されたものを扱うことが出来る。これを例えば AVI のファイル構造に従って1コマずつ作成していけば良い。 AVI の作成についてはここが参考になる。

ただ、 ImageMagick が必要なのが難点。 ImageMagick の QuantumDepth が8であればよいが、16であると普通の BMP と違うので、自前で変換しなくてはいけない。変換はビットシフトするだけで良いがやはりちょっと面倒だと思う。さらに、 C/C++ から外部プログラムを扱うのがやや面倒。fork-execという手法を使えば良いらしいのだけども…

そこで、別の方法を考える。FuseFSを使う。FuseFSとはユーザー権限で、独自のファイルシステムを作成できるモジュール。これを使って、仮想的に連番の画像ファイルが存在するようにし、読み出されたときはGnuplotを呼び出して出力された画像を返すようにする。こうして、FFmpegから連番ファイルを読み込ませて動画ファイルに変換させる。この方法ならDisk Freeで、しかも画像ファイル構造には無頓着に実装できる。(ただし、FuseFSについて勉強しなくてはいけないけれど。)
FuseFSはいろいろなプログラミング言語から扱える。C言語RubyPythonなど。いちばん良いのはC言語だが、前述したようにGnuplotとのパイプの処理がやや面倒。(それほど面倒でもない?)なので扱い慣れているRubyを扱いたい所だが、Macではなぜか正常に作動しない。そこで仕方なくPythonを使ってみることにした。ちなみにPythonはほとんど自分で書いたことが無かったのだが、あまり抵抗なく書くことが出来た。インデントでブロックを規定してるというのは知っていた。

まずは準備。FuseFSのPython Bindingsをインストールする。
MacPortsの場合、fuse-bindings-pythonfuse-bindings-python25がある。fuse-bindings-pythonPython 2.4用でfuse-bindings-python25はPython 2.5用らしい。新しいのは無いのかな…
Ubuntuの場合、python-fuseがある。
インストールが出来たら、

からダウンロードして、

python main.py /path/to/mountpoint

でマウントする。pythonfuseが扱えるバージョンを使うこと。
そしたら、/path/to/mountpointを開くと、image_0.pngとimage_1.pngとimage_2.pngがみえるはずだ。普通に開いてもらえば、三角関数のグラフがみれると思う。一覧では見えていないが、実はimage_数字.pngというファイルも読み出せば存在するようになっている。試しにimage_100.pngなどを開いてみていただきたい。この数字は313まで呼び出せるようになっている。ffmpegで上限が無いとエラーになってしまうから上限を設定した。ちなみに、image_00100.pngのようにしても読み出せるのでお試しいただきたい。
動画化は

ffmpeg -i /path/to/mountpoint/image_%d.png -sameq out.mp4

などとすれば良い。正弦波が進行していく様子が動画になるはずだ。

コードについて記しておく。
Gnuplotとのやりとりはsubprocess.Popenを用いている。外部プログラムとの連携を比較的簡単に行える。

サンプルプログラムのhello.pyが参考になる。

からfuse-pythonをダウンロードして解凍すると/example/hello.pyに確かあったはず。

現在のコードの問題点は、ファイル名ごとにいちいちGnuplotを立ち上げているということ。プロセス立ち上げのコストがかかってしまう。できれば一度だけGnuplotを立ち上げ、その中でplotを何回も呼び出して、としたいのだが、plotしたときに出力される画像データがどこで区切られているのか検出するのが困難なので実装できてない。ちなみに既に呼び出されたファイルについてはreleaseが呼ばれるまでは保持するようにしている。あとは、Pythonに慣れていないのとメモリ管理に詳しくないのとでいただけない実装になっているからなおしたい。