pngquantでpng容量削減圧縮したときなぜかグレースケール化したpngの方が容量が大きくなってしまう問題

## TL;DR (Too Long, Didn’t Read) 要約
pngquantは画像がグレースケールと見るや仕事をサボる[^1]ので、`--quality -90` オプションで働かせましょう。

## えっ！？グレースケール画像の方が容量でかくね？
自炊したページをpngquantにかけて容量削減しようとしたとき、ふと気が付くと白黒2色刷りページの、グレースケール化したpngより、グレースケール化していないpngの方がpngquantにかけた後の容量が小さくなってるのに気づきました。

|対象|pngquant処理前|pngquant処理後|
|:--|:--|:--|
|グレースケール化していないpng|961.6KB|**270.9KB**|
|グレースケール化したpng      |321.8KB|**296.5KB**|


容量を少しでも減らしたいがためにグレースケール化してるのにしていない方が容量が小さいなんてこれは許容できません。
なんでこんな逆転現象が生じてるのか、グレースケール化した方をしてないものより容量を小さくするにはどうすればいいのか、調べました。


## --verbose オプションで何が起こってるのか調べる
###グレースケール処理していない画像をpngquantした結果
96652色の色が使われていることを検出。
(Q=89) 品質89で処理されたことがわかりました。（961.6KB→270.9KB）

```sh
$ pngquant --verbose --ext _new.png --speed 1 256 nonGS.png
nonGS.png:
  read 962KB file
  made histogram...96652 colors found
  selecting colors...4%
  selecting colors...9%
  selecting colors...14%
  selecting colors...42%
  selecting colors...47%
  selecting colors...52%
  selecting colors...80%
  selecting colors...100%
  moving colormap towards local minimum
  eliminated opaque tRNS-chunk entries...0 entries transparent
  mapped image to new colors...MSE=3.154 (Q=89)
  writing 256-color image as nonGS_new.png
  copied 1KB of additional PNG metadata
Quantized 1 image.
```


###一方、グレースケールされた画像をpngquantした結果
256色の色が使われていることを検出。
(Q=100) 品質100で処理されたことがわかりました。（321.8KB→296.5KB）
そしてカラー画像のときのような処理をしていないように見受けられます。

```sh
$ pngquant --verbose --ext _new.png --speed 1 256 GS.png
GS.png:
  read 322KB file
  made histogram...256 colors found
  eliminated opaque tRNS-chunk entries...0 entries transparent
  mapped image to new colors...MSE=0.000 (Q=100)
  writing 256-color image as GS_new.png
  copied 1KB of additional PNG metadata
Quantized 1 image.
```

つまりpngquantはカラー画像を読ませるとゴリゴリに処理するのに対して、グレースケール画像を読ませると圧縮の必要なしと見なしてほとんど処理してくれないのです。

これを解消するには、グレースケール画像であろうとpngquantに強制的にカラー画像を読ませたときと同程度の削減処理をさせればいいわけです。

## --qualityオプションでどんな画像であろうと一定レベルの削減を行うようにする
`--quality [<min>]-[<max>]` （または `-Q [<min>]-[<max>]`）で品質を指定してグレイスケールでも削減させるようにします。qualityは0～100で指定してデフォルトでは100です。

```
$ pngquant --quality 0-50
$ pngquant --quality 80-100
```

カラー画像を処理する場合、およそ(Q=90)で処理されているので、同レベルの削減処理をさせるために、qualityを90に指定しましょう。`<min>`を省略して `-90` と指定すると `0-90` を指定したことになり、品質90を狙って処理するということになります（多少誤差は出る）。

```sh
$ pngquant --verbose --ext _new.png --speed 1 --quality -90 256 GS.png
GS.png:
  read 322KB file
  made histogram...256 colors found
  selecting colors...2%
  selecting colors...4%
  selecting colors...6%
  selecting colors...8%
  selecting colors...10%
  selecting colors...23%
  selecting colors...25%
  selecting colors...38%
  selecting colors...40%
  selecting colors...42%
  selecting colors...55%
  selecting colors...57%
  selecting colors...70%
  selecting colors...82%
  selecting colors...95%
  selecting colors...100%
  moving colormap towards local minimum
  eliminated opaque tRNS-chunk entries...0 entries transparent
  mapped image to new colors...MSE=2.618 (Q=91)
  writing 29-color image as GS_new.png
  copied 1KB of additional PNG metadata
Quantized 1 image.
```

見事、(Q=91) で処理され、（321.8KB→155.2KB）と、だいぶ削減されたファイルが生成されました。
品質91といっても見た目はそんなに変わらないし、私としてはせっかくグレイスケールにしたのにカラー画像の方が容量が小さいという状況に我慢できなかったので、小さいファイルを手に入れることができたのでひとまず安心です。

---
ちなみに、pngquantにかける前のグレースケール画像は tweakpng.exe[^tweakpng] で調べてみると、PNG Header の Color 欄は `0=grayscale` になっているのに、処理後のColor欄は `3=paletted` になっていて、もしかしてpngquantってグレースケール画像をカラー画像化してる！？これが容量が逆転する原因！？？って思いましたが、カラー画像の場合は `2=RGB` なので、これは関係なかったみたいです。

## 参考
- [減色ツールPngquantの仕組みと最適パラメータを探る \- じーろぐ](https://zlog.hateblo.jp/entry/2019/05/05/pngquant)
- [pngquantコマンドの使い方: UNIX/Linuxの部屋](http://x68000.q-e-d.net/~68user/unix/pickup?pngquant)

[^1]: これが原因だと思っていますが、間違っていたら教えてください。
[^tweakpng]: [【レビュー】PNG画像を解析して詳細なフォーマット情報を取得・表示するツール「TweakPNG」 \- 窓の杜](https://forest.watch.impress.co.jp/docs/review/669449.html)