Create a gist now

Instantly share code, notes, and snippets.

@shyouhei /chart.png
Last active Sep 29, 2017

What would you like to do?

ギガが減るチャレンジ

比較対象ファイルとしてこちらの http://orangestar.hatenadiary.jp/entry/2017/06/18/163900 「231kb:jpg高画質(10)h812」を利用する。

% openssl sha1 20170618160637.jpg
SHA1(20170618160637.jpg)= 3b42635ecd046473c89bde8021efa810a5f6ade8
zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg

231KiBがスタートだ。

ちなみに余談となるが、このファイルは枠線が絶妙にナナメっておりファイルサイズ増加の理由の一端になっていると思う。

上記ファイル(部分、600%に拡大) ©2017 小島アジコ

今回はオリジナルをリスペクトし、そういうファイルのコンテンツの部分に関しては手を入れないことにする。

png

まずそのままpngに変換する。pngは可逆圧縮(以下lossless)なのでデータは欠損しない。

zsh % convert --version
Version: ImageMagick 7.0.5-10 Q16 x86_64 2017-06-04 http://www.imagemagick.org
Copyright: © 1999-2017 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules
Delegates (built-in): bzlib freetype jng jpeg ltdl lzma png tiff xml zlib
zsh % convert 20170618160637.jpg 20170618160637.png
zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg
-rw-r--r-- 1 shyouhei wheel 189K  6 19 13:07 20170618160637.png

これだけで189KiBまで減る。pngの圧縮は優秀だ。

png + zopfli

しかし上記はpngの真の実力ではない。google/zopfli という圧縮ツールのソースコードをきわめてよく観察すると、実はzopfling(1)という隠しコマンドが潜んでおり。これが実は最強のpng圧縮ツールだったりする(ただしクソ重い)。

zsh % ~/bin/zopflipng --filters=01234mepb --lossy_8bit --lossy_transparent 20170618160637.png 20170618160637.zopfli.png
Optimizing 20170618160637.png
Input size: 193469 (188K)
Result size: 156691 (153K). Percentage of original: 80.990%
Result is smaller

zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg
-rw-r--r-- 1 shyouhei wheel 189K  6 19 13:07 20170618160637.png
-rw-r--r-- 1 shyouhei wheel 154K  6 19 14:16 20170618160637.zopfli.png

154KiBまで減った。--lossy_8bitとかいうオプションにギョッとするけど、もとの絵がそもそも8bitなので今回は問題ない。

mozjpeg

Mozillaの優秀な頭脳たちが結集して作成した高圧縮jpegエンコーダー mozilla/mozjpeg

zsh % convert -compress None -type GrayScale -colors 256 -density 500 20170618160637.jpg BMP3:20170618160637.bmp
zsh % cjpeg -grayscale -outfile 20170618160637.cjpeg.jpg 20170618160637.bmp
zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 449K  6 19 13:21 20170618160637.bmp
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg
-rw-r--r-- 1 shyouhei wheel 189K  6 19 13:07 20170618160637.png
-rw-r--r-- 1 shyouhei wheel 154K  6 19 14:16 20170618160637.zopfli.png
-rw-r--r-- 1 shyouhei wheel 114K  6 19 13:21 20170618160637.cjpeg.jpg

なんか手元では一旦bmpを経由しなければいけなかった(理由不明)が、もとの半分まで減った。jpgで戦うなら最強に近いのではないか。ただしこいつは非可逆圧縮 なので、上下にリストアップされている他の圧縮と比べて、ルールが違う というのは指摘しておくべきだろう。実際、元の絵にはなかったモスキートノイズが出る。

左:zopfli(部分、400%に拡大) 右:mozjpeg(部分、400%に拡大) 共に ©2017 小島アジコ

bmp + brotli

上記の過程で意図せずbmpが生成されてしまったためbrotliでいいのでは仮説が急浮上する。google/brotli というのはgoogleの作った圧縮フォーマットで、chromeおよびそれ系の派生ブラウザであればインライン表示できる。

