ImageMagick で画像比較 (Compare)

ImageMagick は compare コマンドで２つの画像を比較して差分ハイライト画像を生成したり、その差分をメトリックに応じて数値化する事ができます。

# 画像サンプル

ツイッターで画像の再投稿が繰り返される事による画質劣化で、おそらく日本で一番有名な「5000兆円欲しい!」画像を実験サンプルに使わせて頂きます。

- https://twitter.com/KSUWABE/status/854969265358725121


| Original| JPEG quality:50 |  JPEG quality:10 |
| --- | --- | --- |
|5000.png| 5000-q50.jpg| 5000-q10.jpg|
| ![5000.png](https://qiita-image-store.s3.amazonaws.com/0/1798/302920b9-8dc3-03d8-1369-e0a0e7c8db11.png) | ![5000-q50.jpg](https://qiita-image-store.s3.amazonaws.com/0/1798/f2141cee-0749-f685-bb77-edda8ae9dea0.jpeg) | ![5000-q10.jpg](https://qiita-image-store.s3.amazonaws.com/0/1798/2eb478e4-4709-bb56-e801-ce48a02b3172.jpeg) | 
| 5000-crop200.png| 5000-q50-crop200.png| 5000-q10-crop200.png|
| ![5000-crop200.png](https://qiita-image-store.s3.amazonaws.com/0/1798/d34d9754-a378-c5a8-ded9-516b39d265ad.png)| ![5000-q50-crop200.png](https://qiita-image-store.s3.amazonaws.com/0/1798/6ce62fbd-b1b4-8f43-a20c-619ac26818a9.png)| ![5000-q10-crop200.png](https://qiita-image-store.s3.amazonaws.com/0/1798/c6abb0fd-17ea-c849-bbdc-8296e916e111.png) |

```shell:
% convert 5000.png -quality 50  5000-q50.jpg
% convert 5000.png -quality 10  5000-q10.jpg
% convert 5000.png     -crop 200x200+0+0  5000-crop200.png
% convert 5000-q50.jpg -crop 200x200+0+0  5000-q50-crop200.png
% convert 5000-q10.jpg -crop 200x200+0+0  5000-q10-crop200.png
```

# compare コマンド

## ハイライト画像について

比較したい２枚の画像を指定して、ハイライト画像を生成します。

```shell:
compare 5000.png 5000-q50.jpg  5000-q50-diff.png
compare 5000.png 5000-q10.jpg  5000-q10-diff.png
compare 5000-crop200.png 5000-q50-crop200.png  5000-q50-crop200-diff.png
compare 5000-crop200.png 5000-q10-crop200.png  5000-q10-crop200-diff.png
```

色に違いのない場所は白。少しでも変化があれば赤で、違いが大きいほど濃い赤でハイライトします。

| JPEG quality:50 |  JPEG quality:10 |
| --- | --- |
|  5000-q50-diff.png | 5000-q10-diff.png |
| ![5000-q50-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/d88dcfab-06bd-2da3-ffbb-9b7667d0a5cd.png) | ![5000-q10-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/ecb26a7a-1fd2-7cf4-ffdd-9d437a70e34f.png) |
|5000-q50-crop200-diff.png | 5000-q10-crop200-diff.png |
| ![5000-q50-crop200-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/8fc1c862-b82c-5d91-a392-46ae8e089f02.png)  |![5000-q10-crop200-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/99bae631-3351-5a4f-ed51-d068570957e3.png) |

### ハイライト色指定

ハイライト色を変更出来ます。赤が苦手な人向けに良いでしょう。但し色は階調なしで違いがあるか無いかの2色になります。残念。

```shell:
% compare 5000.png 5000-q50.jpg -highlight-color SeaGreen  5000-q50-diff-SeaGreen.png
% compare 5000.png 5000-q50.jpg -highlight-color SeaGreen -lowlight-color PaleGreen  5000-q50-diff-SeaGreenPaleGreen.png
$ compare 5000-crop200.png 5000-q50-crop200.png -highlight-color SeaGreen  5000-q50-crop200-diff-SeaGreen.png
$ compare 5000-crop200.png 5000-q50-crop200.png -highlight-color SeaGreen -lowlight-color PaleGreen  5000-q50-crop200-diff-SeaGreenPaleGreen.png
```

|SeaGreen | SeaGreen - PaleGreen  |
|--- | --- |
| ![5000-q50-diff-SeaGreen.png](https://qiita-image-store.s3.amazonaws.com/0/1798/20fbd74b-f506-0ad0-8188-55b17e2f9697.png) | ![5000-q50-diff-SeaGreenPaleGreen.png](https://qiita-image-store.s3.amazonaws.com/0/1798/74286f45-db90-3444-6354-cc1bbae4fb4d.png) |
| ![5000-q50-crop200-diff-SeaGreen.png](https://qiita-image-store.s3.amazonaws.com/0/1798/02063a95-8d05-df69-3fd5-3c3243aff628.png) | ![5000-q50-crop200-diff-SeaGreenPaleGreen.png](https://qiita-image-store.s3.amazonaws.com/0/1798/a16ac5ea-9e72-bacc-4a68-f6779f0b6ecb.png) |

### グレースケール

比較画像のファイル形式がグレースケールの場合、ハイライト画像もグレースケールです。

```shell:
% convert 5000.png -type grayscale -define png:color-type=0  5000-gray.png
% convert 5000-q50.jpg -type grayscale -define png:color-type=0  5000-q50-gray.png
% convert 5000-q10.jpg -type grayscale -define png:color-type=0  5000-q10-gray.png
% compare 5000-gray.png 5000-q50-gray.png  5000-q50-gray-diff.png
% compare 5000-gray.png 5000-q10-gray.png  5000-q10-gray-diff.png
```

なお、グレースケールの色しか使っていなくても、ファイル形式が RGB だった場合は、赤くハイライトします。コマンド引数でいうと -define png:color-type=0 が大事です。

|  5000-q50-gray-diff.png | 5000-q10-gray-diff.png |
| --- | --- |
| ![5000-q50-gray-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/67670b58-9d5a-b768-d308-69ac9465ca95.png) |  ![5000-q10-gray-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/404f9613-7f94-05a1-5a51-283d5e087cde.png) |

ちょっと分かりにくいですけど、グレースケールで閉じて処理できる点で合理的な挙動です。

## fuzz

デフォルトだと色の値が 1 違うだけでハイライトしてしまいます。見た目で分からない部分を無視したい場合は、-fuzz で削る事ができます。

```shell:
% compare -fuzz  5% 5000-crop200.png 5000-q50-crop200.png  5000-q50-crop200-fuzz5-diff.png
% compare -fuzz  5% 5000-crop200.png 5000-q10-crop200.png  5000-q10-crop200-fuzz5-diff.png
% compare -fuzz 10% 5000-crop200.png 5000-q50-crop200.png  5000-q50-crop200-fuzz10-diff.png
% compare -fuzz 10% 5000-crop200.png 5000-q10-crop200.png  5000-q10-crop200-fuzz10-diff.png
% compare -fuzz 30% 5000-crop200.png 5000-q50-crop200.png  5000-q50-crop200-fuzz30-diff.png
% compare -fuzz 30% 5000-crop200.png 5000-q10-crop200.png  5000-q10-crop200-fuzz30-diff.png
```

| | |
| --- | --- |
| q:50 fuzz5 | q:10 fuzz:5 |
| ![5000-q50-crop200-fuzz5-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/5b003cbc-00c0-3fd4-8eb6-3e411f27a1f8.png) | ![5000-q10-crop200-fuzz5-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/8b084da9-d66c-79c0-1458-8b39bd2aeaa4.png) |
| q:50 fuzz10 | q:10 fuzz:10 |
|![5000-q50-crop200-fuzz10-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/4afd730e-ab88-dc0b-cf82-b33e5e7934a8.png) | ![5000-q10-crop200-fuzz10-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/a6d5eeb4-3f4d-5bea-262a-5b370f0f91f2.png) |
| q:50 fuzz30 | q:10 fuzz:30 |
| ![5000-q50-crop200-fuzz30-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/f849c9d6-9951-51c2-d3cc-bad8fee47979.png) |  ![5000-q10-crop200-fuzz30-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/1d6003a4-ae50-ed38-e29e-1f7b4656f6bd.png) |

この例では -fuzz 5 の時にブロックノイズがよく分かりますね。

## metric

何かしら観測した値の変化を調べる際には差分を数値化するものですが、対象によって差分の計算方法は様々です。その計算に用いる距離関数をメトリックと呼びます。

ちなみに、-metric 指定した時は、(stdout でなく) stderr に数値が出力されます。仕様です。

- https://legacy.imagemagick.org/discourse-server/viewtopic.php?t=9292

標準出力に出す他のデータと区別したいそうですが、正直、これは微妙ですね。^^;

(閑話休題)

compare の -metric オプションは、見た目の差分を定量化する様々なメトリックに対応しています。

```shell:
% compare -list Metric
AE
DSSIM
Fuzz
MAE
MEPP
MSE
NCC
PAE
PHASH
PSNR
RMSE
SSIM
```

このうち、PSNR と SSIM が特に有名です。
こちらで少し紹介しているので参考までに。

- MSE/PSNR vs SSIM の比較画像紹介
  - https://qiita.com/yoya/items/510043d836c9f2f0fe2f

各々の、計算結果を表示してみます。

```shell:
% for m in `compare -list Metric` ;
    do echo "\n" $m ;
      compare -metric $m 5000.png 5000-q50.jpg  NULL: ;
    done
 AE
253997
 DSSIM
0.16212
 Fuzz
2807.22 (0.0428354)
 MAE
1026.85 (0.0156687)
 MEPP
1.86687e+09 (0.00244649, 1)
 MSE
120.248 (0.00183487)
 NCC
0.858571
 PAE
65535 (1)
 PHASH
386.076
 PSNR
27.3639
 RMSE
2807.22 (0.0428354)
 SSIM
0.67576%
```

なお、今まで通り ３つ目にファイル名を指定すると差分をハイライトした画像を生成しますが、メトリック指定での違いがないので NULL: で捨ててます。 (みた感じ PSNR に似てるけど少し違ってて、決め打ちでの独自処理っぽい)

### 画像

compare を単純に使うと赤いハイライトになりますが、R,G,B の３チャネルに分解して処理すると、RGB 別の差分が見られます。

```shell:
% convert 5000.png     -channel R,G,B -separate  5000-RGB.png
% for q in 50 10
    do convert 5000-q$q.jpg -channel R,G,B -separate  5000-q$q-RGB.png
    for i in 0 1 2
      do compare 5000-RGB-$i.png 5000-q$q-RGB-$i.png 5000-q$q-RGB-$i-diff.png
    done
    convert 5000-q$q-RGB-?-diff.png -combine -set colorspace sRGB 5000-q$q-RGB-diff.png
  done
```

| Red | Green | Blue |
|---|---|---|
| 5000-RGB-0.png | 5000-RGB-1.png | 5000-RGB-2.png |
| ![5000-RGB-0.png](https://qiita-image-store.s3.amazonaws.com/0/1798/dd0ca8fa-437c-511f-cd68-2e51bbae97c2.png) | ![5000-RGB-1.png](https://qiita-image-store.s3.amazonaws.com/0/1798/ca2b6eca-738b-7c1e-8c0f-0ee664a354a3.png) | ![5000-RGB-2.png](https://qiita-image-store.s3.amazonaws.com/0/1798/2ed65871-b37b-f3c9-6485-22b36837226b.png)|
| [5000-q50-RGB-0.png | [5000-q50-RGB-1.png | [5000-q50-RGB-2.png |
| ![5000-q50-RGB-0.png](https://qiita-image-store.s3.amazonaws.com/0/1798/dba5bb1c-e337-69be-96b1-aa228f146461.png) | ![5000-q50-RGB-1.png](https://qiita-image-store.s3.amazonaws.com/0/1798/4af4afbb-ca37-b2e0-37bc-d253b53efa46.png) | ![5000-q50-RGB-2.png](https://qiita-image-store.s3.amazonaws.com/0/1798/68b9fdda-612d-485c-4a18-2f4960729b58.png) |
| [5000-q10-RGB-0.png | [5000-q10-RGB-1.png | [5000-q10-RGB-2.png |
| ![5000-q10-RGB-0.png](https://qiita-image-store.s3.amazonaws.com/0/1798/0061ea3f-d1c0-78e3-084d-1c349a0fc953.png) | ![5000-q10-RGB-1.png](https://qiita-image-store.s3.amazonaws.com/0/1798/f38da734-6af9-accc-d721-8bb29d4c6115.png) | ![5000-q10-RGB-2.png](https://qiita-image-store.s3.amazonaws.com/0/1798/58d1aee6-4154-7e6c-4150-8b34c762ae1f.png) |

| Red | Green | Blue |
|---|---|---|
| 5000-q50-RGB-0-diff.png | 5000-q50-RGB-1-diff.png | 5000-q50-RGB-2-diff.png |
| ![5000-q50-RGB-0-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/e92542d5-648a-7186-6b23-1b8e3033ef76.png) | ![5000-q50-RGB-1-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/8c71ebf0-a3ef-932c-3a72-5935d27b52f6.png) |![5000-q50-RGB-2-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/a4e9b05d-fe4a-492c-c8b5-286b06167730.png) |
| 5000-q10-RGB-0-diff.png | 5000-q10-RGB-1-diff.png | 5000-q10-RGB-2-diff.png |
| ![5000-q10-RGB-0-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/9c447d80-06a6-0c8f-e6e0-5e8568c10dad.png) | ![5000-q10-RGB-1-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/c3d831eb-2f86-0def-5800-2f1f5313f855.png) | ![5000-q10-RGB-2-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/04c607ca-310b-0652-4049-0544e0e70663.png) |

| 5000-q50-RGB-diff.png| 5000-q10-RGB-diff.png |
|---|---|
| ![5000-q50-RGB-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/1ece6f3f-86cf-5ae9-9d66-a652dddc8ff1.png) |![5000-q10-RGB-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/35afa451-f4d9-7a8e-8462-41fdcc6670db.png) |


# composite -compose difference 

先程のハイライト表示では、間違い探しといった用途には便利です。
一方、色の差分をそのまま画像にすれば、どの程度色が違うのかの度合いまで分かります。
ただし、単純な R,G,B 値の差なので見た目の違いの正確さには眼をつむります。
(おそらく、Lab に変換してから差分をとった方が良さそう)

```shell:
$ composite 5000.png 5000-q50.jpg -compose difference  5000-q50-diff.png
```

|5000-q50-diff.png |
|---|
| ![5000-q50-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/3377d761-a32b-5ac5-5e0b-5ae887985ec4.png) |

差分が小さく差分画像が暗くてよく見えない時には、-auto-level が便利です。
このオプションは一番暗いピクセルと一番明るいピクセルを 0 と 255 にして、全体の明るさをその間で引き伸ばします。

```shell:
% convert 5000-q50-diff.png -auto-level  5000-q50-autolevel-diff.png
% 
```

|5000-q50-autolevel-diff.png |
|---|
| ![5000-q50-autolevel-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/16f8d316-dccd-e1bb-8a6a-5f76f19a5499.png) |

はい。変わりません。。。何故ならば、左上2pixelに大きな差分があるからで。。左上を拡大してみます。

```shell:
% convert 5000-q50-diff.png -crop 32x32+0+0  32x32.png
$ convert 32x32.png -sample 800% -fx "(i%8>0)*(j%8>0)*u"  32x32-dot.png
```

| 32x32.png|32x32-dot.png |
|---|---|
| ![32x32.png](https://qiita-image-store.s3.amazonaws.com/0/1798/b7b477c9-d1ff-e9d1-0eca-5578c93f4176.png) | ![32x32-dot.png](https://qiita-image-store.s3.amazonaws.com/0/1798/c8b64ed5-3ced-d751-c78c-9614b3a90187.png) |

draw 等を使って黒で塗りつぶすと、期待通りになります。

(追記) -auto-level でなく -normalize を使うと大丈夫かもしれません 参考) https://qiita.com/yoya/items/1da8e8e04881bab67015#-normalize

```shell:
$ convert 5000-q50-diff.png -fill black -draw "line 0,0 1,0" -auto-level  5000-q50-draw-al-diff.png
```

| 5000-q50-draw-al-diff.png |
| --- | 
| ![5000-q50-draw-al-diff.png](https://qiita-image-store.s3.amazonaws.com/0/1798/1a6b9a68-3c0d-4e86-accd-d9962b5cc8bf.png) |


青の値が大きいので、全体的に明るくなりきれないのは、-auto-level の限界です。

# PSNR 値の不具合 (2016年7月〜2017年2月、〜2018年9月)

同一画像を比較した際の PSNR 値は inf(無限大)のはずですが、2016年〜2017年(6系は〜2018年)の期間は 0 を返す不具合がありました。古い ImageMagick を使っている場合に注意が必要です。

- PSNR no longer returns inf
   -  https://www.imagemagick.org/discourse-server/viewtopic.php?t=31487

## ChangeLog 上の話

ChangeLog 上では ImageMagick-6.9.5-4, (2016年7月30日リリース) で不具合が入り、ImageMagick-6.9.8-0, (2017年3月12日リリース) で修正された事になってます。7系同時リリースは 7.0.2-5 と 7.0.5-2 が該当します。

- ImageMagick-6.9.5-4差分
   - https://yoya.hatenadiary.jp/entry/20160730/im
- ImageMagick-6.9.8-0差分
   -  https://yoya.hatenadiary.jp/entry/20170312/im

実際に試すとだいぶバージョンが違うので要注意です。

## 実際の話

以下、バージョンの右のカッコは(リリース日付)です。

- ImageMagick-7 系

不具合が存在するのは ImageMagick-7.0.2-6(2016/7/30) から 7.0.5-0(2017/2/21) まで。
大丈夫なのは 7.0.5-1(2017/3/5) 以降。

```shell:
% ~/ImageMagick/7.0.2-4/bin/compare -metric PSNR rose: rose: NULL:  # あれれ、まだ正しい？
inf
% ~/ImageMagick/7.0.2-5/bin/compare -metric PSNR rose: rose: NULL:  # あれれれ？
inf
% ~/ImageMagick/7.0.2-6/bin/compare -metric PSNR rose: rose: NULL:  #不具合が入った
0
(略)
% ~/ImageMagick/7.0.5-0/bin/compare -metric PSNR rose: rose: NULL:
0
% ~/ImageMagick/7.0.5-1/bin/compare -metric PSNR rose: rose: NULL:  # やっと治った。
inf
```

- ImageMagick-6 系

不具合が存在するのは ImageMagick-6.9.5-4 (2016/7/30) から 6.9.10-12(2018/9/23) まで、
大丈夫なのは 6.9.10-13(2018/10/22)以降。

```shell:
% ~/ImageMagick/6.9.5-3/bin/compare -metric PSNR rose: rose: NULL:
inf
% ~/ImageMagick/6.9.5-4/bin/compare -metric PSNR rose: rose: NULL:  #不具合が入った
0
% ~/ImageMagick/6.9.7-10/bin/compare -metric PSNR rose: rose: NULL:
0
% ~/ImageMagick/6.9.8-0/bin/compare -metric PSNR rose: rose: NULL:  # あれれ、治ってない？
0
(略)
% ~/ImageMagick/6.9.10-12/bin/compare -metric PSNR rose: rose: NULL:
0
% ~/ImageMagick/6.9.10-13/bin/compare -metric PSNR rose: rose: NULL:  # やっと治った。
inf
```

7系から 6系へのバックポートが年単位で遅れてます。

# TODO (物足りない要素)

- 画像サイズが違う場合
- RGB 以外では？
- 各メトリックの紹介
- 対応するソースコードの説明 (GetImageDistortions とか)

# 参考URL


- http://www.imagemagick.org/Usage/compare/