unoh.github.com

ImageMagickでGIFアニメをリサイズ

Thu Dec 11 01:23:14 -0800 2008

こんにちは、いそがわです。

PHPでアニメーションGIFを操作しようとすると、GDはアニメーションGIFをサポートしていないのでImageMagickを利用することになるかと思いますが、いかんせんImageMagickはメソッドの数がベラボーで分かりづらく、Imagickに至ってはまだまだドキュメントがスッカスカで、間違ってたりもするためなのか、意外とアニメーションGIFのリサイズ処理に混乱が見受けられるようなので、ご存知のかたには今更な話題ですが、あらためて。

ImageMagickでアニメーションGIFを“お手軽に”リサイズする基本的な手順は以下のような感じです。

  1. シーケンスのイメージの“結合”(coalesce)
  2. 各イメージを順次リサイズ
  3. 最適化(必要なら)

このうち重要かつ忘れられがちだと思われるのがcoalesce処理です。アニメーションシーケンスの各イメージはすべてが同じサイズとは限りません。各イメージはそれぞれサイズや表示オフセットが異なっているかもしれませんし、また次のイメージを表示する際に現在のイメージをどう処理するかというdisposal methodとかあったりします。高度に最適化されたアニメーションシーケンスほどその構成は複雑で、こうした点を考慮しないと、期待した結果が得られないかもしれません。coalesceはこうしたアニメーションシーケンスを、手軽に扱えるようにする処理です。

MagickWand C APIのMagickCoalesceImagesの説明が簡潔なので引用します。

MagickCoalesceImages() composites a set of images while respecting any page offsets and disposal methods. GIF, MIFF, and MNG animation sequences typically start with an image background and each subsequent image varies in size and offset. MagickCoalesceImages() returns a new sequence where each image in the sequence is the same size as the first and composited with the next image in the sequence.

訳すとこんな感じでしょうか。

MagickCoalesceImages()はページオフセットやdisposal methodを考慮しながらイメージセットを合成します。GIF、MIFF、及びMNGアニメーションシーケンスは、一般的に背景画像で始まり、以降の各イメージのサイズやオフセットはさまざまに異なります。MagickCoalesceImages()はシーケンスの各イメージが、最初のイメージと同じサイズで、かつシーケンスの次のイメージと合成された新しいシーケンスを返します。

下図は、Gakkunさん作のnatmというアニメーションGIF作成ツールで、http://www.creepygif.com/image.php?i=578に掲載されているアニメーションGIFファイルを開いた画面です。ここではこのアニメーションシーケンスの2フレーム目を選択していますが、左側のウィンドウに表示されているこのフレームのイメージは、前後のフレームのイメージと比較して変化のある箇所だけのものになっています。アニメーション全体の縦横サイズは225×344ピクセルですが、この2フレーム目のイメージの縦横サイズは69×211ピクセルで、その表示オフセットは横55ピクセル、縦8ピクセルとなっています。

オリジナル

下図は同じファイルをcoalecse処理したものです。同じ2フレーム目を選択していますが、オリジナルでは変化のある箇所だけが描画されていたイメージは完全なものに合成され、縦横サイズもアニメーション全体と同じになっています。

coalesce処理

coalesce処理されたアニメーションシーケンスは手軽に扱える一方で、その処理内容を鑑みれば必然ですが、元のアニメーションシーケンスが高度に最適化されていればいるほど、相対的なデータサイズが大きくなってしまいます。

アニメーションシーケンスは最適化処理を施すことでデータサイズを縮小できる場合があります。coalesce処理されたアニメーションシーケンスに対する最もお手軽な最適化処理として伝統的に用いられてきたのがdeconstructです。deconstructは、coalesceによって同じサイズになっているシーケンスの各イメージを、その次のイメージと比較して変化のある領域の四辺より広い範囲を切り捨てます(結果のシーケンスは、各イメージのサイズとオフセットがバラバラになります)。

下図は上述のファイルをcoalecse→deconstruct処理したものです。deconstruct処理によって、イメージは前後のフレームのイメージとの差分だけになっているため、coalecse処理だけを施したものではすべて同じになっていた各フレームの縦横サイズとオフセットは、ここではバラバラになっています。一方イメージの差分は、オリジナルではピクセル単位であったものが、変化のある領域の最大四辺となっており、まだ最適化の余地があるということがわかります。

coalesce→deconstruct処理

なおdeconstructは、coalesce処理されていないアニメーションシーケンスに対しては期待通りの結果が得られない場合があり、またそもそもこれの利用は、今では非推奨とされているようです。Imagickではアニメーションシーケンスの最適化処理にoptimizeImageLayersを利用することができます。

コード例

以下はアニメーションGIFファイル「source.gif」を縦横50%縮小したファイル「dest.gif」を書き出す例です。ここではすべて共通で、最適化処理としてdeconstructを行っています。

Imagickでのコード(エラー処理とか省略)

$source = 'source.gif';
$dest   = 'dest.gif';
$scale  = 0.5;

$images = new Imagick($source);
$images = $images->coalesceImages();
$width  = round($images->getImageWidth()  * $scale);
$height = round($images->getImageHeight() * $scale);
do {
    $images->scaleImage($width, $height);
} while ($images->nextImage());
$images = $images->deconstructImages();
$images->writeImages($dest, true);

MagickWand For PHPで上のコードを写経

$source = 'source.gif';
$dest   = 'dest.gif';
$scale  = 0.5;

$images = NewMagickWand();
MagickReadImage($images, $source);
$images = MagickCoalesceImages($images);
$width  = round(MagickGetImageWidth($images)  * $scale);
$height = round(MagickGetImageHeight($images) * $scale);
do {
    MagickScaleImage($images, $width, $height);
} while (MagickNextImage($images));
$images  = MagickDeconstructImages($images);
MagickWriteImages($images, $dest, true);

ちなみにconvertコマンド

$ convert source.gif -coalesce -scale 50% -deconstruct dest.gif

上のコードを見れば一目瞭然かと思いますが、アニメーションGIFのリサイズは、シーケンスの各イメージを個別に順次リサイズすることになり、必然的にイメージ数が多ければそれだけ処理に時間がかかります。JPEGなどのリサイズと比べて時間がかかることがあるのはそのためです。例えば、数十~数百ものイメージからなるアニメーションGIFのリサイズには数分~数十分もかかるかもしれませんから、ユーザからのアップロードを、キューなどを用いずにリクエスト内で処理しようとするとタイムアウトになる可能性があり、注意が必要です。

おかしいなと思ったときは

GIFを扱うツールはさまざまありますが、中にはシーケンスのさまざまな情報を細かく確認できるものもあります。処理結果が期待通りではない場合は、こうしたツールを利用すると状況が確認できるかもしれません。

上にも登場いただいたnatmもそうしたツールのひとつですが、ちなみにこれに付属のドキュメントは、アニメーションGIFについての非常にわかりやすい説明にもなっていて、例えばdisposal methodって具体的にはなんなの?といった疑問が一読で氷解されると思われ、アニメーションGIFの仕組みをちょっと詳しく知りたいというかたにはお勧めです。

ではでは


2008-12-12改定: 文章だけだとわかりづらい気がしたので図版を追加しました。