ImageMagick と WebP

ImageMagick の WebP 対応についてです。 (2017年12月23日投稿)
![webplogo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1798/50a01848-b3ea-2269-fa6d-220221344b5e.png)

# WebP って？

Google が比較的最近(仕様公開は2010年)に世に出してきた画像フォーマットです。VP8 動画コーデックの静止画圧縮を用います。

- https://developers.google.com/speed/webp/

JPEG のように見た目が気にならない程度に画像データを劣化(元の値から変化)させてファイル容量を圧縮する方式です。JPEG よりさらにファイルサイズを削減できます。これと中身がほぼ別物になりますが、PNG のような可逆圧縮も使えます。この２つを区別する場合、前者を Lossy(劣化圧縮, 非可逆圧縮) 、後者を Lossless(無劣化圧縮、可逆圧縮) と呼ぶ事が多いです。

あと、WebP は GIF のようなアニメーションを表現できます。<del>ただ、ImageMagick は WebP のアニメーション機能は未対応です。(最近も issue にあがったけどスルー気味)</del> <add> 7.0.8-68(2019年10月)でアニメーション対応しました。アニメーションに於ける blend モード対応が 7.0.10-26(2020年8月) なので、これ以降のバージョンを使うと良いでしょう。 </add>

使用上の注意として、Lossy 方式の WebP は YUV420 (色差成分の解像度が半分)のみ対応なので、イラストや CG 画像を WebP ファイルとして保存すると画質の劣化が辛い事があります。特に偽色問題(詳細は後述)が有名です。<del>あと、ブラウザによっては表示できないかも？</del>

# ImageMagick で WebP を使う

ImageMagick は 6.6.8(2011年3月) で WebP に対応しました。ただし当初は不具合が沢山あって、6.8.6-8(2013年7月)以降のバージョンをお勧めします。

| ImageMagick+WebPでモバイルファーストしちゃう |
|---|
| ![slide_18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1798/7d153f0c-762a-888e-541e-ed47f8c58d68.jpeg) |
| https://speakerdeck.com/mirakui/how-we-gain-mobile-first-using-webp-with-imagemagick?slide=19|

## 使えるか確認

WebP に対応しているか -list format コマンドで確認できます。(Linux 系の標準パッケージはまだのような？)

```
% convert -list format | grep WebP
     WEBP* WEBP      rw-   WebP Image Format (libwebp 0.6.1 [0208])
% convert rose: rose.webp
% identify rose.webp
rose.webp WEBP 70x46 70x46+0+0 8-bit sRGB 906B 0.000u 0:00.000
```
(蛇足ですが rw- のところ。GIF のようにアニメーション対応すると rw+ になります)

ダメなら libwebp のある環境で ImageMagick を自分でビルドしましょう。

## 自前でビルド

libwebp と ImageMagick の最新版をとってきます。

- https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
  - [libwebp-0.6.1.tar.gz](https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-0.6.1.tar.gz)
