Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ImageMagick で GIF 処理

ImageMagick で GIF を扱う話です。

GIF について

古い画像フォーマットですが。GIF アニメーションとして今でもよく使われます。
img タグを使ってブラウザでインライン表示出来るのが強いですね。
GIF の特徴としてアニメーションが出来る、色数が最大256色。透明色を使えますが半透明には出来ない。といった点があげられます。

減色

256色しか使えないので、色を沢山使った画像を GIF に変換すると画像の劣化が目立ちます。

% convert rgbcube.png rgbcube.gif
rgbcube.png rgbcube.gif
26040.png 26040.gif
26040-x800-div8-24.png ← 拡大 26040-x800-div8-24.gif ← 拡大

この例は極端に悪いケースです。小さい画像だったり特定の色に偏っている場合だと、それほど問題になりません。

透明色

透明色を指定できます。ただし、生贄に1色を透明に捧げる必要があります。
以下のコマンドは黄緑(#00d342)を透明色にするコマンドです。

% convert Opaopa.png -transparent "#00d342" Opaopa-transparent.gif
Opaopa.png Opaopa-transparent.gif
Opaopa.png Opaopa-transparent.gif

この画像では #00d342 色のピクセルを追加すると、そこも透明になります。
つまり、透明色を使うと 255色しか使えなくなります。まぁ256が255に減っても。。という感じです。

GIF インターレース

ファイルの先頭から

  • 8行ごとに1行 > 4行ごとに1行 > 2行ごとに1行 > 残り全部

の順番にピクセルを並べる事で、ネットワークが遅い場合でもはじめに全体像を表示してから、データが取得しながら少しずつ細部を表示できます。
Opaopa-interlace.gif

ピクセルの埋め方と x8拡大 実際の表示と x8拡大
Opaopa-dot1-interlace-1.png Opaopa-dot8-interlace-1.png Opaopa-dot1-interlace-1-cmpl.png Opaopa-dot8-interlace-1-cmpl.png
Opaopa-dot1-interlace-2.png Opaopa-dot8-interlace-2.png Opaopa-dot1-interlace-2-cmpl.png Opaopa-dot8-interlace-2-cmpl.png
Opaopa-dot1-interlace-3.png Opaopa-dot8-interlace-3.png Opaopa-dot1-interlace-3-cmpl.png Opaopa-dot8-interlace-3-cmpl.png
Opaopa-dot1.png Opaopa-dot8.png

黒ピクセルと欠けの区別が出来なくて済みません。。(後で作り直すかも。)

ページオフセット指定

GIF は描画領域としての Screen と、実際に描画する Image のオフセット情報を別に持ちます。

% identify Opaopa-posi.gif
Opaopa-posi.gif GIF 120x72 120x72+50+30 8-bit sRGB 16c 999B 0.000u 0:00.000

GIF の右にある 120x72 が画像サイズで、120x72+50+30 がスクリーン情報です。

Opaopa-posi.gif
Opaopa-posi.gif

左上に +50+30 の隙間が出来るはずです。(ブラウザによって微妙に表示が異なります。)

GIF アニメーション

画像を複数指定すると、それらをコマとした1つのアニメーション画像になります。

Opaopa-anime-dot1-0.png Opaopa-anime-dot1-1.png Opaopa-anime-dot1-2.png Opaopa-anime-dot1-3.png Opaopa-anime-dot1-4.png Opaopa-anime-dot1-5.png Opaopa-anime-dot1-6.png Opaopa-anime-dot1-7.png

% convert Opaopa-anime-dot1-[0-7].png Opaopa-anime-dot1.gif

Opaopa-anime-dot1.gif

それの拡大バージョン

Opaopa-anime-dot8.gif

コマ間隔指定

-delay オプションでコマ間の時間を指定できます。アニメーションの速さになります。
1/100 単位なので、例えば -delay 100 を指定すると 1 frame/sec です。

% convert -delay 100  Opaopa-anime-dot8.gif Opaopa-anime-dot8-delay100.gif
% convert -delay  25  Opaopa-anime-dot8.gif Opaopa-anime-dot8-delay25.gif

尚、 -delay オプションは入力画像ファイル名の前で指定します。後ろだと無視される事に注意してください。なお、コマ毎に指定も出来ます。

-delay 100 (毎秒) -delay 25 (秒間4コマ)
Opaopa-anime-dot8-delay100.gif Opaopa-anime-dot8-delay25.gif

1/100 単位で指定できるなら -delay 1 で 100fps だーー! 等とやりたくなりますが、ブラウザ表示はそこまで頑張ってくれません。せいぜい数十msec までです。

ループ回数

ループ回数を指定する事できます。
例えば、ループ回数に 1 を指定すると、ループしない GIF アニメが作れます。

% convert -delay 50 -loop 1 Opaopa-anime-dot8.gif Opaopa-anime-dot8-loop1.gif

Opaopa-anime-dot8-loop1.gif

おそらく、この記事をここまで読んだ時には画像は止まっていると思うので、画像を別タブとかで開いてリロードして確認してください。^^;

色パレットの Global と Local

GIF の色パレットは全コマ共通の Global カラーテーブル、コマ画像ごとの Local カラーテーブルのどっちでも持つ事ができます。

大昔のガラケー端末では Local カラーテーブルを無視したり、正しくない解釈をする事があり、+map オプションで Global カラーテーブルに一本化する事は、よく行われました。

% convert localcolormap.gif +map globalcolormap.gif

昔話と片付けても良いですが、Global に一本化するとカラーテーブルのデータ量が減るだけでなく、色数も微妙に減らされて結果として圧縮が効きサイズが減る可能性があり、画質に問題がない範囲でカラーテーブル(パレット)のGlobal化を行う事にある程度の理があります。

ImageMagick は内部的に Global カラーテーブルの仕組みはないので、Global か Local かを調べる事はできません。giftext というツールに頼ると良いです。

% giftext Opaopa-anime-dot8.gif | grep "Color Map"
  Has Global Color Map.
  No Image Color Map.
  No Image Color Map.
  No Image Color Map.
  No Image Color Map.
  No Image Color Map.
  No Image Color Map.
  No Image Color Map.
  No Image Color Map.

上のような表記であれば、Global に一本化されています。

尚、公式サイトでは giftrans で説明しています。

giftrans -L speed.gif 2>&1 | grep -c "Local Color Table:"

GIF アニメーションの最適化

さて、このエントリのメインイベントです。
GIF アニメーションは静止画を複数もつだけでなく、動画としての簡単な最適化の機能があります。

まずは最適化手法を2種類紹介して、最後に、それを両方適用する方法も紹介します。

OptimizeFrame フレーム最適化

変化のあるピクセルを全部囲う四角(いわゆる Dirty Rectangle)でクロップした画像を持つ事で、GIF のデータサイズを減らせます。

% convert  Opaopa-anime-dot1.gif  -layers OptimizeFrame Opaopa-anime-dot1-optframe.gif
% identify -format "size=%wx%h geom:%g\n" Opaopa-anime-dot1-optframe.gif
size=41x18 geom:41x18+0+0
size=15x5 geom:41x18+4+7
size=14x7 geom:41x18+3+6
size=8x7 geom:41x18+6+6
size=8x7 geom:41x18+6+6
size=5x3 geom:41x18+5+8
size=15x3 geom:41x18+4+8
size=16x5 geom:41x18+3+7

Opaopa-anime-dot1-optframe.gif
↓ 拡大
Opaopa-anime-dot8-optframe.gif

これだと分からないので、コマを分解します。

% convert Opaopa-anime-dot1-opttrans.gif Opaopa-anime-dot1-opttrans-%d.png
元画像 フレーム最適化
Opaopa-anime-dot8-0.png Opaopa-anime-dot8-optframe-0.gif
Opaopa-anime-dot8-1.png Opaopa-anime-dot8-optframe-1.gif
Opaopa-anime-dot8-2.png Opaopa-anime-dot8-optframe-2.gif
Opaopa-anime-dot8-3.png Opaopa-anime-dot8-optframe-3.gif
Opaopa-anime-dot8-4.png Opaopa-anime-dot8-optframe-4.gif

<以下 5-7 は省略>

OptimizeTransparency

GIF には dispose メソッドという指定があり、透明ピクセルの場合に前のコマの色をそのまま使う事ができます。

つまり、こんな感じ。

元画像 透明最適化
Opaopa-anime-dot8-0.png Opaopa-anime-dot8-opttrans-0.png
Opaopa-anime-dot8-1.png Opaopa-anime-dot8-opttrans-1.png
Opaopa-anime-dot8-2.png Opaopa-anime-dot8-opttrans-2.png
Opaopa-anime-dot8-3.png Opaopa-anime-dot8-opttrans-3.png
Opaopa-anime-dot8-4.png Opaopa-anime-dot8-opttrans-4.png

ただし、コマの大きさ自体は変わっていないので、フレーム最適化ほどはサイズは減らないと思われます。
また、GIF 画像に元々透明ピクセルが含まれている場合は避けた方が良いです。透明ピクセルと「前のコマと同じ色」が区別出来ないので、「非透明 > 透明」の変換をするピクセルが、非透明のままになるでしょう。

(最強の) Optimize

Optimize Frame と Optimize Transparency が合わさり最強となった Optimize オプションがこちらです。

% convert Opaopa-anime-dot1.gif -layers Optimize Opaopa-anime-dot1-optimize.gif
元画像 透明最適化
Opaopa-anime-dot8-0.png Opaopa-anime-dot8-optimize-0.gif
Opaopa-anime-dot8-1.png Opaopa-anime-dot8-optimize-1.gif
Opaopa-anime-dot8-2.png Opaopa-anime-dot8-optimize-2.gif
Opaopa-anime-dot8-3.png Opaopa-anime-dot8-optimize-3.gif
Opaopa-anime-dot8-4.png Opaopa-anime-dot8-optimize-4.gif

見た目わかりにくいですが、ちゃんと画像を最小限の大きさでクロップし、かつ前のコマと同じ色を透明にしています。

% ls -l Opaopa-anime-dot1.gif Opaopa-anime-dot1-optimize.gif
-rw-r--r--  1 yoya  staff  16414 12 22  2016 Opaopa-anime-dot1-optimize.gif
-rw-r--r--  1 yoya  staff  17457 12 22  2016 Opaopa-anime-dot1.gif

残念ながら、この例だと画像サイズ自体小さいので殆ど減りませんが、もっと大きな画像であれば劇的に減る事が多いです。

% ls -l Opaopa-anime-dot8.gif Opaopa-anime-dot8-optimize.gif
-rw-r--r--  1 yoya  staff  26706 12 22  2016 Opaopa-anime-dot8-optimize.gif
-rw-r--r--  1 yoya  staff  45632 12 22  2016 Opaopa-anime-dot8.gif

-layers Optimize おすすめです。

ただ、透明ピクセルが含まれる GIF は Optimize Transparency と同じ理由で駄目です。諦めて Optimize Frame を使いましょう。

yoya
画像処理の事ばかり考えてます。ImageMagick ウォッチングが趣味です。
http://pwiki.awm.jp/~yoya/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away