画像映像・音楽入門> 動画
----
ここではコンピュータを用いて動画を扱う方法についてまとめます。ただし、高等学校情報にある程度のコンピュータの知識を仮定します。
最初に，簡単な動画を作成する方法を説明します。動画には様々な保存形式がありますが、ここでは主にgif、MNGを扱います。これはこれらの形式がある程度知られた形式であり，しかも法律的な意味で自由に使用できる形式であることによります。
動画を作成，保存する形式の1つとして、flashがあげられます。これは上のgif, mngの両形式とは違い，それぞれの画像をベクタで保存します。flashは非常に広く普及しており，これを作成するソフトも存在しますが，残念ながらこの形式は法的に保護された保存形式です。flashを使って行ってよいことと行ってはいけないことについてはwikipediaのflashの項などを参照してください。
動画は複数の静止画像を時間的に変化させながら表示させる方式のことであり、映像をコンピュータ上で扱うための方法です。簡単な動画はいくつかの静止画像を組み合わせて作ることができます。例えば，同じ大きさの静止画像を複数枚重ね合わせることで，”パラパラアニメ”と同じ方法の動画を作ることができます。
このように作られる動画で、扱われる静止画像のことを"フレーム"と呼ぶことがあります。例えば、10枚の静止画像によって作られる動画は10フレームの動画と呼びます。
動画はいくつかの情報を持っています。単純な動画ではそれぞれのフレームをある一定の間隔で表示します。しかし、この方法だけでは問題があることを次に述べます。
例えば，ある画像だけを長い時間表示したいとします。この場合には，全く同じ内容の画像を複数回表示することになります。残念ながら、この場合には同じ画像情報を何度も扱うことになるので、保存されるデータ量が多くなります。しかも保存されている内容は全く同じ内容なので，その分のデータが無駄になります。
このような状況に対応するには，それぞれのフレームの表示時間を扱えると便利です。このときには長い時間表示したい画像の表示時間を長く設定すれば画像のデータ量を増やすことなく上と同じ効果を得ることができます。もちろん、それによって保存するデータにいくつかの数値が加わりその分のデータ増加はありますが，これによるデータ増加は画像の保存に必要なデータと比べれば微々たるものです。画像の扱いかたと数値の扱いかたについては高等学校情報Cなどを参照してください。
そのため、動画を扱う形式には静止画像だけでなく、画像の表示時間も記録できることが望ましいでしょう。このように、動画を保存するといってもそれらが扱う情報は保存形式ごとに異なっています。先ほどあげたgifとmngはどちらも表示時間を記録することができます。
ここまでは全ての画像の大きさが同じであると仮定してきました。しかし、場合によっては大きさが異なる画像を使った方が便利な場合があります。例えば，一般的なアニメで，人物がまばたきを行ったり言葉を発するために口を動かしたりすることがあります。この場面をよく観察すると、目や口の部分だけが変化しており，それ以外の部分は全く変化していないことがわかります。これを実現するには，目や口のサイズの画像を用意し，人の顔の画像と重ね合わせることでこの結果を得ることができます。この方法の利点は変化していない部分の情報を繰り返さずにすむことです。例えばまばたきをする分には顔全体を変化させる必要がないので、その部分はフレーム間でつかいまわすことができるわけです。同じ部分を描画しなくてすむので動画全体のサイズも小さくてすみます。
コンピュータ上でこの結果を得るには，動画の保存形式はこれまでの情報に加えて、静止画像のサイズと，その画像が表示される位置を扱える必要があります。gifとmngはこれらを扱うことができます。
ここでは実際に人物が口を動かす動画を作ってみます。動画を作るソフトウェアはいくつか知られていますが，ここではgimpを使いました。gimpは静止画像の編集を得意としますが，動画もある程度扱うことができます。ここからの解説ではある程度のgimpの知識を仮定します。Gimpの操作方法についてはgimpなどを参照してください。
gimpでは動画を扱うためにレイヤーを使います。gimpでは複数のレイヤーを使えますが，複数のレイヤーを持った画像をgifで保存する時，gimpはそれぞれのレイヤーをgif動画のフレームとして扱います。動画が表示される時間はレイヤーの名称を用いて指定することができます。レイヤーの名称を
 名前 (数値 ms)(フレームの表示方法)
