ImageMagick の PNG オプション

ImageMagick の PNG を処理するオプションを解説するエントリです。

# -quality &lt;Q&gt;

可逆圧縮の PNG は画質的に常に 100% なので JPEG や WebP 等と意味合いが異なりますが、quality オプションはあります。
圧縮の速さとファイルサイズのトレードオフ調整に便利です。
10の桁と1の桁を分けて使います。
ちなみにデフォルト75で、これはイケてないのですが内部的にデフォルト初期値が 0 な為に 0 や 00 も 75 扱いになります。

- 10 の桁

| 0 (または省略) | Z_HUFFMAN_ONLY strategy、 zlib デフォルト圧縮レベル |
|---|---|
| 1-9 |  zlib の圧縮レベル |

- 1 の桁

| 0-4 | PNG フィルタメソッド |
|---|---|
| 5 | 圧縮レベル > 5 の時、libpng adaptive フィルタ <br /> 圧縮レベル <= 5 又はグレースケールやパレットの時はフィルタ無し。|
| 6 |  libpng adaptive フィルタ
| 7 | MNG出力では"LOCO" フィルタ(intrapixel differing) <br /> でなければフィルタ無し。IM-6.7.0-9 以前では機能しない |
| 8 | Z_RLE strategy ( quality < 10 だと Z_HUFFMAN_ONLY) adaptive フィルタ。 IM-6.7.0-10 以前では 6 扱い |
|9 |Z_RLE strategy ( quality < 10 だと Z_HUFFMAN_ONLY), PNG フィルタ 無し。IM-6.7.0-10 以前では 6 扱い |

参考までに 10の桁と 1 の桁を変えての実行結果。

```shell:
% for q in `seq 0 1 9` ;
   do convert fujisan.png  -quality ${q}0 fujisan-${q}0.png ;
   done
% ls -l fujisan-??.png
-rw-r--r--  1 yoya  staff  11136998 12 13 22:57 fujisan-00.png
-rw-r--r--  1 yoya  staff  12291141 12 13 22:57 fujisan-10.png
-rw-r--r--  1 yoya  staff  12067045 12 13 22:57 fujisan-20.png
-rw-r--r--  1 yoya  staff  11658367 12 13 22:57 fujisan-30.png
-rw-r--r--  1 yoya  staff  12004722 12 13 22:57 fujisan-40.png
-rw-r--r--  1 yoya  staff  11678218 12 13 22:57 fujisan-50.png
-rw-r--r--  1 yoya  staff  11277910 12 13 22:57 fujisan-60.png
-rw-r--r--  1 yoya  staff  11136998 12 13 22:57 fujisan-70.png
-rw-r--r--  1 yoya  staff  10973235 12 13 22:57 fujisan-80.png
-rw-r--r--  1 yoya  staff  10936107 12 13 22:57 fujisan-90.png
```

```shell:
% for q in `seq 0 1 9` ;
    do convert fujisan.png  -quality 0${q} fujisan-0${q}.png ;
    done
% ls -l fujisan-??.png
-rw-r--r--  1 yoya  staff  11136998 12 13 22:59 fujisan-00.png
-rw-r--r--  1 yoya  staff  30001632 12 13 22:59 fujisan-01.png
-rw-r--r--  1 yoya  staff  30001632 12 13 22:59 fujisan-02.png
-rw-r--r--  1 yoya  staff  30001632 12 13 22:59 fujisan-03.png
-rw-r--r--  1 yoya  staff  30001632 12 13 22:59 fujisan-04.png
-rw-r--r--  1 yoya  staff  14194478 12 13 22:59 fujisan-05.png
-rw-r--r--  1 yoya  staff  14194478 12 13 22:59 fujisan-06.png
-rw-r--r--  1 yoya  staff  14194478 12 13 22:59 fujisan-07.png
-rw-r--r--  1 yoya  staff  14194478 12 13 22:59 fujisan-08.png
-rw-r--r--  1 yoya  staff  14194478 12 13 22:59 fujisan-09.png
```

quality のデフォルトは 75 で、それより 90 の方がサイズが少し減っていますが、その代わり処理時間が数倍になります。

```shell:
% /usr/bin/time convert fujisan.png -quality 75 fujisan-75.png
        4.67 real         4.41 user         0.20 sys
% /usr/bin/time convert fujisan.png -quality 90 fujisan-90.png
       12.20 real        11.78 user         0.25 sys
% ls -l fujisan-75.png fujisan-90.png
-rw-r--r--  1 yoya  staff  11136998 12 14 01:31 fujisan-75.png
-rw-r--r--  1 yoya  staff  10936107 12 14 01:31 fujisan-90.png
```

# -comment

任意のコメントをつけられます。

```shell:
% convert -comment "This is a rose." rose: rose-comment.png
% identify -verbose rose-comment.png | grep -i comment
Image: rose-comment.png
    comment: This is a rose.
```

# -define png:

## png:color-type