- https://www.imagemagick.org/download/
  - [ImageMagick-6.9.9-28.tar.gz](https://www.imagemagick.org/download/ImageMagick-6.9.9-28.tar.gz)

```
% tar xvfz libwebp-0.6.1.tar.gz
% cd libwebp-0.6.1
% ./configure --prefix=$HOME/libwebp/0.6.1
% make install
＜略＞
% tar xvfz ImageMagick-6.9.9-28.tar.gz
% cd ImageMagick-6.9.9-28
% ./configure --prefix=$HOME/ImageMagick/6.9.9-28 --with-webp=yes --with-webp-dir=$HOME/libwebp/0.6.1
% make install
＜略＞
% $HOME/ImageMagick/6.9.9-28/bin/convert -list format | grep WebP
     WEBP* rw-   WebP Image Format (libwebp 0.6.1[0208])
```
# JPEG と雑に比較する

本来はたくさんの画像で検証すべきですが、ちょっと雑に実験。

```
% convert fujisan-800.png fujisan-800.jpg
% convert fujisan-800.png fujisan-800.webp
% ls -l fujisan-800.png fujisan-800.jpg fujisan-800.webp
-rw-r--r--  1 yoya  staff  133519 12 26 16:42 fujisan-800.jpg
-rw-r--r--@ 1 yoya  staff  631243 12 26 16:41 fujisan-800.png
-rw-r--r--  1 yoya  staff   43302 12 26 16:42 fujisan-800.webp
```

わぉ。。。と喜びたい所ですが、実はデフォルトで出力する画質が違います。
実際に並べて見ると、細部が微妙に潰れて見えるでしょう。 (Qiita は WebP を貼れないので、PNG として貼ります)

|fujisan-800.jpg.png|
|---|
![fujisan-800.jpg.png](https://qiita-image-store.s3.amazonaws.com/0/1798/b8d5ce81-cf1a-956b-6df7-6bd1ec6d6d76.png)|

|fujisan-800.webp.png|
|---|
| ![fujisan-800.webp.png](https://qiita-image-store.s3.amazonaws.com/0/1798/4ce58c3c-b4d3-9d92-5e70-6f601227c38f.png) |

compare コマンドで SSIM 算出してみます。

```
% compare -verbose -metric ssim  fujisan-800.png fujisan-800.jpg null:
fujisan-800.png PNG 800x534 800x534+0+0 8-bit sRGB 631243B 0.020u 0:00.019
fujisan-800.jpg JPEG 800x534 800x534+0+0 8-bit sRGB 133519B 0.020u 0:00.019
Image: fujisan-800.png
  Channel distortion: SSIM
    red: 0.976227
    green: 0.980428
    blue: 0.969786
    all: 0.97548
fujisan-800.png=> PNG 800x534 800x534+0+0 8-bit sRGB 1.190u 0:01.210
% compare -verbose -metric ssim  fujisan-800.png fujisan-800.webp null:
fujisan-800.png PNG 800x534 800x534+0+0 8-bit sRGB 631243B 0.020u 0:00.019
fujisan-800.webp WEBP 800x534 800x534+0+0 8-bit sRGB 43302B 0.020u 0:00.019
Image: fujisan-800.png
  Channel distortion: SSIM
    red: 0.936094
    green: 0.945314
    blue: 0.923273
    all: 0.934894
fujisan-800.png=> PNG 800x534 800x534+0+0 8-bit sRGB 1.400u 0:01.639
```

ちょっとこれは SSIM 値が下がっていて不公平ですね。

## JPEG quality に合わせる。

webp:emulate-jpeg-size=true で画質の尺度を JPEG の quality に近づける事ができます。

```
% identify -verbose fujisan-800.jpg | grep Qual
  Quality: 92
% convert -define webp:emulate-jpeg-size=true -quality 92 fujisan-800.png  fujisan-800-q92-jpegemu.webp
```

|fujisan-800-q92-jpegemu.webp.png|
|---|
| ![fujisan-800-q92-jpegemu.webp.png](https://qiita-image-store.s3.amazonaws.com/0/1798/db8b3bc0-c1ca-0b5c-ad0b-cfd632d56935.png) |

SSIM もいい感じの値です。

```
% compare -verbose -metric ssim  fujisan-800.png fujisan-800-q92-jpegemu.webp null:
fujisan-800.png PNG 800x534 800x534+0+0 8-bit sRGB 631243B 0.010u 0:00.019
fujisan-800-q92-jpegemu.webp WEBP 800x534 800x534+0+0 8-bit sRGB 113914B 0.030u 0:00.009
Image: fujisan-800.png
  Channel distortion: SSIM
    red: 0.984659
    green: 0.991055
    blue: 0.977479
    all: 0.984398
fujisan-800.png=> PNG 800x534 800x534+0+0 8-bit sRGB 1.410u 0:01.670
```

```
% ls -l fujisan-800.jpg  fujisan-800-q92-jpegemu.webp
-rw-r--r--  1 yoya  staff  113914 12 26 17:04 fujisan-800-q92-jpegemu.webp
-rw-r--r--  1 yoya  staff  133519 12 26 16:42 fujisan-800.jpg
```

劇的には減っていませんが、そこそこ節約できています。
SSIM の値を見ると今度は JPEG より品質が良すぎるので、もっと quality 下げないと逆に不公平ですし、のっぺりするのを問題にするのであれば filter-sharpness で対処もできます。まだまだファイルサイズを減らす余地があると言えます。

# -define webp:〜

ここからは具体的なパラメータの解説です。
実質 libwebp や VP8 の説明になっています。
ImageMagick の webp モジュールはほぼ libweb を呼ぶだけで、webp:〜 オプションで指定した値は、そのまま libwebp の WebPConfig に渡ります。そこから更に VP8 に横流しされたりします。

## webp:lossless (boolean)

true で画像を劣化させない圧縮です。処理に時間がかかる上に劣化を伴う圧縮ほどはファイスサイズが減りません。

```
% convert fujisan.png fujisan.webp
% convert fujisan.png -define webp:lossless=true fujisan-lossless.webp
% ls -l fujisan.png fujisan.webp fujisan-lossless.webp
-rw-r--r--  1 yoya  staff   7537942 12 26 17:39 fujisan-lossless.webp
-rw-r--r--@ 1 yoya  staff  11179570 12 12 01:31 fujisan.png
-rw-r--r--  1 yoya  staff    702622 12 26 17:39 fujisan.webp
```

### near lossless (2023/12/20 追記)

ImageMagick-7.1.1-6(2023/04/02) から -quality で 99 以下の値を指定すると near lossless として処理します。

near lossless については以下のエントリを参照下さい。

-  WebP Lossless はちゃんと Lossless してます
   - https://qiita.com/yoya/items/a000c2e123d448a37f47#near-lossless

なお、7.1.1-5 までは webp:near-lossless で指定していました。こちらの指定は廃止されてます。

## webp:method (integer)

圧縮メソッドで 0〜6の値でデフォルトは 4 。
エンコード速度、圧縮サイズ、画質のトレードオフを制御できます。
値を増やすほど圧縮に時間をかけて頑張ります。値を減らすと速い代わりに画質も圧縮も低下していきます。

```
% for m in `seq 0 1 6` ;
    do convert fujisan.png -define webp:method=$m fujisan-method-$m.webp ;
  done
% ls -l fujisan-method-?.webp
-rw-r--r--  1 yoya  staff   1216252 12 26 17:45 fujisan-method-0.webp
-rw-r--r--  1 yoya  staff    883528 12 26 17:45 fujisan-method-1.webp
-rw-r--r--  1 yoya  staff    760004 12 26 17:45 fujisan-method-2.webp
-rw-r--r--  1 yoya  staff    700532 12 26 17:45 fujisan-method-3.webp
-rw-r--r--  1 yoya  staff    702622 12 26 17:45 fujisan-method-4.webp
-rw-r--r--  1 yoya  staff    667396 12 26 17:45 fujisan-method-5.webp
-rw-r--r--  1 yoya  staff    648458 12 26 17:45 fujisan-method-6.webp
```

## webp:image-hint (string)

default, photo, picture, graph の４種類から選びます。
指定したシーンに適したエンコードをしてくれそうですが、
実装を見ると graph だけ参照しています。また、今のところ Lossless のみ対応です。

似た概念の機能として libwebp に preset 指定があり、こちらは Lossy にも効きますが、ImageMagick は preset 未対応です。(2024年3月13日時点)

## webp:target-size (int)

目標とする画像サイズを指定できます。単位はバイト数。(KやMではない。ビットでもない)
あと、これはあくまで目標値で、ぴったりのサイズは期待できません。
後述する webp:pass とセットで使います。

## webp:target-psnr (double)

目標とする画質を指定できます。こちらもあくまで目標値です。
PSNR なので dB 単位。 42 がよく使われます。
後述する webp:pass とセットで使います。
 
## webp:segments (int)

セグメントの個数です。1〜4が指定できて デフォルトが4です。
JPEG と違って、WebP はマクロブロック毎にエンコードパラメータを切り替えられます。
とはいえ全部のマクロブロックで違うパラメータを持つのは逆効果なので、グループ化して共通化する仕組みがセグメントです。

## webp:sns-strength (int)

どの画像領域でビット数がより減った方が良いのかを探すアルゴリズム(spatial noise shaping)で、0 は無し。100は最大限に努力します。
デフォルトは50です。

## webp:filter-strength (int)

ブロッキングフィルタの強さです。０がオフ、100が最大です。大きいほど滑らかになります。20〜50 がよく使われます。

## webp:filter-sharpness (int)

ぼやけを抑えるよう、画像をシャープ(先鋭化)にするフィルタをかけます。

## webp:filter-type (int)

0:nostrong(off (-nostrong相当)、1:simple (-strong相当) 2: complex
例えば、simpleだとマクロブロックの外2pixelのluma(chromaはフィルタしない)をフィルタしますが、complex は3pixelまでフィルタにかけます。(frame_dec.c)

## webp:auto-filter (boolean)

true にすると品質的にバランスのよい最適化を行います。お任せモード。

## webp:alpha-compression (int)

透明度データを圧縮するなら 1、しないなら 0。どちらもロスレスのはず。

## webp:alpha-filtering (int)

透明度データのフィルタ指定。 0:なし、1:fast(default)、2:best

## webp:alpha-quality (int)

透明度データの画質を指定します。量子化レベルを指定します。

- Quality:[0, 70] -> Levels:[2, 16]
- Quality:]70, 100] -> Levels:]16, 256].

70を境に量子化のレベルの扱いが切り替わります。

## webp:pass (int)

webp:target--psnr や webp:target--size は指定した目標値に近くまで処理を何度もやり直します。その最大回数です。
libwebp 的に 1〜10の値が有効で、デフォルトが 1 (って、あれれ？ デフォだと -psnr, -size あまり意味ない？)

## webp:show-compressed (int)

圧縮した画像をユーザメモリに返す機能だけど、よくわかりません。^^;

## webp:preprocessing (int)

0=none, 1=segment-smooth, 2=pseudo-random dithering.
実装をみるとフラグ処理してるので、マニュアルに明記されてないが、3 指定で 1 と 2 の両方動作するかも。

## webp:partitions (int)

progressive decoding: 0〜3 から選択。

## webp:partition-limit (int)

1つ目のパーテーションを 512K に合わて画質を下げるかどうか。0 は何もしない。100 は全力。

## webp:emulate-jpeg-size (boolean)

quality 指定を JPEG 相当に似せる。
JPEG では quality75 がバランス良いが、内部的には 50 なので、そのマッピングを行う。

## webp:low-memory (boolean)

true にするとメモリ使用を抑えます。ただしエンコードは遅くなります。

## webp:thread-level (int)

マルチスレッドエンコード。0:無効、1(以上): 有効。

## webp:use-sharp-yuv (int) (2019年追記)

libwebp-0.6.0 (2017年1月リリース) で色劣化への対策が入っているのですが、ImageMagick は 7.0.8-28 (2019年2月)でようやく対応しました。

```
% convert rb-grid.png rb-grid.webp
% convert rb-grid.png -define webp:use-sharp-yuv=1 rb-grid-sharp-yuv.webp
% convert rb-grid.png -sampling-factor 4:2:0 rb-grid-YUV420.jpg  # (参考)
```
| rb-grid.png | rb-grid.webp(.png) |
|---|---|
| ![rb-grid.png](https://qiita-image-store.s3.amazonaws.com/0/1798/85186238-dbbe-461b-ca68-fdb1a8c9fe83.png) | ![rb-grid.webp.png](https://qiita-image-store.s3.amazonaws.com/0/1798/721f72b0-3d35-6e3b-5c38-72e2b68b1759.png) |
| rb-grid-sharp-yuv.webp(.png) | (参考) rb-grid-YUV420.jpg |
| ![rb-grid-sharp-yuv.webp.png](https://qiita-image-store.s3.amazonaws.com/0/1798/da1b9dbb-9976-fdb8-ea13-04a1e8f4d453.png) |![rb-grid-YUV420.jpg](https://qiita-image-store.s3.amazonaws.com/0/1798/6f58c8de-9ad5-aebb-638c-9b6a245a5a00.jpeg) |

詳しくは以下の記事をどうぞ。

- WebP の色劣化問題の改善
   - https://qiita.com/yoya/items/f5ef9b66b556eda9e6bf

わずかにファイルサイズと処理時間を犠牲にしますが、目に見えて改善する事があります。

# libwebp バージョン (2019年追記)

- libwebp のバージョンをあげると速くなる 
   - https://qiita.com/yoya/items/bd5f8ff3be6a57586d03 

古いバージョン(0.2.0 まで)の libwebp を使うと目に見えてエンコードが遅いです。新し目の(0.6.x や 1.x系)を使うと良いです。

| libwebp バージョン毎 encode 速度比較 |
|---|
|![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/2920b7dc-f5e8-e9d1-32b3-f4e994361765.png)|


# 参考 URL

- https://www.imagemagick.org/script/webp.php
  - このエントリは、殆どこれの和訳です。あとは libwebp の README や実際のコードと比較しながら書きました。

- Google 公式仕様
  - https://developers.google.com/speed/webp/docs/riff_container
  - https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
  - https://developers.google.com/speed/webp/docs/compression