として指定するとそのフレームは"数値"ミリ秒だけ表示されます。表示方法にはcombineかreplaceを指定することができ，combineではその画像は直前のフレームと合成されます。replaceではその画像で直前のフレームを置き換えます。
gimpのソースではこれらの情報は./plug-ins/common/gif.cで扱われます。
ここでは背景の顔画像が1枚と口周辺に4枚の画像を配置しました。
ここまではgiｆとmngで共通の操作を扱いました。ここからは主にmngで導入された操作について述べます。これらは、アニメやflashアニメでよく用いられる操作を含みます。
まず、mngでは画像を動かしながら表示することができます。これは、大きな絵全体を動かしながら表示する手法や、物体を変化させることなく移動させる手法として利用することができます。
残念ながらこれらの機能を持つmngファイルを簡単に作る方法はあまり知られていません。例えば、先ほどの例を作ったgimpの2.0系列ではgifアニメに加えてmngアニメを作成できます。しかし、これで作成できるアニメは、gifアニメと同じ機能を持ったmngファイルのみであり、画像の移動のようにmngで加わった操作を扱うことはできません。
いまのところmngファイルを作りたいときには、en:libmngを利用して作るしかないようです。ここでは、libmngの使い方を簡単にまとめます。これ以降の話題では高等学校情報の知識に加えてc言語の知識を仮定します。libmngのバージョンは1.0.9を使いました。
libmngはc言語で書かれたライブラリで、mngファイルの読み出しと書き出しを行うことができます。libmng自体はOSによらないとされていますが、開発環境などを構築する手間を考えると、linuxディストリビューションのコンパイル済みのパッケージを使うのがよいでしょう。ここからはlibmngの使い方について述べますが、これはlibmng付属のドキュメントと似た内容です。
以降の関数は2.6系列のlinuxで動くことを確認していますが、それ以外のシステムでは異なった動作をするかもしれません。
libmngは、ある程度オブジェクト指向を意識した関数を提供しています。まず、mngファイルを扱うために、mng_initialize関数を使います。ただし、動的なメモリ確保とメモリの解放を内部で行うために、メモリ確保とメモリ解放のための関数を引数として与える必要があります。ここでは確保のための関数としてmallocを与え、解放のための関数としてfreeを与えます。
 mng_handle handle = mng_initialize(userdata, malloc, free, NULL);
これ以降、handleをmngを扱うクラスのインスタンスのように扱うことになります。ただし、c言語自体はオブジェクト指向言語ではないので、メソッド呼び出しの代わりに、
 mng_xxx(handle, ...);
のように関数を呼ぶことになります。これはオブジェクト指向言語では、
 handle.xxx(...):
に対応する操作です。mng_initializeによって得られた変数は、mngの読み出しと書き出しの両方に用いることができます。ここでは、mngファイルの作成が目的なので、mngの作成法を述べます。mngを作成するには、まずmng_create関数を使って変数を初期化します。
 mng_create(handle);
ただし、この操作を行う前に、openstream, closestream, writedataと呼ばれるコールバック関数を与える必要があります。これらはここで扱うmngファイルを扱う関数です。例えば、出力するmngファイルをggg.mngとすると、openstream関数はこれを書き込み用に開く必要があります。
このときopenstream関数は、
 static int myopen(mng_handle handle){
   f = fopen("ggg.mng", "w");
   return 1;
 }
となります。ここで、返り値のintはファイル操作が成功したかどうかを表す値なので、本来ならfopenの返り値によって変更すべきです。ここでは、簡単のため常に操作は成功するものとしました。また、返り値の型は本来はint型ではないのでこのままコンパイルを行うと警告がでます。このときには適切にキャストを行うか、引数の型を変更してください。writedataは、openstream関数で開いたファイルにデータを書き出します。
writedata関数には書き出したデータを蓄える場所、書き出す長さ、実際に書き出された長さを表す引数が与えられます。
 static int mywrite(mng_handle handle,void *data, int size, int* written){
   *written = fwrite(data,1, size, f);
   return 1;
 }
これらのコールバック関数を実際にhandleに与えるには、mng_setcb_xxx関数を使います。実際の変数名はlibmng.hなどを見て調べることができます。
 mng_setcb_openstream(handle, mywrite)
ここまででmngを作成する準備ができました。実際にmngを作成するには、mngにどのような機能があるかを把握する必要があります。mngのデータは”チャンク”と呼ばれるデータで扱われます。それぞれのチャンクには名前とチャンクに付属するデータがついており、名前とデータを指定することでチャンクを指定することができます。具体的なチャンクの種類はmngの仕様(MNGを参照)などを参照してください。具体的には例えばMHDRチャンクをしていするときには、
 mng_putchunk_mhdr(handle, 320,200,30,3,3,3,583);
のような関数を使います。ここで、関数名のmhdrを他のチャンク名に変えることで、他のチャンクを作成することができます。詳しい引数の意味は仕様書を参照してほしいのですが、1,2,3番目の引数は画面の幅、高さ、表示画像のfpsを表します。
チャンクのデータを指定し終わったら実際にmngファイルを作成します。このためには、
 mng_write(handle);
を使います。
mngファイルを表示するには、libmngに付属の各mngビューアを使うことができます。これは、libmngソース内で./contrib/gcc以下に含まれるファイルです。例えば、SDLを使ったビューアなどが存在し、mngを表示することができます。