ImageMagick 使用例 -- アニメーションの最適化
- 基本的なフレーム最適化
- ピクセルを重ねない - 1 フレームおきに同じ画像が繰り返される
- 移動する穴のアニメーション - フレーム最適化が難しいアニメーション
- フレームの二重化
- Layer Optimize Plus
- 重複フレームの除去
- フレーム更新の分割
- ディレイがゼロのフレームの除去
- スピードのアニメーション - 色数が多すぎるアニメーション
- カラー最適化の前にフレーム最適化を?
- あいまいなカラー最適化
- 単一のグローバルカラーテーブル
- LZW 最適化
- ロッシー LZW 最適化 - (非 IM)
-
細かな最適化 GIF 最適化に関するその他の情報源 これらの例では基本的なアニメーションの扱いを活用しはじめ、アニメーションの最終的な表示とファイルサイズの最適化を試みます。これは特に複雑な GIF アニメーションで重要であり、より小さなサブフレームのオーバーレイを使えるほか、アニメーションの扱いを制御する 3 種類の破棄方法を利用できます。
アニメーション最適化への導入
アニメーションの最適化は容易ではありません。特に GIF アニメーションは色数の制約があり、さらにさまざまなフレーム破棄手法を選べたり、あるフレームから次のフレームへより小さな「サブフレーム」のオーバーレイを使えたりします。アニメーションを最適化するときは、次の順序で最適化を試みるとよいでしょう。
しかし、ここで最適化手法を見ていく順序はこれとは異なります。GIF アニメーションではフレーム最適化が最も基本的な最適化手法であり、最も大きな効果が得られるところです。そのため最初に取り上げます。利用者がつまずきやすい最も難しい側面は、おそらく GIF アニメーションの色数制限に起因するカラー最適化でしょう。その一側面である単一のグローバルカラーテーブルは、GIF として保存する直前の最後のステップとして行わなければなりません。さもないと、最終的な GIF ファイル保存時に各演算子の効果が失われてしまうことがあります。
ImageMagick の汎用 GIF オプティマイザ
「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッドの「**Optimize**」は、下記で詳しく説明する多くの手法を使い、GIF アニメーションを 1 つの妥当なステップで最適化しようとします。現在このオプションは(順番に)次と同等です…
- アニメーションの合体 (Coalesce)。
- 基本的なフレーム最適化。
- および透明度の最適化。
この時点で GIF アニメーションを直ちに保存できます。これらはほとんどのアニメーションシーケンスに適用できる、比較的安全な最適化ステップですが、必ずしも GIF アニメーションが小さくなる保証はありません。これは特に生の動画シーケンスで顕著で、透明度の最適化は一般に LZW 圧縮率の悪化を招きます。しかし、アニメ調の画像を含むほとんどの GIF アニメーションについては、「Optimize」演算子はよく最適化されたアニメーションを生成するはずです。とはいえこの演算子はまだ開発中で、将来は次のような標準的な最適化ステップも追加される可能性があります…
- IM が GIF ファイルに保存するときに通常行うのと同様、アルファチャンネルの 50% しきい値処理を行い、半透明ピクセルを除去します。これを上書きしたければ、事前に自分で半透明の扱いを済ませておくこともできます。詳しくは GIF のブール透明度を参照してください。
- 何らかのカラー最適化手法。正確に何を行うかはまだ未定で、アニメーションや関係する色数に応じて選ばれるかもしれません。提案歓迎。
- 単一のグローバルカラーテーブル、すなわち「
[+map](https://imagemagick.org/command-line-options/#map)」操作。
言い換えれば、「Optimize」がゆくゆくは IM の汎用 GIF アニメーション オプティマイザとなり、IM 利用者が手軽に使えるようになることが期待されています。それまでは、特にスクリプトでの使用には注意してください。変更されるからです。もちろん、特定のアニメーションにとっては多くの最適化ステップが手間に見合わないこともあります。このオプションはおそらくかなり遅くもなるでしょう。これが計画であり、この IM 使用例のセクションが目指してきた目標でもあります。
フレーム最適化
フレーム最適化は、画像全体を完全にオーバーレイするのではなく、より小さなサブ画像をオーバーレイすることに基づきます。これは当然、ピクセル数を減らし、ディスク上のファイルやネットワークを通じて送るデータを小さくします。また、より小さなフレームをオーバーレイすれば、クライアントのコンピュータが画面上のピクセルを変更する作業も減ります。しかし GIF 形式には、直前に表示されたフレームを扱うためのさまざまな破棄方法があり、それによってオーバーレイのサイズが変わってきます。それだけでなく、オーバーレイを複数の部分や更新動作に分割し、より複雑だがより最適化されたアニメーションにすることも可能です。フレーム最適化は複雑なため、既存のフレーム最適化はまず「[-coalesce](https://imagemagick.org/command-line-options/#coalesce)」操作で常に取り除くのが普通です。Coalesce の例を参照してください。当然、これは既存の手作業による最適化も取り除いてしまうので、いくらか注意が必要です。
基本的なフレーム最適化
「[-deconstruct](https://imagemagick.org/command-line-options/#deconstruct)」メソッドは GIF アニメーションに対する基本的なフレーム最適化を生成します。しかし前のセクションの Deconstruct の例で示したように、この演算子は透明ピクセルが絡むすべての GIF アニメーションでは正しく動作しません。具体的には、アニメーションが色付きのピクセルを透明へ消去する場合です。つまりオーバーレイ アニメーションでのみ動作します。「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッドの「**OptimizeFrame**」は GIF フレーム オプティマイザとして設計されており、任意の GIF 破棄方法を使って、最小のサブフレームのオーバーレイ画像を見つけようとします。結果は一般に混合破棄アニメーションになりますが、特定のアニメーションにとってそれが最良の解決策だと判断されれば、しばしば消去フレーム アニメーションや純粋なオーバーレイ アニメーションも生成します。入力アニメーションは「合体されたアニメーション」でなければならない、つまり完全な画像フレームの並びで、すべて同じサイズで、キャンバスのオフセットがないことを忘れないでください。もちろん、合体済みアニメーションに既存の破棄方法があってもまったく無関係で、「OptimizeFrame」メソッドからは無視されます。例として、前のセクションで作成した直前破棄アニメーションで試してみましょう。 | |
magick canvas_prev.gif -coalesce -layers OptimizeFrame optframe.gif
gif_anim_montage optframe.gif optframe_frames.gif
![[IM Output]](../static/img/anim_opt/optframe.gif)
![[IM Output]](../static/img/anim_opt/optframe_frames.gif)
ご覧のように「[-layers](https://imagemagick.org/command-line-options/#layers) OptimizeFrame」は、直前破棄を使って、アニメーションを元のフレーム最適化された形へ正しく戻しました。この最適化は、扱いがより厄介な背景破棄アニメーションに対しても正しく機能します… | |
magick canvas_bgnd.gif -coalesce -layers OptimizeFrame optframe_bgnd.gif
gif_anim_montage optframe_bgnd.gif optframe_bgnd_frames.gif
![[IM Output]](../static/img/anim_opt/optframe_bgnd.gif)
![[IM Output]](../static/img/anim_opt/optframe_bgnd_frames.gif)
このアニメーションは背景破棄を使って完璧にフレーム最適化されています。この演算子はすべての GIF アニメーションで正しく機能し、一般に可能な限り最良の単純な「破棄とフレーム最適化」を返します。
さて、IM が提供するような単純なフレーム最適化に関する悪い知らせです… 「[OptimizeFrame](#optframe)」は IM が見つけられる、与えられたアニメーションに対する可能な限り最良のフレーム最適化を返しますが、うまくいかない特殊なケースがいくつかあります。それには次のものが含まれます。
- ピクセルの消去(透明への復帰)が必要なのに、フレームのオーバーレイが大きすぎて、消去すべき小さな領域のピクセルを効率よく消去できないアニメーション(下記の移動する穴のアニメーションを参照)。
- 互いに大きく離れた、2 つ以上の小さな変化領域を含むアニメーション。これは実際にはかなり一般的で、フレーム最適化が非常に厄介です。(下記のフレーム更新の分割を参照)
- 非常に複雑な背景が長期間(3 フレーム以上)静止し続けた後にわずかに変化し、また長期間静止し続け…ということを延々と繰り返すアニメーション。あるいは、静止した背景がごく短時間だけ大きく覆い隠されるもの。この複雑な状況では、どんなコンピュータのアルゴリズムでも「最良の」フレーム最適化を見つけ出すのはほぼ不可能です(つまり、何を静的な背景とみなすべきか?)。これらのケースで良い最適なフレームオーバーレイの並びを生成できるのは、見たものを直感的に把握できる人間だけです。
最適化が難しいアニメーションの例を募集中です。ぜひご提供を。 IM が良い最適化を生成できないアニメーションの例を見つけたら、さらなる研究のために私にメールしてください。新しい手法や自動化された解決策が、こうして開発できます。もちろん、貢献者としてあなたの名前を掲載します。
ピクセルを重ねない - 1 フレームおきに同じ画像が繰り返される
画像にとって最良の最適化が、ピクセルをまったく重ねないことであることもあります! 例えば右にあるのは、nixscripter から提供された単純なアニメーションです。フレームを見ると、あまり最適化されていないのがわかります。しかし、アニメーションの 1 フレームおきが単に繰り返されていることに注目してください。
gif_anim_montage paddleball.gif paddleball_frames.gif
これをフレーム最適化すると、とても特殊な GIF の破棄シーケンスが得られます。 | |
magick paddleball.gif -coalesce -layers OptimizeFrame paddleball_opt.gif
gif_anim_montage paddleball_opt.gif paddleball_opt_frames.gif
![[IM Output]](../static/img/anim_opt/paddleball_opt.gif)
![[IM Output]](../static/img/anim_opt/paddleball_opt_frames.gif)
ここで起きているのは、元のフレームをオーバーレイする代わりに、IM が「直前」GIF 破棄を使って最初の画像を復元する選択をしたことです。復元されたフレームはそのまま残るので、変化したピクセルはありません。よってサブフレームのオーバーレイは何もないところまで縮小されます。残念ながら IM も GIF 形式もサイズゼロの画像は許さないので、代わりに透明ピクセル 1 個の特別な最小画像が使われます。この画像はミス画像として知られ、「[-crop](https://imagemagick.org/command-line-options/#crop)」が実際の画像データを「外した」ときにも幅広く使われ、同じ結果を生みます。この画像は事実上、フレームのメタデータ、すなわち破棄方法・時間ディレイ・ループ回数だけを保持します。そのため「空」であってもアニメーションの不可欠な一部です。よって最小限の透明ピクセル 1 個を重ねることで、IM はこのアニメーションで膨大な容量(と時間)を節約しました。
移動する穴のアニメーション - フレーム最適化が難しい
ここに、どんな通常の最適化方法でもうまくフレーム最適化できない GIF アニメーションの極端な一例があります。このアニメーションは基本的に、変化しない単純な背景画像に、フレームごとに位置が変わる透明な「穴」が空いているだけです。これを作るには合体された画像シーケンスを作る必要があり、固定の背景画像にレイヤーのアルファ合成を使って穴を切り抜きます。また「[+antialias](https://imagemagick.org/command-line-options/#antialias)」スイッチを使い、3 種類の青と透明の計 4 色だけが使われるようにしました。こうすればカラー最適化の問題に対処せずに済みます。 | |
magick +antialias -size 100x100 -delay 100 xc:SkyBlue -loop 0 \
-fill DodgerBlue -draw 'circle 50,50 15,25' \
-fill RoyalBlue -draw 'circle 50,50 30,25' \
null: \( -size 100x100 xc:none -draw 'circle 40,25 27,22' \) \
\( +clone -rotate 90 \) \( +clone -rotate 90 \) \
\( +clone -rotate 90 \) -compose DstOut -layers Composite \
-set dispose background moving_hole.gif
gif_anim_montage moving_hole.gif moving_hole_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_frames.gif)
ご覧のようにアニメーションは機能し、丸い「穴」がこのページの背景色を見せていて、
バイトのアニメーションファイルになっています。では、このアニメーションに対して素直なフレーム最適化を試してみましょう。 | |
magick moving_hole.gif -layers OptimizeFrame moving_hole_opt.gif
gif_anim_montage moving_hole_opt.gif moving_hole_opt_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_opt.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_opt_frames.gif)
おや、何も起こりませんでした! IM が達成できた最良の最適化は、まったく変化なしでした! 上記の合体版のこのアニメーションは、これが最も最適なのでしょうか? このアニメーションに関する限り…そう、これが純粋なフレーム破棄最適化で達成できる、本当に最良の単純な最適化なのです! よくありません。問題は、GIF アニメーションが前のフレームが描いたピクセルを「消去」または「消す」には、「背景」破棄方法を使う必要があることです。ただし特殊な状況では「直前」破棄方法も使えます。しかし「背景」破棄は、たった今オーバーレイされた領域しか消去できません。最初のフレームは画像全体を完全にオーバーレイしたので、画像全体が消去されます。アニメーションのごく一部のピクセルだけを消去すればよいのにです。その結果、2 番目のフレーム全体をオーバーレイする必要が生じます。そのフレームのほとんどはたった今表示されたばかりなのに! このひどいジレンマがアニメーションの残り全体にわたって続き、基本的なフレーム最適化がまったくできません。このアニメーションのフレーム最適化が難しい、と言ったとおりです。
フレームの二重化 - 「穴」をフレーム最適化する方法
しかしすべてが失われたわけではありません。アニメーションにいくつか余分なフレームを加えることで、「[OptimizeFrame](#optframe)」メソッドに、利用可能な GIF 破棄方法をうまく活用する余地を与えられます。例えばここでは、最初の画像を二重化して余分なフレームを 1 つ加えますが、アニメーション全体のタイミングを変えないよう、ディレイをゼロにします。 | |
magick moving_hole.gif[0] -set delay 0 moving_hole.gif \
-layers OptimizeFrame moving_hole_dup.gif
gif_anim_montage moving_hole_dup.gif moving_hole_dup_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_dup.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_dup_frames.gif)
最初のフレームを二重化することで、アニメーションは
バイトから
バイトへと縮小されました。よって、アニメーションは今や 5 フレームになりましたが、サブフレーム画像オーバーレイのサイズが大幅に減ったため、全体としてはずっと小さくなっています。二重化は本質的に、破棄方法によるピクセル消去機能を、次のフレームが行うピクセルオーバーレイ機能から分離します。破棄もオーバーレイも GIF アニメーションプログラムでは同じフレーム更新の一部として行われるので、速度や品質の低下は感じられないはずです。これは複雑で巧妙な手法であり、GIF アニメーションのデザイナーや GIF 最適化プログラムが目にしたり理解したりすることはまれですが、必要なときにはその恩恵は十分に得る価値があります。しかしサブフレーム画像サイズの縮小は短期間しか続きません。後のフレームも次のフレームのためにピクセルを消去しなければならないので、フレームは再び大きくなり、後のピクセルを消去し続けます。つまり、ピクセル消去は常により大きなフレームを生み、決して小さくはしないからです。では、_すべての_フレーム(二重化が不要な最後のフレームを除く)を二重化して、最終的な画像にどう影響するか試してみましょう… | |
magick moving_hole.gif \( -clone 0--1 -set delay 0 \) \
+delete -insert 2 -insert 1 -insert 0 \
-layers OptimizeFrame moving_hole_double.gif
gif_anim_montage x2 moving_hole_double.gif moving_hole_double_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_double.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_double_frames.gif)
ご覧のように、フレーム数はほぼ倍になりましたが、画像サイズはすべてずっと小さくなり、
バイトのアニメーションが生成されました。より小さな結果ですが、最初に行った 1 フレームだけの二重化ほど大きな節約ではありません。何が起きているか追えるように説明すると、「[背景](anim_basics.html#background)」フレームは前のフレームの正確な複製で、表示されているものに変化を加えません。しかし、次のフレーム画像をオーバーレイする前に消去すべきアニメーションの領域を定義します。続く「[None](anim_basics.html#none)」フレームが、変更すべきピクセルと、前のフレームの破棄が消去したピクセルを埋めます。上記のアニメーションでは、それは新しい穴を形作るのに必要なピクセルと、前の「穴」を埋め戻すのに使われたピクセルを意味します。結果は小さくなりますが、余分なフレームを加えること自体にコストがあるので、それほど大きくはありません。少なくとも、加えた各フレームが独自のカラーテーブルを持たないからこの程度で済みます。さもなければ余分なカラーテーブルのサイズのために、このアニメーションは実際には大きくなってしまっていたでしょう!
Layer Optimize Plus - 自動フレーム二重化最適化
うれしいことに、バージョン 6.2.7 以降、IM は通常のフレーム最適化処理の一部として、フレームの二重化最適化を自動で行えるようになりました。しかし、アニメーションを小さくするためにフレームを加えるというのはあまりに思い切った手なので、独自の「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッド「**OptimizePlus**」が与えられました。例えば、IM にフレーム二重化最適化をさせてみましょう… | |
magick moving_hole.gif -layers OptimizePlus moving_hole_oplus.gif
gif_anim_montage x2 moving_hole_oplus.gif moving_hole_oplus_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_oplus.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_oplus_frames.gif)
つまり、IM は先ほどのフレーム二重化の例と同じ結果を返しました。よって GIF ファイルは依然として
バイトです。ただし「OptimizePlus」は、結果として得られるアニメーション(3 フレーム)の現フレームと次フレームのピクセル数が減る場合にのみフレーム二重化を行うので、二重化するかどうかは IM に判断させられます。「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッド「[OptimizePlus](#optimizeplus)」はフレーム最適化された GIF アニメーションを作る際に余分なフレームを加えるので、最終的なアニメーションに変化を加えない不要・余剰なフレームも取り除きます(ディレイ時間は適宜マージします)。つまり、自動的に「[RemoveDups](#removedups)」(次項参照)も行います。「[OptimizeFrame](#optframe)」メソッドはこれを行いません。
重複フレームの除去 - 連続する重複画像のマージ
残念ながら、このアニメーションを合体 (coalesce)すると、上記で加えた余分なフレームもすべて得られてしまいます。
magick moving_hole_oplus.gif -coalesce gif:- |\
gif_anim_montage x2 - moving_hole_oplus_cframes.gif
合体されたアニメーションからそうした無用な重複フレームを取り除けるよう、「**RemoveDups**」メソッドが用意されています。これはアニメーションの各フレームを次のフレームと比較し、両者が(現在のファズ係数で設定された色の類似度で)同一なら最初のフレームを取り除きます。また、アニメーションのタイミングが失われないよう、2 つのフレームのタイミングのディレイもマージされます。例えば…
magick moving_hole_oplus.gif -coalesce -layers RemoveDups gif:- |\
gif_anim_montage - moving_hole_oplus_rmdups_frames.gif
これで、アニメーションは元の合体された形に戻りました。余分なフレームを取り除く別の方法については、下記の「[RemoveZero](#removezero)」メソッドを参照してください。
フレーム更新の分割 - 離れた 2 つの変化を別々に更新する
フレーム二重化で見たように、「ピクセルの消去」を新しいピクセルのオーバーレイから分離することで、単一フレームのオーバーレイ全体のサイズを減らせます。しかしこのアニメーションは依然として非常に大きなオーバーレイを生み、そのほとんどがフレーム間で実際には変化しないピクセルで構成されています。つまり、主なオーバーレイフレームは、互いにかなり離れた 2 つのかなり小さな領域だけを更新しているのに、単一の大きなオーバーレイ画像を生んでいるのです。両方の変化を同時に更新し、その間にある変化しないピクセルもすべて含めるのではなく、代わりに各領域を別々に更新します。つまり、_フレーム更新を 2 つの段階に分割_し、それぞれが分離した領域の変化を担当します。この場合、まず古い穴を埋め、それから新しい穴を別の更新として作れます。2 つの別々の変化のどちらをどの順番で行うか(破棄に関する可能性を除けば)は実は重要ではありませんが、論理的にするよう努めるべきです。また、一方の変化が他方より作りやすいこともあります。例えばここでは、古い穴を埋める処理を、新しい穴を「掘る」処理とは別の更新として、余分なフレームを挿入します。これは生成しやすい中間フレームであると同時に、動作の最も論理的な順序でもあります。もちろん最後のフレームに対してこれを行う必要はありません。そのフレームはアニメーションがループする前に捨てられるだけだからです。 | |
magick moving_hole.gif \
\( +antialias -size 100x100 -delay 0 xc:SkyBlue \
-fill DodgerBlue -draw 'circle 50,50 15,25' \
-fill RoyalBlue -draw 'circle 50,50 30,25' \) \
\( +clone \) -insert 1 \( +clone \) -insert 3 +swap \
-set dispose background moving_hole_split.gif
gif_anim_montage x2 moving_hole_split.gif moving_hole_split_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_split.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_split_frames.gif)
加えた中間フレームは、その周囲のユーザーに表示されるフレーム(ディレイ時間がゼロでないもの)とは異なることを忘れないでください。これは単純な「フレーム二重化」ではなく、離れた 2 つの小さな変化を分離するものです。この中間フレームの追加は、自動化できる単純なステップではありません。賢いヒューリスティックを開発してこれらの中間フレームを生成することは可能かもしれませんが、何をすべきか、さらにはそれをすべきかどうかすら、常に自明とは限りません。そうしたヒューリスティックを作ってみたい方は、私にメールしてください。 では、これらの余分なフレームを加えた後で、標準的なフレーム最適化を試してみましょう… | |
magick moving_hole_split.gif \
-layers OptimizeFrame moving_hole_split_opt.gif
gif_anim_montage x2 moving_hole_split_opt.gif \
moving_hole_split_opt_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_split_opt.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_split_opt_frames.gif)
これら「ディレイがゼロの中間フレーム」の追加により、このアニメーションは元の未最適化アニメーションよりもうまくフレーム最適化でき、
バイトのアニメーションを生成します。しかしこの特定のケースでは、自動のフレーム二重化手法(上記の「OptimizePlus」layers メソッドを参照)を使うほどには良くありません。とはいえ、「ディレイがゼロの中間フレーム」を加えても、その「フレーム二重化」手法を併用することは妨げられません… | |
magick moving_hole_split.gif \
-layers OptimizePlus moving_hole_split_oplus.gif
gif_anim_montage x2 moving_hole_split_oplus.gif \
moving_hole_split_oplus_frames.gif
![[IM Output]](../static/img/anim_opt/moving_hole_split_oplus.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_split_oplus_frames.gif)
このアニメーションは今や、フレーム更新ごとに 2 つの余分な「ディレイがゼロの中間フレーム」を持ちます。最初のフレームは古い穴を埋め、2 番目のフレームは透明ピクセルを含む領域を消去し、最後に消去すべきでなかったピクセルを復元します。結果はこの特定の問題アニメーションに対して可能な限り最も最適なフレーム最適化となり、最終ファイルサイズは
バイトになります。つまり、4 フレームのアニメーションが、ディレイ時間ゼロのフレームを 6 つ加えることで小さくなったのです! 元のフレーム数の倍以上です。奇妙ですが本当です! もちろん、GIF アニメーションプログラムがディレイがゼロの中間フレームを、その正体、すなわちアニメーションの本当のフレーム間の中間更新として実際に認識してくれるといいのですが。とはいえ、更新が大きく分離していて非常に小さい場合、余分なフレームによるわずかな間はめったに目立ちません。
もちろん、アニメーションの分離した 2 つの部分が実際には関係していないなら、時間的に同期させる必要はありません。別の代替案としては、余分なフレームを加える代わりに、アニメーションを 2 つの完全に別々のアニメーションに分割し、ウェブページ上で一緒に表示することもできます。アニメーションの分割を参照してください。ただし、この特定のアニメーションは、時間的に独立した別々のアニメーションには分割できません。第一に、離れた変化を時間的に同期させる必要があります。第二に、変化する 4 つの領域が水平方向にも垂直方向にも重なっています。つまり、何らかの CSS の小技なしには、単純な HTML の「表」でサブアニメーションを完全な全体に再結合できません。間違いを証明できますか? FUTURE: 「離れた 2 つのオブジェクト」をアニメーション化する、より良い例への参照。「アニメーションの扱い」で、別々に動く 2 つのオブジェクトを扱う例など。
ディレイがゼロのフレームの除去 - 中間更新を取り除く
もちろん、これら加えられた中間フレームに関心がなく、アニメーションから取り除いて、一定時間ユーザーに実際に表示されるフレームだけを残したいこともあります。アニメーションを単に合体 (coalesce)して「[RemoveDups](#removedups)」メソッドを使うことはできません。すべての「中間フレーム」が周囲のフレームに似ているわけではなく、よって重複ではないからです。しかし、この種のフレームはディレイ時間がゼロなので、別の特別な「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッド「**RemoveZero**」を使えます。これはディレイ時間がゼロのフレームをすべて取り除きます。この同じメソッドは、フレーム二重化や「[OptimizePlus](#optimizeplus)」手法で加えられたフレームも取り除きます。例えば…
magick moving_hole_split_oplus.gif -coalesce -layers RemoveZero gif:- |\
gif_anim_montage - moving_hole_split_rmzero_frames.gif
これも、アニメーションをユーザーに見えるフレームだけに戻し、アニメーションを単純化します。もちろんディレイがゼロの中間フレームを取り除いた後は、それらを再び加えるのは非常に難しくなります。含まれていた変化の情報が失われるからです。その結果、アニメーションはその後うまくフレーム最適化できないかもしれません。そうしたフレームの主目的の 1 つは、結局のところ最適化なのですから。
フレーム最適化の結果とまとめ
移動する穴のアニメーションの最適化をまとめましょう…
ご覧のように、複雑なフレーム処理を、IM と多少の人間の介入の助けを借りて使うことで、「移動する穴」のアニメーションを、フレーム数は元の 3 倍弱になったものの、元のサイズのほぼ半分までフレーム最適化できました。もちろん結果はアニメーションごとに大きく異なりますが、フレーム最適化に使った手法は同じです。少しの注意と前もっての考慮が必要なだけで、それは人間が得意とし、コンピュータが苦手とするものです。 | _要点はこうです。IM は現在着目しているフレームの組のピクセル数だけでなく、加えられた余分なフレームの全体サイズ、さらにはおそらく得られる全体の圧縮結果も、どうフレーム最適化するか決定する際に考慮すべきだということです。
一方で IM は、直接関係するフレームを超えて、結果として得られるピクセル数の節約も見ていません。つまり、後のフレームのサイズも、フレーム二重化や使われた破棄方法の結果として小さくなりうるのです。これは特に、すぐ次のフレームではなく後のアニメーションシーケンスで大幅なピクセル数削減をもたらしうる「直前画像破棄」方法を使うかどうかの選択で当てはまります。ここでの良い選択には、しばしば人間の入力が必要です。
そのため、IM が特定のアニメーションに対して最良の最適化選択を生成すると保証はできません。しかし、再帰を使わずに、その選択をしっかり試みていることは確かです。つまり、決定には直近のピクセル数だけを使っています。
再帰的なアルゴリズム、つまり選択をしてから、その選択(さらに先の再帰的選択も含む)から結果として得られるアニメーションの最良の最終サイズを見るものなら、保証された最良の最適化を生成できます。しかし、それは極めて遅い演算子にもなりえ、大きなアニメーションでは最終決定に何年もかかりかねません。また、圧縮最適化の選択も含める必要があります。これらが最終結果に影響しうるからです。言い換えれば、そうしたアルゴリズムは最良の最適化を保証できる一方で、重い計算コストでそれを行うのです。
もちろん、アニメーションが何を達成しようとしているかを熟知した人間なら、上記でフレーム更新の分割で見たように、複雑なアニメーションでは一般によりうまくやります。
再帰的な GIF 最適化演算子を作ってみたい方は、ぜひどうぞ。できる限りお手伝いします。市場のほぼすべての他の GIF 最適化プログラムを上回るでしょう。また、ほとんどの GIF アニメーション開発者はおそらくあなたの努力に(金銭的に)とても感謝するでしょう。_
---|---
半透明の扱い
GIF ファイル形式は半透明ピクセルの使用を許しません(GIF のブール透明度を参照)。これは事実であり、アニメーションを適切に最適化する前、あるいは GIF 形式に保存する前でさえ、存在しうる半透明ピクセルを、そのアニメーションに適した方法で処理する必要があります。デフォルトでは、これらのピクセルを処理しないと、IM は 50% しきい値を使ってこれらのピクセルを完全に透明か完全に不透明のどちらかに変換します。しかし、それは問題への最良の処理方法ではないかもしれません。特に、影効果のように半透明ピクセルの大きな領域を含む画像ではそうです。例えば私は、テレポートされるオブジェクトとしてほぼ任意のサブ画像を受け取れる、スターゲイトのアスガルドのテレポートアニメーションを作りたいと思いました。
magick -channel RGBA -fill white \
\( medical.gif -repage 100x100+34+65 -coalesce -set delay 200 \) \
\( +clone -motion-blur 0x20+90 -blur 0x3 -colorize 100% \
+clone -colorize 30% +swap -composite -set delay 10 \) \
\( +clone -roll +0-20 -blur 0x3 -colorize 30% \
-motion-blur 0x15+90 -motion-blur 0x15-90 -set delay 10 \) \
\( +clone -colorize 30% \
-motion-blur 0x30+90 -blur 0x5 -crop +0+10\! \) \
\( +clone -motion-blur 0x50+90 -blur 0x2 -crop +0+20\! \) \
\( +page -size 100x100 xc:none -set delay 200 \) \
-set dispose background -coalesce -loop 0 teleport.miff
gif_anim_montage teleport.miff teleport_frames.png
私はあえてアニメーションを IM の内部 MIFF: ファイル形式のままにしました。これにより元の画像が変更なしに保存されることが保証されるからです。そしてフレームの表示には PNG: ファイル形式を使い、そこに含まれるすべての半透明ピクセルが見えるようにしました! これは半透明ピクセルを含むアニメーションだけでなく、色数が多いアニメーションでも重要です。いったん画像シーケンスを GIF に保存してしまうと、良いカラー最適化を生成できる見込みは、高いものから難しいものへと変わります。 さて、アニメーションシーケンスができました。これを直接 GIF として保存しようとすると、IM はそれら半透明ピクセルをすべてしきい値処理してしまいます。 |
magick teleport.miff teleport_thres.gif
gif_anim_montage teleport_thres.gif teleport_thres_frames.gif
結果は私たちが望んだものとはまるで違って見えます。デフォルトの 50% 透明度処理は、アニメーションを縮む「卵」のように見せます。このアニメーションで達成したかったものでは決してありません。この種の透明度処理で許容できるなら、他の最適化に進む前に、次のように適用します…
magick teleport.miff -channel A -threshold 50% +channel \
...ここでさらに処理を行う... teleport.gif
上記の自前のやり方を使う利点の 1 つは、しきい値レベルを制御できることです。例えば「10%」とすれば、存在するほぼすべての半透明ピクセルを除去し、「90%」とすれば、それらをすべて不透明にできます。 |
magick teleport.miff -channel A -threshold 90% +channel teleport_thres90.gif
gif_anim_montage teleport_thres90.gif teleport_thres90_frames.gif
しかし、このようなアニメーションにしきい値を適用するのは良い解決策ではありません。私が達成しようとしている透明効果を本当に台無しにするからです。 上記アニメーションのすべての特殊効果を保つ最良の全体的解決策は、単に単色の背景を加えることです。 |
magick teleport.miff -bordercolor skyblue \
-coalesce -border 0 teleport_bgnd.gif
gif_anim_montage teleport_bgnd.gif teleport_bgnd_frames.gif
これはアニメーションからすべての透明度を取り除きますが、アニメーションが特定の背景色でのみ機能するという代償を伴います。しかし特定のウェブページ用にアニメーションを作っているなら、それは十分許容できるかもしれません。ただし、輪郭が鋭い画像では、このようなディザパターンを使うと鋭い縁が「点々とした」輪郭になることがあります。そのため、一般的なケースには推奨しません。もう 1 つの解決策は、透明ピクセルと不透明ピクセルの何らかのパターンを生成して、画像の半透明度を保とうとすることです。これには IM がこの問題を解決できる幅広いディザリングオプションを提供しています。FUTURE: 透明度のディザリングに関する、これから作成するセクションへのリンク。例えば量子化とディザリング。 なお、アルファチャンネルのモノクロディザリングを使うという明白な最初の解決策は単純ではなく、正しく行うにはおそらく高度なマルチ画像合成が必要です。 単純な解決策は、アルファチャンネルだけに限定できる拡散ピクセル秩序ディザ手法を使い、半透明ピクセルを除去することです。 |
magick teleport.miff -channel A -ordered-dither o8x8 teleport_od.gif
gif_anim_montage teleport_od.gif teleport_od_frames.gif
結果はまずまずですが、テレポートというより溶けていくオブジェクトのように見えます。 ハーフトーンを使うと、透明パターンをより大胆にすることで、もっと良い効果が得られます。 |
magick teleport.miff -channel A -ordered-dither h8x8a teleport_htone.gif
gif_anim_montage teleport_htone.gif teleport_htone_frames.gif
しかしこの特定のアニメーションでは、(水平線のディザパターンから)垂直線を生成するユーザー設計のディザマップを使うと、半透明ピクセルを除去しつつ、テレポートアニメーションを引き立てる効果が得られることがわかりました。 |
magick teleport.miff -rotate 90 \
-channel A -ordered-dither hlines -rotate -90 teleport_lines.gif
gif_anim_montage teleport_lines.gif teleport_lines_frames.gif
ご覧のように、GIF アニメーションの半透明を扱うには、かなり多くの可能性があります。
カラー最適化
半透明ピクセルの処理は、GIF ファイル形式の最初の制約にすぎません。次の制約は、アニメーションの各カラーテーブルにつき 256 色という制限です。フレームごとに別々のカラーテーブルを持つことが許されています。これは、単一のアニメーションが 256 色を超える色を持てることを意味します。しかし、それでさえ常に良い考えとは限りません。利用可能なカラー最適化オプションの手早い要約が欲しいだけなら、動画から GIF への変換の例に飛ぶことをお勧めします。そこではアニメーションの色の問題が最悪の形で現れています。
GIF のカラー問題
特に GIF アニメーションは色の扱いに問題を抱えています。まず半透明の色を許さず、次にフレームごとに 256 色、またはグローバルで 256 色という制限があります。最後に、あるフレームのピクセルに使われた色が、画像のその部分が変化していないのに、次のフレームでも同じ色に一致しなければ、最良のフレーム最適化はうまく機能しません! これは簡単な問題に思えるかもしれませんが、減色はそれ自体が極めて複雑な分野で、IM 使用例でも独自のまるまる 1 セクションを必要としました。色の問題こそが、ワールドワイドウェブで見つかるほとんどの GIF アニメーションがアニメ調か、あるいは非常に見栄えが悪い理由です。特に、より大きな版のアニメーションからリサイズされた場合がそうです。アニメーションのリサイズでは、実際のリサイズ処理そのものよりも、カラー最適化により多くの労力が必要になるでしょう。ここでは、アニメーションの元のソースを持っていると仮定します。しかしそれが常に可能とは限らないので、変更済みの GIF アニメーションを最適化する場合は、いくらか余分な注意が必要かもしれません。とはいえ、色数が多すぎるアニメーションがある場合、まず覚えておくべきことは…
直接 GIF 形式に保存せず、
MIFF ファイル形式か、別々の PNG 画像を使うこと。
GIF として保存した途端、GIF のカラー最適化の努力を制御できなくなり、おそらくさまざまなフレーム最適化手法を使ってもうまく最適化できない、非常に見栄えの悪い GIF アニメーションができてしまいます。
スピードのアニメーション - 色数が多すぎるアニメーション
まず、カラー最適化に関わる問題を本当に試せるよう、膨大な色数を持つ GIF アニメーションを生成する必要があります。 | |
magick -dispose none -channel RGBA \
\( medical.gif -repage 100x60+5+14 -coalesce -set delay 100 \) \
\( medical.gif -repage 100x44+34+6 -coalesce -set delay 10 \
-motion-blur 0x12+0 -motion-blur 0x12+180 -wave -8x200 \) \
\( medical.gif -repage 100x60+63+14 -coalesce -set delay 100 \) \
\( medical.gif -repage 100x44+34+6 -coalesce -set delay 10 \
-motion-blur 0x12+0 -motion-blur 0x12+180 -wave +8x200 \) \
null: \( +page -size 120x15 xc:SkyBlue xc:RoyalBlue \
-size 120x70 gradient:SkyBlue-RoyalBlue \
+swap -append -blur 0x3 -background white -rotate -25 \
\) -gravity center -compose DstOver -layers Composite \
-loop 0 speed.miff
magick speed.miff speed.gif
gif_anim_montage speed.gif speed_frames.gif
![[IM Output]](../static/img/anim_opt/speed.gif)
![[IM Output]](../static/img/anim_opt/speed_frames.gif)
私はアニメーションを直接 GIF 形式に保存せず、まず MIFF 形式のファイル「[speed.miff](../static/img/anim_opt/speed.miff)」に保存したことに注意してください。これは、GIF のメタデータ、タイミングのディレイ、さらには歪みのない画像のすべての色を含め、元々作成(または変更)されたアニメーションのあらゆる側面を保ちます。元のアニメーションを保ってから初めて、私は元のアニメーションを直接 GIF 形式に変換しました。そうすれば、上記のコードが何を達成しようとしているか、なぜそれを「スピード」と呼んだかを示せます。これはまた、研究と後の比較のための基準となる GIF アニメーションを提供するためでもありました。では、元のアニメーションのさまざまな詳細を見てみましょう。
magick identify -format "Number of Frames: %n\n" speed.miff | head -1
magick identify -format "Colors in Frame %p: %k\n" speed.miff
magick speed.miff +append -format "Total Number of Colors: %k" info:
ご覧のように、アニメーションの各画像は非常に多くの色を持っています。各フレームが異なる色数を持つだけでなく、1 番目と 3 番目のフレームは色の面で非常に似ていますが、まったく同じではありません。しかし GIF ファイル形式はフレームごとに最大 256 色しか保存できないので、ImageMagick がこれを GIF 形式に保存したとき、最も速く、最も間抜けなやり方でそうしました… アニメーションの各フレームの色数を減らしたのです(減色と呼ばれる処理)…
magick identify -format "Colors in Frame %p: %k\n" speed.gif
magick speed.gif +append -format "Total Number of Colors: %k" info:
各フレームの減色後の色数がわずかに異なるため、IM はアニメーションの各フレームに別々のカラーマップを供給する必要もありました。これは、GIF ファイルが(常にそうであるように)1 つの「グローバルカラーテーブル」を持つだけでなく、3 つの別々の「ローカルカラーテーブル」も持つことを意味します。「magick identify」コマンドは、GIF ファイルがそうしたローカルカラーテーブルをいくつ持つかを教えてくれません。その情報は形式に固有すぎて、IM が通常行う画像処理には重要でないからです。しかし、より専門的な「[Giftrans](http://www.ict.griffith.edu.au/anthony/software/#giftrans)」プログラムは、低レベルのローカルカラーテーブルがいくつ使われたかを教えてくれます…
giftrans -L speed.gif 2>&1 | grep -c "Local Color Table:"
ご覧のように、このアニメーションは
個のローカルカラーテーブルを持っており、これは画像内に存在するフレーム数より 1 つ少なく、まさに私が予測したとおりです。各フレームが異なる色の組を持つだけでなく、誤差補正ディザの問題点で述べたように、わずかに異なる色のパターン(画像のディザパターン)も持ちます。通常、この IM の減色とディザリングのデフォルト動作は非常に良く、絵、特に実生活の写真に完璧に適しています。実際、アニメーションの個々のフレームは一般に素晴らしく見えます。問題はすべて、個別に減色されたフレームを後で 1 つのアニメーションシーケンスに連結しようとするときに起こります。
カラー最適化の前にフレーム最適化を?
上記で見たように、アニメーションを直接 GIF 形式に保存することは機能しますが、フレームからフレームへかなり多くの色の差が生じ、これは後のフレーム最適化にとって悪いことです(後で見るとおり)。色の差がこうした問題を引き起こすのを防ぐには、アニメーションを保存する前にフレーム最適化を行い、フレーム間に持ち込まれる色の差を避けられます。 ただし、減色の前にフレーム最適化を行うと、減色のダイナミクスが変わることに注意してください。最適化されたサブフレームには、しばしば動かない静的な領域がより少なく現れるので、そのフレームの減色はそれらの色をより重要でないとみなせ、よりわずかな色になることがあります。
あいまいなカラー最適化
しかし、GIF 形式に保存される前の元のアニメーションにアクセスできないこともあります。これは特に、元のアニメーションを WWW からダウンロードした場合に当てはまります。それは、すでにすべての GIF の色の歪みが存在するアニメーションを手にしていて、後の最適化で問題が生じることを意味します。さて、フレームからフレームへわずかに異なる色の組が使われ、アニメーションの各フレームに異なるピクセルのパターンが使われるため、各フレームはまったく別の画像とみなせます。例えば、大量の同じ背景画像を共有する、1 番目と 3 番目のフレームを比較してみましょう… |
magick compare speed.gif'[0,2]' speed_compare.gif
![[IM Output]](../static/img/anim_opt/speed_compare.gif)
上の例の赤い領域は、予想どおり異なる 2 つの塗りつぶされた四角い領域を示しています。しかしそれだけでなく、2 つのフレームの背景を縁取る色の差の帯も示しています。これらは、まったく同じ背景を表すのに異なる色のピクセルが使われた、背景グラデーションの縁に沿った「かき混ぜられた」ディザパターンを表しています。これは、異なる色の組やディザパターンを使うことで生じる背景の乱れが最も少なかったフレームの組でもありました。実際の連続したフレーム間の差ははるかに悪く、ほぼ真っ赤な差を生みます。 | _このような画像の差は、ソース画像が JPEG 画像形式で保存されていた場合にも問題になります。この形式はロッシー圧縮方法を使い、(100% 品質でさえ)画像にわずかな色の差を生じさせます。ただし、その差は一般に画像全体ではなく、実際の差の領域の周りのハロに限られます。
私が言えるのは、すべてのフレームの静的な背景画像として 1 枚の画像を使う予定でない限り、アニメーションへの JPEG 画像の使用は避けよ、ということだけです。_
---|---
アニメーション内で 1 つのフレームから次のフレームへ非常に多くのピクセルが異なるので、アニメーションをフレーム最適化しようとすると、最適化がまったく得られないのも驚くにはあたりません…
magick speed.gif -layers OptimizeFrame speed_opt2.gif
gif_anim_montage speed_opt2.gif speed_opt2_frames.gif
しかし、アニメーションフレームの変化しない部分の間のピクセルの色の差のほとんどは、実際にはむしろ小さいものです。そうでなければ、とても良い減色ではなかったでしょう。それは、IM に色の比較を少し緩めるよう頼めば、わずかな色の差を無視するよう頼めることを意味します。これは適切なファズ係数を設定することで行います。
magick speed.gif -fuzz 5% -layers OptimizeFrame speed_opt3.gif
gif_anim_montage speed_opt3.gif speed_opt3_frames.gif
ご覧のように、小さなファズ係数を加えることで、IM はわずかにしか異ならないピクセルを無視するようになり、まずまずのフレーム最適化を生成します。どれだけのファズ係数が必要かは、IM が元の画像の減色でどれだけ苦労したかによります。この場合はそれほどでもなかったので、ごく小さな係数だけが必要でした。小さなファズ係数が許容できる結果を生むなら、フレーム最適化と透明度の最適化にそれを設定すればよいだけです。ただ、フレームごとに別々のカラーテーブルがまだあって対処する必要があることを忘れないでください。それが次の議論の点です。なお、フレーム最適化は 2 番目のフレームに「直前破棄」を使う決定をしたことにも注意してください。つまり、2 番目のフレームを表示した後、オーバーレイの前に画像を直前のフレーム破棄(最初の画像)に戻すのです。これは、全体を通して破棄を使わない場合よりも小さなオーバーレイ画像サイズになりました。全体を通してNone 破棄だけを使う単純なオーバーレイアニメーションが欲しいだけなら、代わりに古い Deconstruct 演算子(Layers CompareAny としても知られる)を使ってそれを生成できたでしょう。
magick speed.gif -fuzz 5% -deconstruct speed_opt4.gif
gif_anim_montage speed_opt4.gif speed_opt4_frames.gif
単一のグローバルカラーテーブルの生成
さて、すべてのフレームが異なる色の組を持つため、IM はフレームごとに別々のカラーテーブルを付けて画像を保存せざるを得ませんでした。最初のフレームには 1 つのグローバルなもの、後のフレームには 3 つのローカルカラーテーブルです。例えば、ここでは非常に単純なプログラム「[Giftrans](http://www.ict.griffith.edu.au/anthony/software/#giftrans)」を使い、いくつのフレームカラーテーブルが作られたかを報告させました。
giftrans -L speed.gif 2>&1 | grep -c "Local Color Table:"
完全に合体された(あるいはフィルムストリップのような)アニメーションでは、フレームごとに別々のカラーテーブルを持つことは完全に問題なく妥当であり、そうした状況ではこれは問題ではありません。つまり、まったく異なる画像のスライドショーには、別々のカラーテーブルが最も見栄えの良い結果を生みます。そのため、これは IM の通常の動作です。しかし、これらの余分なカラーテーブルはすべて非常にコストがかかります。各カラーテーブルは多くの容量を使いえます。画像の各フレームにつき最大 768 バイト(256 色 × 1 色あたり 3 バイト、または 3/4 キロバイト)です。それだけでなく、GIF 圧縮はこれらのカラーテーブルを圧縮せず、ピクセルデータだけを圧縮します! 別々のカラーテーブルにこれだけのファイル容量を割くのが問題なら、特にほとんどの GIF アニメーションのように色があまり変化しない画像では、IM に必要なグローバルカラーテーブルだけを使わせ、ローカルカラーテーブルを一切加えないようにできます。--- ローカルカラーマップを取り除くには、すべての画像が palette 型になり、すべて同じパレットを使う必要があります。コマンドラインでは、これは「-map image」を設定して共通パレットを定義することで行えます。-colors は使えません。それは個々の画像に対して働くからです。コマンドラインの解決策は特別な「[+map](https://imagemagick.org/command-line-options/#map)」オプションで、すべての画像に加えられる共通パレットへのグローバルな減色を行います。なお、画像へのどんな変更もパレットを無効にしやすいので、減色は GIF のフレームおよび/または圧縮の最適化の前に行うべきですが、共通パレットは保存直前の最後に行う必要があります。「[+map](https://imagemagick.org/command-line-options/#map)」が画像の色数を減らす必要がなければ、それを行ったり色をディザリングしたりせず、すべての画像にわたって共通パレットを加えるだけです。--- IM は、すべてのフレームが同じカラーパレットを使う場合、単一のグローバルカラーテーブルを生成できます。IM では、カラーパレットはそうしたパレットを使う画像形式から読み込むか、「[-map](https://imagemagick.org/command-line-options/#map)」減色演算子を使って割り当てることでのみ、画像に割り当てられます。詳しくは事前定義カラーマップによるディザを参照してください。この単一カラーテーブルを生成する 1 つの方法は、単にすべてのフレームを「[-append](https://imagemagick.org/command-line-options/#append)」で連結し、それから「[-colors](https://imagemagick.org/command-line-options/#colors)」コマンドを使って色数を最小限の組(256 未満、あるいはもっと小さなカラーテーブルが欲しければさらに小さく)に減らすことです。結果のカラーテーブルは、それから「[-map](https://imagemagick.org/command-line-options/#map)」を使って元の画像に適用できます。例えば、ここでは画像を 64 色の単一の組に減らします。これは特別な MPR メモリ内レジスタを使って、生成したカラーマップを「[-map](https://imagemagick.org/command-line-options/#map)」コマンドに割り当てます。 |
magick speed.gif \
\( -clone 0--1 -background none +append \
-quantize transparent -colors 63 -unique-colors \
-write mpr:cmap +delete \) \
-map mpr:cmap speed_cmap.gif
![[IM Output]](../static/img/anim_opt/speed_cmap.gif)
さて、結果のアニメーションを「[Giftrans](http://www.ict.griffith.edu.au/anthony/software/#giftrans)」を使って調べると、画像が今やフレームごとの別々のカラーテーブルではなく、単一の「グローバル」カラーテーブルを使っているのがわかります。 | _画像を連結する前に「None」の「[-background](https://imagemagick.org/command-line-options/#background)」色を使っているのは、これを合体されていないアニメーションに使えるようにし、余分な不要な色を加える可能性をなくすためです。
特別な「[-quantize](https://imagemagick.org/command-line-options/#quantize)」設定の「transparent」カラースペースを使ったのは、IM がカラーマップに半透明の色を生成しようとしないようにするためです。結果を半透明を扱えない GIF に保存するので、それは無用なことです。
最後に、透明色のためのスペースを残すよう、63 色に減色します。透明度を必要とするアニメーションもあれば、(これのように)後で圧縮最適化のためにまだ必要とするものもあります。_
---|---
これをより簡単にするため、IM はすべてのフレームにわたって共通のカラーマップ(256 色)を生成し、グローバルに適用する特別なオプション「[+map](https://imagemagick.org/command-line-options/#map)」も提供しています。これは上記の自前の方法よりずっと単純です。 |
magick speed.miff -alpha off +map speed_map.gif
![[IM Output]](../static/img/anim_opt/speed_map.gif)
これにより、結果の画像には
個の「ローカル」(あるいは余分な不要な)カラーテーブルが生じました。次の最適化セクションでは、このアニメーションの単一カラーテーブル版を使います。とはいえ、これは実際にはアニメーション最適化のどの時点でも、特に最終保存の前にも行えます。 カラーテーブル最適化の結果、直接変換した GIF では
バイトだったアニメーションが、「[+map](https://imagemagick.org/command-line-options/#map)」演算子を使った後は
バイトになりました。アニメーションのフレーム(と「ローカルカラーテーブル」)が多いほど、節約も大きくなります。さて、アニメーションへのどんな変更も一般に各画像の保存されたパレットを取り除くので、「[+map](https://imagemagick.org/command-line-options/#map)」演算子が GIF へのアニメーション保存の前の最後の操作であることが重要です。覚えておいてください。
ローカルカラーマップの除去は、GIF 形式への保存前の最後の最適化であるべきです。
秩序ディザ、「ノイズ」の除去
作成中
ただし、これまで見てきたすべての手法では、1 つのオーバーレイから別のオーバーレイへとディザパターンが変わりうることに注意してください。テレビのノイズのように見えるピクセルのかき混ぜです。
... 少ない色数 ...
より小さい動かない領域のフレーム最適化では、もっとひどく見える四角い領域のノイズが出ることさえあります。 ... 秩序ディザ ... 今のところは、より実践的で詳細の少ない動画から GIF へ、最適化のまとめを参照してください。
圧縮最適化
半透明ピクセルを処理し、カラーと フレームの最適化を使って、アニメーションを GIF 形式に保存したら、GIF 圧縮アルゴリズムに合わせることで、さらにファイルサイズを多少減らすこともできます。GIF ファイル形式が使える LZW 圧縮やランレングス圧縮は、一定の色の大きな領域や、何度も繰り返されるピクセル列を見つけると、より良く圧縮します。
透明度の最適化
| フレーム最適化で見たように、オーバーレイされる画像は、すでに表示されているものを繰り返しているだけのことがよくあります。つまり、GIF 破棄方法が適用された後にすでに存在しているのと同じ色のピクセルをオーバーレイしているのです。しかし、それらのピクセルをわざわざ繰り返す必要はありません。画像ですでに透明度を使っているなら、利用できる透明ピクセル色があります。それらの領域を透明に変換すれば、均一な透明ピクセルのより大きな領域が得られます。それは、オーバーレイされる同じ領域に一致させるのに必要な、異なる色の混合を使うよりも、良く圧縮できます。例えば、ここに単純なフレーム最適化されたオーバーレイアニメーションがあります… | ![]() |
![]() |
|---|---|---|
では、「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッド「**OptimizeTransparency**」(IM v6.3.4-4 で追加)を使い、表示結果を変えるピクセルを透明に置き換えてみましょう。 |
magick bunny_bgnd.gif -layers OptimizeTransparency \
+map bunny_bgnd_opttrans.gif
gif_anim_montage bunny_bgnd_opttrans.gif bunny_bgnd_opttrans_frames.gif
![[IM Output]](../static/img/anim_opt/bunny_bgnd_opttrans.gif)
![[IM Output]](../static/img/anim_opt/bunny_bgnd_opttrans_frames.gif)
ご覧のように、サブフレームは今や大きな透明領域を持ち、それらは最終的な結果のアニメーションに影響しません。ピクセルの変更が必要な領域は依然としてオーバーレイされますが、変化しない領域は透明にされました。それにはアニメーション化されているオブジェクトの内部も含まれ、かなり見苦しい「穴」を残します。より大きな一定の透明色の領域は(理論上)より良く圧縮するので、結果の「乱雑な」アニメーションはずっと小さくなり、ファイルサイズはフレーム最適化された結果の
バイトから
バイトに減りました。これはごくわずかな労力で得られるかなり大きな節約です。なお、この最適化メソッドは合体されたアニメーションである必要はなく、サブフレームのサイズは変更されないまま残され、このフレームや後のフレームの破棄の必要性を保ちます。そのため、どんな節約も、アニメーションの同じピクセル数に対する圧縮率の改善という形だけであり、ファイルに保存される実際のピクセル数の節約ではありません。よって、これは必要なフレーム最適化を完了した後、最終的な最適化ステップの 1 つとして行うべきです。
FUTURE: アニメーションから「背景を取り除く」へのリンク
もちろん、他のほとんどの「[-layers](https://imagemagick.org/command-line-options/#layers)」メソッド(比較や最適化)と同様、ファズ係数を指定して、色が「どれだけ似ている」とみなすかを調整できます。これにより、ひどく色がディザリングされてしまったアニメーションを扱えます。とはいえ、上記のカラー最適化を学んでいれば、その問題は起きないはずですが。無料のアニメーション GIF ツール「**[InterGIF](http://utter.chaos.org.uk/~pdh/software/intergif.htm)**」も、上記と同じ種類の透明度圧縮最適化を提供しますが、「近い」色の変化も透明にする「ファズ係数」をサポートする機能はありません。IM が使えないときの代替を除いて、私はこれを推奨しません。
LZW 最適化 - (非 IM)
アプリケーションによっては、アニメーション内の画像の圧縮率をさらに最適化し、もっと小さくできるものもあります。しかしこれを行うには、GIF 画像ファイル形式が通常使う LZW 圧縮に関する専門的な知識が必要です。基本的に、特定のピクセル列がすでに LZW 圧縮アルゴリズムで処理されていれば、それらを透明ピクセルに変換しません。そうしても画像の圧縮が改善しないからです。奇妙に聞こえますが、うまくいきます。残念ながら、ImageMagick はこれを行いません。一般的なケースで良い結果を生む、まずまず良いヒューリスティックを得るには、多大なスキルとリソースを要する非常に複雑な処理だからです。しかし、「[Gifsicle](http://www.lcdf.org/gifsicle/)」アプリケーションをその最高の「-O2」最適化レベルで使い、この手法の実践的な例を示すことはできます。 |
gifsicle -O2 bunny_bgnd.gif -o bunny_bgnd_lzw_gifsicle.gif
gif_anim_montage bunny_bgnd_lzw_gifsicle.gif bunny_bgnd_lzw_frames.gif
LZW 圧縮最適化は、画像を単純な透明度最適化での
バイトから、「Gifsicle」では
バイトに減らしました。大きな改善ではありません。しかしより重要な側面は、LZW 最適化は(上記の透明度の最適化で行ったように)変化しないピクセルを透明に変換した一方で、すでに見たことのあるピクセル列は変えなかったことです。つまり、アニメーション内でまだ繰り返されていないピクセルのグループだけが変えられました。それらのピクセルは(おそらく)すでに LZW 圧縮パターンを使ってよく圧縮されるからです。なお、繰り返しのピクセルパターンを生成するためにどのピクセルを透明にすべきかの選択は、非常に複雑で難しく、正確な LZW 実装にすら依存しうることに注意してください。それは完全に予測可能なアルゴリズムではなく、ヒューリスティックです。そのため、異なるプログラムは一般に、圧縮される特定の画像に応じて異なる結果を生みます。あるプログラムはある画像でより良い圧縮率を生み、別のプログラムは別の画像でより良いかもしれません。
ロッシー LZW 最適化 - (非 IM)
もう 1 つの圧縮改善方法は、ピクセル色そのものを「近い色の一致」へとわずかに変更し、画像内の色参照の繰り返しを増やすことです。繰り返されるパターンは当然より良く圧縮するので、より高い圧縮率を生めます。先述の「Gifsicle」アプリケーションのフォークである giflossy も「gifsicle」プログラムを生成しますが、こちらは画像をわずかに変更する(「ロッシー」)オプションがあり、GIF 画像、特にアニメーションのサイズをさらに大きく減らせます。 | |
gifsicle -O3 --lossy=80 bunny_bgnd.gif -o bunny_bgnd_giflossy.gif
gif_anim_montage bunny_bgnd_giflossy.gif bunny_bgnd_lossy_frames.gif
作成: 2007 年 3 月 22 日 (「animation」の細分化)
更新: 2007 年 4 月 23 日
著者: Anthony Thyssen, Anthony.Thyssen@gmail.com
例の生成に使用: ![[version image]](../static/img/anim_opt/version.gif)
URL: https://usage.imagemagick.org/anim_opt/
これは
のサイズになり、これは驚くべき 1/3 のサイズ削減です。残念ながらこの方法は結果の画像を変更するため、最適化はロッシーです。微妙な色の情報を失いうるからです。プラスの面では、フレーム間の最適化ではなく、個々のフレームを圧縮できます。例えばここでは、先述の非ロッシー LZW 圧縮と、ロッシー LZW 圧縮を比較した差分画像を生成しました。 |
magick compare bunny_bgnd_lossy_frames.gif bunny_bgnd_lzw_frames.gif \
bunny_bgnd_diff_frames.gif
| ご覧のように、色の変更のほぼすべては、漫画のウサギではなく、元の「草」の背景画像にありました。基本的に、大きな差を生むのにちょうど十分なだけわずかに変更し、この画像では見た目に実際にはっきりわかる差はありません。もちろん減色とディザリングはそれ自体がロッシーな操作で、通常はいずれにせよ必要なので、GIF 画像や GIF アニメーションにロッシー圧縮方法を使うことは、それほど悪いとはみなされません。 | Photoshop で使うために特許化されたそうしたアルゴリズムの別の例については、米国特許 7031540 - 最小限の視覚的歪みで画像の Lempel-Ziv 圧縮性を高める変換を参照してください。読むのは骨が折れますが、より良い圧縮を達成するために使う方法を詳述しています。 |
|---|---|
秩序ディザによる LZW 最適化
ディザリング処理は通常 LZW 最適化よりもロッシーな処理なので、より良い解決策は、ディザリング処理の一部として繰り返し可能なパターンを導入しようとすることかもしれません。それは秩序ディザを使ってそうしたパターンを生成することで達成でき、これまでのすべての LZW 最適化方法よりもはるかに強力な LZW 圧縮の節約をもたらします。おまけに、静的な背景を持つ実生活のアニメーションのフレーム最適化も改善できます。これは特に、背景が静的で変化しないものになるよう、人工的に背景をきれいに整える場合に当てはまります。もちろん、秩序ディザによる圧縮最適化は、以前にディザリングされたり他の形でカラー最適化されたりしていない画像でのみ機能します。そのため、まだ GIF 画像形式向けに最適化されていないアニメーションでのみ機能します。また、現在 IM の秩序ディザは均一なカラーパレットでのみ機能します。IM にはまだ秩序ディザの「最良色」や「ユーザー提供」のパレット実装がありませんが、非常に限られた(そして固定の)カラーパレット向けにそうしたアルゴリズムを使うプログラムは見たことがあります。改善された LZW 圧縮最適化のために秩序ディザを使う実践的な例については、秩序ディザされた動画を参照してください。
その他の LZW 最適化
LZW 最適化のその他の改善は、画像内の「ディザパターン」の他の並べ替えによっても達成できます。そして、まさにそれを行える GIF ツールもあります。しかし、そうした最適化は承認される前に常に人間の目で確認すべきです。時には微妙だが悪い色の変化が生じることがあるからです。
圧縮最適化のまとめ
圧縮最適化を使って達成された最終ファイルサイズの完全なまとめです。
ご覧のように、非常に複雑な LZW 最適化を、組み込みの透明度の最適化よりも使っても、最終的なアニメーションサイズにわずかな改善しか得られませんでした。しかし、結果は利用可能な多くの GIF 最適化アプリケーションプログラムと、最適化される特定のアニメーションとの間でも非常に変動します。本当にファイルサイズの最後の 1 バイトまで絞り出す必要があるなら、LZW 最適化こそまさに必要なものかもしれません。そして、本当に最良の結果が必要なら、いくつもの異なるプログラム(そしてヒューリスティック実装)を試して、特定のアニメーションをどれがより良く圧縮するか、また他にどんな最適化機能を提供するかを確かめるべきです。たいていは透明度の最適化でほとんどの用途には十分です。LZW 最適化はわずかに良い結果を生むだけで、ディスクの保存サイズというより、ネットワーク送信サイズの非常にわずかな節約になります。後者はより大きな「チャンク」や「ブロック」の保存単位を使うからです。このため、LZW 最適化は過剰だと感じますし、その労力にも、お金にも見合わないと思います(これらのツールのほとんどは商用で販売されています)。 | _残念ながら、これらの GIF オプティマイザは、あらゆる種類の事前最適化されたアニメーションをうまく扱えるわけではないことがわかりました。
例えば、私のテストでは「[Gifsicle](http://www.lcdf.org/gifsicle/)」は、すでに「背景破棄」手法を使って最適化されたアニメーションをうまく扱えませんでした。
一方で「[InterGIF](http://www.chaos.org.uk/~pdh/software/intergif.htm)」は、すでに初期キャンバスと「直前破棄」手法を使うよう最適化されたアニメーションを扱えないことがわかりました。これはまた透明度の最適化の使用に限られており、それは IM が今や提供しているものです。
よって、ある GIF 最適化ユーティリティの出力を別のものに渡して、GIF 最適化ユーティリティを混ぜて使わないことをお勧めします。少なくとも、まずアニメーションを合体して以前のフレーム最適化を取り除かない限りは。
IM、Gifsicle、InterGIF はすべて、自身の最適化を取り除くそうした合体オプションを提供します。とはいえ、非 IM アプリケーションがすべてのアニメーションを正しく合体すると保証はできません。IM はできます。
_
---|---
これらのプログラムを IM の高度なフレーム最適化手法(異なる GIF 破棄手法を自動で選択し切り替える)と確実に併用できないため、IM がこれら LZW 圧縮オプティマイザを単に使うよりも、全体としてより良い結果を生むことがしばしばあるとわかりました。また、結果を後でもう一度合体 (Coalesce)し、そのフレームを元の未最適化アニメーションと比較して、非 IM プログラムがアニメーションを完全に台無しにしていないことを確認することもお勧めします(上記の注を参照)。本当に、私はそれが起きるのを見たことがあり、スクリプトはアニメーションが有効なままであることを二重チェックすべきです。
この種の最適化についての(Windows ツールを使った)別のチュートリアルは WebReference Frame Optimation です。なお、このサイトは圧縮最適化についてなので、名前が誤っています。
細かな最適化
GIF アニメーションで使えるその他の最適化手法がいくつかあり、それらはしばしばあまりに当たり前すぎて見落とされます。
- GIF コメントを取り除く。
多くの GIF アニメーションには大きなテキストコメントが付いています。これらはしばしば、宣伝の一種としてグラフィックエディタによって自動的に加えられたものです。例えば「Gimp」はデフォルトで「Created with The GIMP」を画像に加えます。コメントが不要なら、それは容量の無駄です。GIF が保存される前に、IM の「magick」コマンドに「[+set](https://imagemagick.org/command-line-options/#set) comment」演算子を加えて取り除いてください。ただし、コメントが著作権表示の場合は、法的理由から取り除くのは良い考えでないかもしれないことに注意してください。 - 色数を減らす。
アニメーションがより少ない色数で問題なく見えるなら、より小さなカラーテーブルを使ってください。カラーテーブルは常に 2 の累乗なので、32 色未満を使えるなら、それは 256 色を使うよりずっと小さくなります。これは特に重要です。カラーテーブルは GIF 画像データに使われる LZW 圧縮で圧縮されないからです。また、より少ない色を使うと、より一般的なピクセル列が見つかるため、一般により良い LZW 圧縮を生みます。ただし、これは常にそうとは限りません。(減色による)色のディザリングが圧縮を悪化させることもあるからです。ディザリングを切るか秩序ディザを使うことが、ここで重要になりえます。 - ユーザーに見えるフレーム数を半分にする。
より滑らかでないアニメーションで構わないなら、フレームの総数を半分にすると、最終ファイルサイズの良い改善を生めます。もちろん、ファイルが半分のサイズになるわけではなく、アニメーションの品質は下がります。しかし、非常に大きなファイルサイズ削減を生めます。 - アニメーションを切り抜く/リサイズする。
画像サイズが小さいほど、ファイルサイズも小さくなります。よって、大きなアニメーションが必要ないなら、大きなアニメーションを使わないでください。より大きなアニメーションや動画を表す小さなサムネイルは、一覧では本物よりも好ましいことがよくあります。 - 代替の圧縮。
アニメーションをアニメーションとして使う予定がなく、つまり単に保存したいだけなら、LZW 圧縮を切って、ファイル全体を「gzip」または「bzip2」で圧縮して保存してください! 結果はずっと小さくなりますが、クライアントのブラウザで直接使えるようにするには、ウェブサーバーがブラウザに正しい「コンテンツ」と「圧縮」のヒントを与える必要があります。「Apache」ウェブサーバーはデフォルトではこれを行いませんが、行うよう設定できます。さらに良いのは、より良い保存圧縮のために、非圧縮アニメーションのディレクトリ全体を 1 つのファイルにアーカイブすることです。
他に最適化のアイデアがあれば、ぜひお知らせください。
GIF 最適化に関するその他の情報源
以上で、アニメーションを扱うためのさまざまな基本的な方法と手法が完了しました。しかし全体像を完成させるには、実際の画像のアニメーションに伴う問題を扱う手法を詳述した、次の IM 使用例のページへ進むべきです。また、上記の手法の多くは、動画から GIF への最適化の実践的な例で実演されています。GIF アニメーションの扱いに本当に真剣なら、減色についても徹底的に読むことをお勧めします。減色はしばしば良い GIF アニメーションの扱いの鍵だからです。WWW で見つけた、GIF アニメーション最適化手法に関する他の有用な情報源には、次のものがあります…
ここに掲載すべきページがあると思ったら、メールしてください。有用な内容のページのみ追加するので、あなたのリンクを追加する保証はありません。
![[IM Output]](../static/img/anim_opt/paddleball_frames.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_oplus_cframes.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_oplus_rmdups_frames.gif)
![[IM Output]](../static/img/anim_opt/moving_hole_split_rmzero_frames.gif)
![[IM Text]](../static/img/anim_opt/moving_hole_sizes.txt.gif)
![[IM Output]](../static/img/anim_opt/teleport_frames.png)
![[IM Output]](../static/img/anim_opt/teleport_thres.gif)
![[IM Output]](../static/img/anim_opt/teleport_thres_frames.gif)
![[IM Output]](../static/img/anim_opt/teleport_thres90.gif)
![[IM Output]](../static/img/anim_opt/teleport_thres90_frames.gif)
![[IM Output]](../static/img/anim_opt/teleport_bgnd.gif)
![[IM Output]](../static/img/anim_opt/teleport_bgnd_frames.gif)
![[IM Output]](../static/img/anim_opt/teleport_od.gif)
![[IM Output]](../static/img/anim_opt/teleport_od_frames.gif)
![[IM Output]](../static/img/anim_opt/teleport_htone.gif)
![[IM Output]](../static/img/anim_opt/teleport_htone_frames.gif)
![[IM Output]](../static/img/anim_opt/teleport_lines.gif)
![[IM Output]](../static/img/anim_opt/teleport_lines_frames.gif)
![[IM Text]](../static/img/anim_opt/speed_nframes.txt.gif)
![[IM Text]](../static/img/anim_opt/speed_ncf.txt.gif)
![[IM Text]](../static/img/anim_opt/speed_ncolors.txt.gif)
![[IM Text]](../static/img/anim_opt/speed_ncolors2.txt.gif)
![[IM Output]](../static/img/anim_opt/speed_opt2_frames.gif)
![[IM Output]](../static/img/anim_opt/speed_opt3_frames.gif)
![[IM Output]](../static/img/anim_opt/speed_opt4_frames.gif)
![[IM Output]](../static/img/anim_basics/bunny_bgnd_frames.gif)
![[IM Output]](../static/img/anim_basics/bunny_bgnd.gif)
![[IM Output]](../static/img/anim_opt/bunny_bgnd_lzw_gifsicle.gif)
![[IM Output]](../static/img/anim_opt/bunny_bgnd_lzw_frames.gif)
![[IM Output]](../static/img/anim_opt/bunny_bgnd_giflossy.gif)
![[IM Output]](../static/img/anim_opt/bunny_bgnd_lossy_frames.gif)
![[IM Output]](../static/img/anim_opt/bunny_bgnd_diff_frames.gif)
![[IM Text]](../static/img/anim_opt/bunny_bgnd_compress_sizes.txt.gif)