PNG はグレー画像、パレット画像、フルカラー画像、そして各々に透明度をつける組み合わせで、カラータイプを持ち、タイプに応じたピクセル形式でファイルに保存します。

- カラータイプ一覧

|Color Type | Bit Depth | Interpretation  |
|---|---|---|
|0 ![png-pixel-type0.png](https://qiita-image-store.s3.amazonaws.com/0/1798/58da5b0d-4046-276c-46b7-29d0d048c0d2.png) | 1,2,4,8,16 | グレースケール. |
|2 ![png-pixel-type2.png](https://qiita-image-store.s3.amazonaws.com/0/1798/471da74d-2c4c-8509-7e8a-f080cb0ad349.png)| 8,16 | RGB の3つ組. (PNG24,PNG48)|
|3![png-pixel-type3.png](https://qiita-image-store.s3.amazonaws.com/0/1798/2bb1fcff-3039-aa78-01fe-de26fb29ff1c.png) | 1,2,4,8 | パレットへのインデックス値。PLTE チャンクが必須。(PNG8) |
|4 ![png-pixel-type4.png](https://qiita-image-store.s3.amazonaws.com/0/1798/df919379-b48c-5805-e9f3-0c29fc6eeff5.png)| 8,16 | グレースケールの各ピクセルにアルファ値が続く。いわゆる YA 形式. |
|6 ![png-pixel-type6.png](https://qiita-image-store.s3.amazonaws.com/0/1798/abb0d86d-6674-b4c4-e59b-a04a063125d3.png)| 8,16 | RGBAの4つ組、A はアルファ値. (PNG32,PNG64)|

出力PNGのカラータイプを指定できます。
ちなみに互換性のないカラータイプには(そのままでは)変換できません。

例えば、color-type 2 の RGB 画像 fujisann.png を用意して、

```shell:
% identify -verbose fujisan.png | grep color_type
    png:IHDR.color_type: 2 (Truecolor)
```
RGB から color-type:2(RGB) と 6(RGBA) には変換出来ます。 

```shell:
% convert fujisan.png -define png:color-type=2  2.png
% convert fujisan.png -define png:color-type=6  6.png
%
```

しかし、それ以外の値 color-type:0(Grayscale) 3(Palette), 4(Grayscale+Alpha) を指定するとエラーになります。

```shell:
% convert fujisan.png -define png:color-type=0  0.png
convert: Cannot write image with defined png:bit-depth or png:color-type. `0.png' @ warning/png.c/MagickPNGWarningHandler/1744.
% convert fujisan.png -define png:color-type=3  3.png
convert: Cannot write PNG8 or color-type 3; colormap is NULL `3.png' @ error/png.c/WriteOnePNGImage/9752.
% convert fujisan.png -define png:color-type=4  4.png
convert: Cannot write image with defined png:bit-depth or png:color-type. `4.png' @ warning/png.c/MagickPNGWarningHandler/1744.
```

グレースケールなら -colorspace Gray 。パレットなら -type Palette で変換して下さい。

```shell:
% convert fujisan.png -colorspace Gray -define png:color-type=0  0.png
% convert fujisan.png -type Palette  -define png:color-type=3  3.png
% convert fujisan.png -colorspace Gray -define png:color-type=4  4.png
```

## png:bit-depth

R,G,B 各々に多くの場合 8bit (0〜255)で格納しますが、このビット数を変更できます。

```shell:
% convert fujisan.png -define png:bit-depth=16 fujisan-q16.png
% identify fujisan.png fujisan-q16.png
fujisan.png PNG 4240x2832 4240x2832+0+0 8-bit sRGB 10.6617MiB 0.000u 0:00.000
fujisan-q16.png PNG 4240x2832 4240x2832+0+0 16-bit sRGB 12.6534MiB 0.000u 0:00.000
```

ただし、上記のカラータイプ一覧にある通り、カラータイプ毎に使える bit-depth が制限される事に注意です。

```shell:
% convert fujisan.png -define png:bit-depth=4 fujisan-q4.png
convert: Cannot write image with defined png:bit-depth or png:color-type. `fujisan-q4.png' @ warning/png.c/MagickPNGWarningHandler/1744.
```

なお、基本的には R,G,B のビット幅ですが、パレット形式(PNG8)の時だけは、インデックスのビット幅の指定になります。パレット形式の R,G,B ビット幅は 8 固定です。

## png:compression-filter

PNG は zlib 圧縮がよく効くようRGBデータ表現を少しいじるフィルタの仕組みがあり、その具体的な指定ができます。
範囲は 0〜9です。

- 0: 未指定。つまり quality の指示に準ずる。
- 1〜4: 0〜3 にずらしてフィルタタイプを明示的に選択できます。libpng の定義で 0:NONE, 1:SUB, 2:UP, 3:AVG
- 5: 適応フィルタ(全部試して良いのを選択する) カラーマップは例外
- 6: 適応フィルタ。カラーマップも含める。
- 7: MNG LOCO 圧縮 (アニメーションのコマ間の差分圧縮)
- 8: 適応フィルタ + Z_RLE ストラテジ
- 9: フィルタなしの Z_RLE ストラテジ

例えば綺麗なグラデーション画像の場合、隣のピクセル色との差分で表現すれば、全部同じ値になって圧縮率がめちゃくちゃ上がる。なんて事も極端な例としては有り得ます。上記の例だと 1:SUB がそれに相当します。

## png:compression-level

zlib の圧縮は時間をかけた方がよりサイズを減らせるので、その頑張り具合を指定できます。
範囲は 0〜9 です。0 は速いけど圧縮があまり効かない。9 は遅いけどかなり圧縮されます。

## png:compression-strategy

zlib 圧縮はランレングスとハフマン圧縮を組み合わせたアルゴリズムで、例えばハフマン圧縮だけ使う。みたいな指定が出来ます。範囲は 0〜4 です。

- 0: 未指定。つまり quality の指示に準ず
- 1: FILTERED
- 2: ハフマン圧縮のみ。(RLE無し)
- 3: RLE のマッチ距離を1にする
- 4: 固定ハフマン圧縮

## png:swap-bytes

ImageMagick-6.8.8-3 からの機能です。

PNG は bit-depth=16 の時にネットワークバイトオーダー、つまりビッグエンディアンで値を格納します。

- https://www.w3.org/TR/2003/REC-PNG-20031110/#7Integers-and-byte-order

ここに(多分間違えて)リトルエンディアンで入れてくる機械があるそうで、その対処用みたいです。

- http://www.imagemagick.org/discourse-server/viewtopic.php?f=2&t=24818

## png:exclude-chunk

特定のチャンクを取り除く事ができます。
例えば、コメントは tEXt チャンクに入るので、

```shell:
% convert -comment "This is a rose." rose: rose-comment.png
% identify -verbose rose-comment.png | grep -i comment
Image: rose-comment.png
    comment: This is a rose.
```

ここから tEXt を消してみます。

```shell:
% convert rose-comment.png -define png:exclude-chunk=tEXt out.png
% identify -verbose out.png | grep -i comment
    comment: This is a rose.
```

ぐぬぬ。。そういえば zlib 圧縮してコメントを格納する zTXt チャンクがありますね。。

```shell:
% convert rose-comment.png -define png:exclude-chunk="tEXt,zEXt" out.png
% identify -verbose out.png | grep -i comment
%
```

めでたく消えました。

## png:include-chunk

png:exclude-chunk の逆のようです。

## その他

チャンクの種類ごとにいろんな指定がありますが、数が多すぎますし疲れたので、また今度気が向いたら。
これって何？といった質問があれば追記します。

# -set

## -set profile PNG-chunk-x:<file>


PNG 出力時にチャンクを混ぜる事ができます。
渡すファイルは、チャンクから長さとCRCを抜いたデータです。長さとCRCは自動で計算して補完されます。

-x の x で何のチャンクの前または後に入れるのかを指定できて、例えば PNG-chunk-b: で PLTE の前、-m: で PLTE と IDAT の間、-e で IDAT の後ろを表します。
公式の説明は以下の場所にあります。

- https://imagemagick.org/script/command-line-options.php#set

-profile PNG-chunk-x:<file> でも同じようです。

# PNG 1.2 spec, ver 1.5.0

- https://ftp-osl.osuosl.org/pub/libpng/documents/pngext-1.5.0.html

2017年に PNG 仕様の改訂があり、ImageMagick 7系はそこで追加されたチャンクに対応しました。
(ImageMagick 6 系は未対応のままです。こういう事はよくあります)

## eXIf(Exif) チャンク

例えば、Exif 付きの JPEG 画像ファイルを PNG に変換すると、eXIf チャンクが付与されます。
ただ、世間一般の画像ビューアは、この新しい eXIf を認識しないようで、Orientation 値が入っていても
向きの補正はしません。

# 独自チャンク

## orNT(Orientation) チャンク

Orientation を含めた Exif 付き JPEG から ImageMagick-7 で PNG 変換したファイルは、
eXIf と共にorNT チャンクがつきます。これは PNG 非公式の ImageMagick 独自チャンクです。
なお、-orient 指定して PNG 出力すると orNT だけ付きます。

orNT は PNG の仕様ではない為、世間の画像ビューアに表示に影響しません。
ImageMagick-7 では、この orNT と先程の eXIf が auto-orient 対象なので、
見た目の違いが生じる可能性があります。ご注意を。

ちなみに、両方ある場合は、eXIf の orientation が優先されるようです。
(orientation 値を orNT を 2、eXIf を 6 にして auto-orient にかけたら 6 として処理されるのを  7.1.1-10 で確認。orNT, eXIf の順に並んでいるチャンクの後勝ちの可能性もあるので要検証)

# 参考

- https://imagemagick.org/script/command-line-options.php
- https://blog.awm.jp/2016/01/26/png/

# おわり

以上です。