zsh % ~/bin/brotli --best --keep 20170618160637.bmp
zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 449K  6 19 13:21 20170618160637.bmp
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg
-rw-r--r-- 1 shyouhei wheel 189K  6 19 13:07 20170618160637.png
-rw-r--r-- 1 shyouhei wheel 154K  6 19 14:16 20170618160637.zopfli.png
-rw-r--r-- 1 shyouhei wheel 136K  6 19 13:21 20170618160637.bmp.br
-rw-r--r-- 1 shyouhei wheel 114K  6 19 13:21 20170618160637.cjpeg.jpg

136KiB。これはかなり優秀と言っていいと思う。zopflipngよりも減ってる。ただmozjpegには及ばないな(比較がフェアではないが)。

webp

これもgoogle製なのでchromeとか系で表示できるやつだが、webmproject/libwebpというものもある。

zsh % cwebp -noalpha 20170618160637.jpg -o 20170618160637.webp
Saving file '20170618160637.webp'
File:      20170618160637.jpg
Dimension: 563 x 812
Output:    75126 bytes Y-U-V-All-PSNR 39.09 99.00 99.00   40.85 dB
block count:  intra4: 1313
              intra16: 523  (-> 28.49%)
              skipped block: 384 (20.92%)
bytes used:  header:            365  (0.5%)
             mode-partition:   6021  (8.0%)
 Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
    macroblocks:  |       8%|      19%|      46%|      24%|    1836
      quantizer:  |      36 |      30 |      25 |      18 |
   filter level:  |      11 |       6 |       4 |       2 |
zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 449K  6 19 13:21 20170618160637.bmp
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg
-rw-r--r-- 1 shyouhei wheel 189K  6 19 13:07 20170618160637.png
-rw-r--r-- 1 shyouhei wheel 154K  6 19 14:16 20170618160637.zopfli.png
-rw-r--r-- 1 shyouhei wheel 136K  6 19 13:21 20170618160637.bmp.br
-rw-r--r-- 1 shyouhei wheel 114K  6 19 13:21 20170618160637.cjpeg.jpg
-rw-r--r-- 1 shyouhei wheel  74K  6 19 14:29 20170618160637.webp

一気に74KiBまで減る。マジか? と思うが、これは実は罠で、cwebpでは可逆圧縮がデフォルトであった(そういう意味ではmozjpegよりも圧倒的に優秀であるとは言えると思う)。ロスレスにするには-losslessとする必要がある。

zsh % cwebp -noalpha -lossless 20170618160637.jpg -o 20170618160637.lsss0.webp
Saving file '20170618160637.lsss0.webp'
File:      20170618160637.jpg
Dimension: 563 x 812
Output:    157128 bytes
Lossless-ARGB compressed size: 157128 bytes
  * Header size: 469 bytes, image data size: 156634
  * Lossless features used: PALETTE
  * Precision Bits: histogram=5 transform=5 cache=0
  * Palette size:   256
zsh % ls -l --human --sort=size 2017*
-rw-r--r-- 1 shyouhei wheel 449K  6 19 13:21 20170618160637.bmp
-rw-r--r-- 1 shyouhei wheel 231K  6 19 12:33 20170618160637.jpg
-rw-r--r-- 1 shyouhei wheel 189K  6 19 13:07 20170618160637.png
-rw-r--r-- 1 shyouhei wheel 154K  6 19 14:44 20170618160637.lsss0.webp
-rw-r--r-- 1 shyouhei wheel 154K  6 19 14:16 20170618160637.zopfli.png
-rw-r--r-- 1 shyouhei wheel 136K  6 19 13:21 20170618160637.bmp.br
-rw-r--r-- 1 shyouhei wheel 114K  6 19 13:21 20170618160637.cjpeg.jpg
-rw-r--r-- 1 shyouhei wheel  74K  6 19 14:29 20170618160637.webp

154KiB。まあzopfliと大差ない。逆に言うとpngで用が足りるなら、わざわざロスレスのwebpを採用する意味はないだろう。このフォーマットは強いjpegとして使うべきっぽい。

