サーバーのストレージ節約のため、保存されているPNG画像の圧縮をしたいと思い手法を調べてみたところ、OptiPNGを使った圧縮と、Zopfliを使った圧縮のどちらかが良さそうな感じでした。Zopfliは、PHPから使えるphp-ext-zopfliというライブラリがあるので、そちらを。
とりあえずどっちもサーバーにインストールしてみました。
インストール
■OptiPNGのインストール
yum install optipng
■php-ext-zopfliのインストール
git clone https://github.com/cubicdaiya/php-ext-zopfli.git git clone https://code.google.com/p/zopfli/ cp -r /[user]/zopfli/src/zopfli/ /[user]/php-ext-zopfli/ phpize ./configure make make install
更にモジュールを読み込むため、php.d/にzopfli.iniというファイルを作成し、下記を書き込み保存します。
extension=zopfli.so
OptiPNGはyumにリポジトリがあるのでそのままインストール出来ます。この時点ではVer0.6.4がインストールされました。
php-ext-zopfliは、まずZopfli本体も必要なのでそちらもダウンロードし、php-ext-zopfliを展開した中のディレクトリにあるzopfliディレクトリに、zopfli/src/zopfli/の中身をコピーします。これをしないとmake installでエラーになります。
使ってみる
■OptiPNGを使う
OptiPNGはこんな感じのコマンドを入力すると、色々自動で最適化してくれます。
optipng sample.png
上記のコマンドで下記のようなログが出て最適化されます。
OptiPNG 0.6.4: Advanced PNG optimizer. Copyright (C) 2001-2010 Cosmin Truta. ** Processing: sample_opti.png 74x74 pixels, 4x8 bits/pixel, RGB+alpha Input IDAT size = 5159 bytes Input file size = 5216 bytes Trying: zc = 9 zm = 8 zs = 0 f = 0 IDAT size = 4227 zc = 9 zm = 8 zs = 1 f = 0 IDAT size = 4223 zc = 1 zm = 8 zs = 2 f = 0 zc = 9 zm = 8 zs = 3 f = 0 zc = 9 zm = 8 zs = 0 f = 5 zc = 9 zm = 8 zs = 1 f = 5 zc = 1 zm = 8 zs = 2 f = 5 zc = 9 zm = 8 zs = 3 f = 5 Selecting parameters: zc = 9 zm = 8 zs = 1 f = 0 IDAT size = 4223 Output IDAT size = 4223 bytes (936 bytes decrease) Output file size = 4280 bytes (936 bytes = 17.94% decrease)
ファイルサイズが17.94%低下しました。
左が元ファイルで、右が最適化後のファイルです。見た目ほとんど変わっていません。
なお、オプションで圧縮処理の仕方について色々指定も出来るので、用途に合わせてオプションをつけるといいです。
■Zopfliを使う
無事にインストールできていれば、zopfli_png_recompressというIDATチャンクを圧縮してくれる関数が追加されていますので、下記のように使うとPNGファイルが圧縮されます。
$origin_file = dirname(__FILE__) . '/sample.png'; $recompress_file = dirname(__FILE__) . '/sample-recomp.png'; $data = file_get_contents($origin_file); $recompress = zopfli_png_recompress($data); file_put_contents($recompress_file, $recompress);
同じファイルを使っての結果は下記のようになりました。
*** Testing zopfli_png_recompress() *** ORIGIN: 5216 RECOMPRESS: 4920
比較
先ほどのサンプルのような、非常に単純な小さい画像だとOptiPNGの方が良いようですが、イラストのような画像の場合は以下のようになりました。
*** Testing zopfli_png_recompress() *** ORIGIN: 19653 RECOMPRESS: 18092 OPTIPNG: 18868
左がオリジナルファイル、中央がZopfli(8.0%圧縮)、右がOptiPNG(4.0%圧縮)です。
OptiPNGの方もオプションの値次第で色々と変わってくると思いますが、色数を減らさずあまり見た目変わらない程度の圧縮方法では、特徴としてはOptiPNGが単調な画像向き、Zopfliが複雑な画像向きかな、と思いました。
実装について
以上のような結果から、内容によって使い分けるのが良いだろうということで、iconDecotterでは、フレーム画像のサムネイルにはOptiPNG圧縮を、ユーザーのbeforeアイコン画像についてはZopfli圧縮をかけるようにしました。投稿されたフレーム素材画像については、とりあえずは未圧縮としています。
参考
PNG画像を最適化するツール「OptiPNG」: 小粋空間
php-ext-zopfliでPNG画像を再圧縮 – pixiv engineering blog
追記
そもそもアルゴリズムが違うんだから両方かければいいよ、というツッコミをいただき。
*** Testing zopfli_png_recompress() *** ORIGIN: 19653 RECOMPRESS: 18092 OPTIPNG: 18868 RECOMPRESS - OPTIPNG: 18068 OPTIPNG - RECOMPRESS: 18068
どっちを先に処理しても同じかな?
あんまりたくさん走らない処理なら、両方掛けてしまうのが良いみたいでした。
コメント
[…] ※なおそれのせいか、Zopfli圧縮処理をかけるとタイムアウトエラーが出るように なったっぽいので、今は一旦Zopfli圧縮は経由しないようにしてます…。 […]