はじめに
この記事は Graphical Web Advent Calendar の 16 日目の記事として書かれました。
Graphical Web ということで、PNG フォーマットの簡単な説明と Web ブラウザ上で動作する PNG 解析ツールを作ったので使い方と解析結果の見方について書いていこうと思います。
また、人気のある PNG 画像最適化ツールがどのような最適化を行っているのか調べていきます。
PNG の仕様に入る前に
ここから、PNG の仕様について最低限の説明を書いていきます。
PNG の最適化や検証するときに必要になるので、退屈かもしれませんが軽く目を通してください。
以下の項目について、なんとなく分かれば良いです。
- シグネチャ
- 必須チャンクの役割
- IHDR
- PLTE
- IDAT
- IEND
PNG 仕様概要
PNG フォーマットは、先頭にシグネチャがあり、そこからチャンクと呼ばれるデータが連続する形で構成されています。
シグネチャ
シグネチャは PNG ファイルであることを確認するために使用されます。
シグネチャは必ず以下の8バイトとなっています。
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a
PNG ファイルをテキストエディタなどで開いたときに先頭のあたりに「PNG」という文字を見かけた事はないでしょうか?
それはこのシグネチャを ASCII にしたときの表示の一部です。
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 01 00 00 00 01 00 08 06 00 00 00 5c 72 a8 |.............\r.|
00000020 66 00 00 00 19 74 45 58 74 53 6f 66 74 77 61 72 |f....tEXtSoftwar|
00000030 65 00 41 64 6f 62 65 20 49 6d 61 67 65 52 65 61 |e.Adobe ImageRea|
...
チャンク
チャンクはデータを格納する形式で、PNG ではシグネチャをのぞいて全てのデータがこの形式で記述されています。
チャンクは以下のような形になっています。
データの大きさ | チャンクタイプ | データ | 破損チェック |
---|---|---|---|
4 Byte | 4 Byte | 可変 | 4 Byte |
チャンクの大きさは計算可能なので、対応していないチャンクタイプでも読み飛ばすことで表示に影響を与えないようにする事ができます。
- データの大きさ
- 文字通り、データの大きさが整数で入っている
- つまり、チャンクの大きさは 12 + この数値となる
- チャンクタイプ
- ASCII で 4 文字はいっている。先頭が大文字なら必須チャンク
- データ
- チャンクタイプごとに決められた形式ではいっているデータ
- 破損チェック
- CRC-32 という形式でチャンクタイプ+データが破損していないか確認するためのデータ
必須チャンク
PNG では画像として最低限必要な情報を必須チャンクと呼び、以下のものが必須チャンクとされています。
- IHDR
- PLTE (ただし、画像タイプが Indexed Colour のときのみ必須)
- IDAT
- IEND
PNG はこの4つのチャンクさえサポートしていれば大体なんとかなるので、今回は必須チャンクに絞って説明します。
IHDR チャンク
IHDR チャンクは画像の形式に関する情報を扱います。
横幅 | 縦幅 | ビット深度 | カラータイプ | 圧縮方法 | フィルタ方法 | インタレース方法 |
---|---|---|---|---|---|---|
4 Byte | 4 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte | 1 Byte |
- 横幅
- 画像の横幅
- 縦幅
- 画像の縦幅
- ビット深度
- 色情報につかうビット数を指定します
- RGB の時、ビット深度が 8 だったら R8G8B8 で 24 bit になります
- グレイスケールの時、ビット深度が 8 だったら 8 bit になります
- 1, 2, 4, 8, 16 が有効ですが、カラータイプによって選択できない組み合わせもあります
- カラータイプ
- 0: Grayscale (グレイスケール)
- 2: True Colour (RGB形式)
- 3: Indexed Colour (パレット形式、カラーマップ形式)
- 4: Grayscale with Alpha (アルファチャンネル付きグレイスケール)
- 6: True Colour with Alpha (RGBA形式)
- 圧縮方法
- 0: Deflate
- フィルタ方法
- 0: Basic
- インタレース方法
- 0: None
- 1: Adam7
PLTE チャンク
カラータイプが Indexed Colour のときだけ必須となり、パレットの情報を扱います。
それ以外のカラータイプでは優先して表示する色を表すチャンクとなります。
データ形式は以下のように RGB でそれぞれ 1 Byte ずつのものが最大 256 個まで連続しています。
いくつ連続しているかはチャンクのデータサイズを 3 で割って算出します。
赤 | 緑 | 青 |
---|---|---|
1 Byte | 1 Byte | 1 Byte |
また、 Indexed Colour 形式でアルファチャンネルが必要なときは、この後にアルファチャンネルのみの tRNS チャンクが追加されることがあります。
IDAT チャンク
画像本体です。IDAT チャンクでは ZLIB という形式で圧縮された画像の色情報がはいっています。
画像の色情報は、行ごとに分かれていて以下のようになっています。
フィルタ | 色情報 | 色情報 | 色情報 | ... |
---|---|---|---|---|
1 Byte | 可変 | 可変 | 可変 | ... |
色情報はビット深度と Colour Type によって変わります。
- Grayscale: (ビット深度) bit
- Indexed Colour: (ビット深度) bit
- Truecolor: (ビット深度 * 3) bit
- Truecolor with alpha: (ビット深度 * 4) bit
- Grayscale with alpha: (ビット深度 * 2) bit
フィルタについては以下のようになっています。
- 0: None (そのまま)
- 1: Sub (左の Byte との差)
- 2: Up (上の Byte との差)
- 3: Average (左と上のバイトの平均との差)
- 4: Paeth (Paethアルゴリズムによる値(詳細は仕様を見てください))
フィルタというとぼかしたりなどのエフェクトを思い浮かべる方もいるかもしれませんが、PNG でいうフィルタとはデータを偏った形で表現するための仕組みです。
グラデーションが多用される画像などでは、隣接する色との差分にした方が圧縮しやすくなるためです。
フィルタに関して注意する点が一つあり、一部の解説では「隣の色」とされていることがありますが「隣の色を含むバイト」との比較です。例えば、RGBA それぞれ 8bit ずつだとすると、隣の対応するバイトとの距離は 4 バイトになります。以前は「隣のバイト」と書いてしまっていたので修正しました。(2013/06/28追記)
また、IDAT チャンクはデータ部分を分割して複数の IDAT チャンクにすることができます。その際、IDAT は必ず連続していなければいけません。
IEND チャンク
画像の終わりを示す空チャンク。データ部分にはなにも入りません。
PNG ファイルの解析
ここまで、PNG の仕様について説明してきました。
いきなり、延々と仕様の話しをされてうんざりした人もいるかもしれません。
しかし、多くの Web 開発関係者は画像サイズを小さくしようとしていますが、そのために PNG とはどういうものか把握しておくのはとても重要な事です。
残念な事に Web 上の情報を見ていると、PNG がどういうものか理解せずにサイズを小さくすると謳って、結果誤った事を書いたものが多く見られます。
ここからは、最近人気のある最適化ツールが一体どのような最適化を行っているのか検証していきます。
最適化については、別のブログで簡単に書いたのでそちらも参考になるかもしれません。
- PNG軽量化の減色と圧縮について | GREE Engineers' Blog :
http://labs.gree.jp/blog/2012/11/5373/
png Identify
では、解析をどのようにするかですが、ちょうど良いツールが見当たらなかったので JavaScript で自作しました。
これは他の PNG の情報ツールでは見られないような圧縮関連の情報も表示するツールとなっていて、圧縮効率の調査や最適化アルゴリズムの調査に活用する事が出来ます。
調査するツール
以下の最近人気のある最適化ツールなどについて調べていきたいと思います。
- PNGGauntlet (Windows)
- ImageOptim (Mac OS X)
- AzConvPNG (Windows)
最適化対象の画像
ライセンスがはっきりしていて、PNG に向いているイラスト調の画像なので「うぶんちゅ!」という漫画の表紙絵を例に使わせていただきます。
- 架空線 – AERIAL LINE - : 第1話 「うぶんちゅがやって来た!」:
http://www.aerialline.com/comics/ubunchu/episode01 - © 瀬尾浩史
サイズ比較
まずは、一番気になるサイズ比較から行っていきましょう。
元画像 | PNGGauntlet | ImageOptim | AzConvPNG | |
---|---|---|---|---|
サイズ | 1,698,506 | 1,629,668 | 1,631,506 | 1,702,386 |
差分 | - | -68,838 | -67,000 | +3,880 |
Indexed Colour 形式での挙動も調べるため、ImageAlpha で 256 色に減色した画像も比較します。
元画像 | PNGGauntlet | ImageOptim | AzConvPNG | |
---|---|---|---|---|
サイズ | 520,806 | 494,704 | 493,989 | 519,658 |
差分 | - | -26,102 | -26,817 | -1,148 |
Truecolor 形式では PNGGauntlet, Indexed Colour では ImageOptim が優れた結果となりました。
では、ここから各ツールでどのような最適化が行われたか見ていきます。
チャンク構成
Truecolor
元の画像では以下のチャンクが付与されていますが、各ツールでは除去されています。
- pHYs (Physical pixel dimensions)
- iCCP (Embedded ICC profile)
- cHRM (Primary chromaticities and white point)
元画像のチャンク構成
ImageOptim で最適化した後のチャンク構成
これらは、表示する際に色や大きさを決定する際に使われることもありますが、ブラウザによっては無視される事もあるため削除しても特に問題にならない事が多いです。
(これらのチャンクに対応したブラウザで、きっちりと表示色などを制御したい場合は削除してはいけません。)
Indexed Colour
元画像を ImageAlpha で減色した後のチャンク構成
上記のものを PNGGauntlet で最適化した後のチャンク構成
ImageAlpha で減色した画像では gAMA (Image gamma) という表示に関するチャンクが付与されていますが、これも無視されることがあるので削除してもあまり影響しません。
それよりも特徴的なのは、IDAT が細かく分割されている点です。これはファイルサイズが増えるだけなので最適化ツールにかけると一つにまとめられています。
フィルタ最適化
PNG の特徴であるデータを偏らせる行毎のフィルタですが、どういう状況でどのフィルタを選択するのがベストかというのは実際にやってみるしかありません。
また、仕様にも「Indexed Colour の時はフィルタを使わない方が良い」と書いてあるように、最適化ツールでも Indexed Colour の時はすべて None になっています。(ので省略します)
元画像
PNGGauntlet
ImageOptim
AzConvPNG
PNGGauntlet と ImageOptim のフィルタの並びが同じになりましたが、これは共に PNGOUT, OptiPNG を使用しているので、同じ結果になったのだと思います。AzConvPNG はフィルタの最適化は行っていないようです。
パレット最適化
ImageAlpha で減色した画像は (Indexed Colour なので当然ですが) パレットをもっており、このパレットの並び順によっても ZLIB の圧縮率に影響する事があります。
記事が縦長になってきたので画像は省略して結論だけ以下にまとめます。
- AzConvPNG: 独自の並び替え(元のパレットと全然違う)
- PNGGauntlet: 元のパレットと似ているけど多少異なる
- ImageOptim: 並び替えを行っていない
ZLIB の圧縮率
ここからが本題です。
フィルタやパレットの最適化を行っても、それだけではファイルサイズは変わりません。
しかし、フィルタやパレット最適化によって「圧縮しやすい状態」になった画像は最終的なファイルサイズで大きな差がでます。
実際に見てみます。
Truecolor
元画像
PNGGauntlet
ImageOptim
AzConvPNG
Truecolor では PNGGauntlet と ImageOptim が優秀な結果を出しています。ファイルによって優劣が入れ替わる事も多々あるので、この二つはどちらもほぼ互角といって良い性能ではないでしょうか。
それぞれの各ブロックの圧縮をみると、LZSSの平均長(LZSS-Avg) が似た傾向にあり、どちらもかなりの高圧縮となっています。
Indexed Colour
元画像
PNGGauntlet (すいません、ブロック数が多すぎたので省略してます)
ImageOptim
AzConvPNG
こちらも PNGGauntlet と ImageOptim がほぼ互角です。しかし、パレットの入れ替えを行っていない ImageOptim の方が最終的に小さくなっているため、ImageOptim はこの結果を見越して、あえて入れ替えなかったのかもしれません。フィルタとパレットの選択によっても結果が変わるため、このあたりのアルゴリズムが当たるかどうかで結果が変わるのかもしれません。
また、全体的に各ツールでの圧縮の特徴ですが、元画像や AzConvPNG ではブロックごとの圧縮後のサイズが大体同じサイズになっているのに対し、ImageOptim と PNGGauntlet は大きくばらついています。 ImageOptim と PNGGauntlet の特徴が似ている理由は推測になりますが後述します。
まとめ
最終的な ZLIB の再圧縮では ImageOptim と PNGGauntlet が優秀な結果となりました。
AzConvPNG がこの二つと比べてあまり結果が出せなかったのは、ZLIB 最適化のアルゴリズムにあるとおもいます。
これは推測ですが、ImageOptim と PNGGauntlet はどちらも PNGOUT というツールを使っているのですが、そこで採用されている ZLIB 部分の最適化を担う Kflate (と一部で呼ばれている最適化アルゴリズム)の影響があると思います。
(AzConvPNG は zlib ver 1.2.5 の Deflate パラメータの中で最もサイズの小さくなるものを選択している模様。 zlib のパラメータはバッファのサイズなどを一律で扱うために、圧縮後のブロックサイズがほぼ平坦になるものだと思われます。)
今回は一つのファイルのみを対象としましたが、対象となるファイルによって優劣が変わる事もあるので、どのツールで最適化するか悩んだ場合は自分で解析してみて比べるのも一つの手段です。
また、PNGGauntlet と ImageOptim どちらを選択したほうが良いかについては、わずかでも小さくしたい場合は両方使用して優れている方を採用、そうでない場合は使用しているOSで選択すれば良いのではないでしょうか。
以上、長くなりましたが PNG の仕様と最適化の挙動について書いてきました。
何かの参考になれば幸いです。
自分は「PNG」よりも「臼NG」の方をよく見かけます。