ここまでのまとめ

  • 圧縮による劣化を避けたい場合
    • Androidネイティブアプリ広告などchrome系という前提を置けるなら、bmpをbrotliで圧縮するのがなんだかんだで一番ギガが減らない
    • そうじゃなくて閲覧環境を特定できないなら、zopflipngが一番ギガの減りが少ない
  • 圧縮で画像が劣化していいなら
    • Chrome系という前提を置けるなら、非可逆圧縮のwebpが圧倒的。jpegよりもかなり小さく、かつ、jpegよりもかなりきれい。
    • そうじゃなくて閲覧環境を特定できないなら、mozjpegを使うのが良さそう。

減色する

もとネタの小島先生は減色によるサイズ圧縮を使っていたので、これを試すとどうなるか。

png + zopfli

zsh % convert 20170618160637.jpg -resize '416x600' -strip -colors 8 20170618160637.3bit.png
zsh % ~/bin/zopflipng --filters=01234mepb --lossy_transparent 20170618160637.3bit.png 20170618160637.3bit.zopfli.png
Optimizing 20170618160637.3bit.png
Input size: 56712 (55K)
Result size: 34454 (33K). Percentage of original: 60.753%
Result is smaller

zsh % ls -l --human --sort=size *3bit*
-rw-r--r-- 1 shyouhei wheel 56K  6 19 17:24 20170618160637.3bit.png
-rw-r--r-- 1 shyouhei wheel 34K  6 19 17:24 20170618160637.3bit.zopfli.png

いきなり34KiBまで減った。これは小島先生が手で作成したgif画像より僅差で小さいくらいなので、ようは2017年の現在あえて gifを選ぶメリットはない ということでもある。

bmp + brotli

zsh % convert 20170618160637.jpg -resize '416x600' -strip -colors 8 BMP3:20170618160637.3bit.bmp
zsh % ~/bin/brotli --best --keep --verbose 20170618160637.3bit.bmp
zsh % ls -l --human --sort=size *3bit*
-rw-r--r-- 1 shyouhei wheel 122K  6 19 17:26 20170618160637.3bit.bmp
-rw-r--r-- 1 shyouhei wheel  56K  6 19 17:24 20170618160637.3bit.png
-rw-r--r-- 1 shyouhei wheel  34K  6 19 17:24 20170618160637.3bit.zopfli.png
-rw-r--r-- 1 shyouhei wheel  32K  6 19 17:26 20170618160637.3bit.bmp.br

32KiB、まあ減ったけど小改善かな。

webp

zsh % cwebp -noalpha -m 6 -q 0 pass 10 -lossless 20170618160637.3bit.zopfli.png -o 20170618160637.3bit.loss0.webp
Saving file '20170618160637.3bit.loss0.webp'
File:      20170618160637.3bit.zopfli.png
Dimension: 416 x 600
Output:    34914 bytes
Lossless-ARGB compressed size: 34914 bytes
  * Header size: 222 bytes, image data size: 34667
  * Lossless features used: PALETTE
  * Precision Bits: histogram=4 transform=4 cache=0
  * Palette size:   7
zsh % cwebp -noalpha -m 6 pass 10 20170618160637.3bit.zopfli.png -o 20170618160637.3bit.webp
Saving file '20170618160637.3bit.webp'
File:      20170618160637.3bit.zopfli.png
Dimension: 416 x 600
Output:    51734 bytes Y-U-V-All-PSNR 38.49 99.00 99.00   40.25 dB
block count:  intra4: 798
              intra16: 190  (-> 19.23%)
              skipped block: 133 (13.46%)
bytes used:  header:            307  (0.6%)
             mode-partition:   3649  (7.1%)
 Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
    macroblocks:  |      92%|       0%|       7%|       0%|     988
      quantizer:  |      27 |      23 |      15 |      15 |
   filter level:  |       8 |       5 |       2 |       2 |
zsh % ls -l --human --sort=size *3bit*
-rw-r--r-- 1 shyouhei wheel 732K  6 19 17:37 20170618160637.3bit.tif
-rw-r--r-- 1 shyouhei wheel 122K  6 19 17:26 20170618160637.3bit.bmp
-rw-r--r-- 1 shyouhei wheel  56K  6 19 17:24 20170618160637.3bit.png
-rw-r--r-- 1 shyouhei wheel  51K  6 19 17:39 20170618160637.3bit.webp
-rw-r--r-- 1 shyouhei wheel  35K  6 19 17:35 20170618160637.3bit.loss0.webp
-rw-r--r-- 1 shyouhei wheel  34K  6 19 17:24 20170618160637.3bit.zopfli.png
-rw-r--r-- 1 shyouhei wheel  32K  6 19 17:26 20170618160637.3bit.bmp.br

losslessで32KiB、lossyでは逆に51Kと増えてしまったが、これはおそらく使い方が悪くて、losslessはきちんとPALETTEって出力されてるからindexed colorになってるけど、たぶんlossyはそうなってない。

ここまでのまとめ

  • いまどきgifを選択する必要はない
  • webpと減色の相性は調査不足を感じる

lossyでよければpngcrush、pngquantあたりも人気ですよね。

Owner

shyouhei commented Jun 19, 2017

まあ多分使い方が悪いんでしょうが、そのへんのツールはlossyのくせにlosslessなzopfliに勝ててないので微妙かなーと思います。

zsh % pngcrush -reduce 20170618160637.png 20170618160637.pngcrush.png

 | pngcrush-1.8.11
 |    Copyright (C) 1998-2002, 2006-2016 Glenn Randers-Pehrson
 |    Portions Copyright (C) 2005 Greg Roelofs
 | This is a free, open-source program.  Permission is irrevocably
 | granted to everyone to use this version of pngcrush without
 | payment of any fee.
 | Executable name is pngcrush
 | It was built with   bundled libpng-1.6.28
 | and is running with bundled libpng-1.6.28
 |    Copyright (C) 1998-2004, 2006-2016 Glenn Randers-Pehrson,
 |    Copyright (C) 1996, 1997 Andreas Dilger,
 |    Copyright (C) 1995, Guy Eric Schalnat, Group 42 Inc.,
 | and bundled zlib-1.2.11, Copyright (C) 1995-2017,
 |    Jean-loup Gailly and Mark Adler,
 | and using "clock()".
 | It was compiled with gcc version 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1).

  Recompressing IDAT chunks in 20170618160637.png to 20170618160637.pngcrush.png
   Total length of data found in critical chunks            =    167537
   Critical chunk length, method   1 (ws 15 fm 0 zl 4 zs 0) =    175903
   Critical chunk length, method   2 (ws 15 fm 1 zl 4 zs 0) >    175903
   Critical chunk length, method   3 (ws 15 fm 5 zl 4 zs 1) =    172362
   Critical chunk length, method   6 (ws 15 fm 5 zl 9 zs 0) =    171167
   Critical chunk length, method   9 (ws 15 fm 5 zl 2 zs 2) >    171167
   Critical chunk length, method  10 (ws 15 fm 5 zl 9 zs 1) =    165129
   Best pngcrush method        =  10 (ws 15 fm 5 zl 9 zs 1) =    165129
     (1.44% critical chunk reduction)
     (1.09% filesize reduction)

CPU time decode 0.044220, encode 0.840675, other 0.008554, total 0.902290 sec
zsh % pngquant --output 20170618160637.pngquant.png 20170618160637.png
zsh % ls -l --human --sort=size 2017*.png
-rw-r--r-- 1 urabe.shyouhei 86556269 189K  6 19 19:09 20170618160637.png
-rw-r--r-- 1 urabe.shyouhei 86556269 187K  6 19 19:12 20170618160637.pngcrush.png
-rw-r--r-- 1 urabe.shyouhei 86556269 154K  6 19 19:13 20170618160637.pngquant.png
-rw-r--r-- 1 urabe.shyouhei 86556269 154K  6 19 19:11 20170618160637.zopfli.png
-rw-r--r-- 1 urabe.shyouhei 86556269  56K  6 19 19:10 20170618160637.3bit.png
-rw-r--r-- 1 urabe.shyouhei 86556269  34K  6 19 19:10 20170618160637.3bit.zopfli.png

どちらも、256色に原色して減らすというものなので、元絵がパレットを使ってなくて色数が多い絵じゃないと効果はないですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment