rmagick(ImageMagick)を使って画像の差分を取得する

画像差分が取りたい

修正前、修正後の結果を画像比較できるとテストの安心感が高まりそうという気持ちから、rmagickの機能を調べてみました。

参考にしたもの

確認環境

  • ruby 2.2.3
  • rmagick 2.15.4

rmagickのインストール方法

Githubのwikiを参照下さい。

画像取得について

Seleniumで画像キャプチャを取ることを想定していますが、今回は画像取得については触れずに別で書こうと思います

画像の差分を取る

rmagickで画像を読み込む

まずはrmagickで画像を読み込むところから。

require 'rmagick'

img = Magick::Image.read('./sample.jpg').first

Magick::Image.readの結果が配列で返ってきますので、firstとしています。

画像がどのくらい違うか確認する:Magick::Image#difference

Magick::Image#differenceというメソッドがあるので、これを使うと同じ画像かどうかが分かるようです。リファレンスの説明にはCall the IsImagesEqual function.という記述がありました。

使ってみる

require 'rmagick'

img = Magick::Image.read('./sample.jpg').first

p img.difference(img)
# => [0.0, 0.0, 0.0]

同じだと0で返ってくるようです。

返ってくる値は何なのか

リファレンスを見ると mean_error_per_pixel,normalized_mean_error,normalized_maximum_errorの3つの値だと書いてありました。

画像系に詳しい人だともしかしたらこれで分かるのかもしれませんが、私にはよく分からず。

ImageMagick側を見れば分かるかな、ということでImageMagickをキーワードにググってみたところ、このページを発見。

曰く、

mean_error_per_pixel:
  This value is the mean error for any single pixel in the image.
normalized_mean_error:
  This value is the normalized mean quantization error for any single pixel in the image.
  This distance measure is normalized to a range between 0 and 1.
  It is independent of the range of red, green, and blue values in the image.
normalized_maximum_error:
  Thsi value is the normalized maximum quantization error for any single pixel in the image.
  This distance measure is normalized to a range between 0 and 1.
  It is independent of the range of red, green, and blue values in your image.

ということみたいです。

違いがあるかないかという点では0であるかを見れば良さそうで、どのくらい違うかはこの値を見て判断することが出来そう(出来そうだけどどうやったら出来るかはいまいち分からない)

実際どんな感じになるのか

適当な画像を用意して比較してみました

全く同じファイル

上述の通り [0.0, 0.0, 0.0]

同じファイルをサイズを変えて比較してみる
  • 640×480のGIFファイルを用意して320×240にサイズ変更したものと比較すると

[521.8944702148438, 0.0070885103517538675, 1.0]

ファイルの内容をちょっと変えてみる
  • 元々のファイルに「テスト」みたいな文字を加えたものと比較すると

[37.7886962890625, 0.0004698213267336715, 1.0]

  • 「テスト」の文字をとても小さくすると

[5.609326362609863, 5.188514493355332e-05, 0.9294117647058824]

  • 「テスト」の文字をとても大きくすると

[5286.41259765625, 0.07936659467536895, 1.0]

  • 背景色の色味をちょっと変えてみる

[8371.1787109375, 0.021862748084959475, 0.17254901960784313]

  • 背景色の色味を強めに変えてみる

[30593.5625, 0.29990951254957343, 0.6313725490196078]

とりあえずの結論

はっきりは分からないのと、テストデータも手元のツールで簡単に使っただけなので、正確性があるかないが大変に怪しいですが、左端の値が一番感覚と近いかなぁという印象(違いが小さいと値も小さい)。本当は閾値みたいに使えたらと思ったのですが、ちゃんと使うには、今のところ値の意味を理解できていないがために難しそうです。もちろん、閾値ではなくて、「0じゃなかったら違う画像!」みたいには使えると思います。

差分を画像として確認する:Magick::Image#composite または Magick::Image#composite!

実際に、DIFFの結果が見たい、という場合にはMagick::Image#compositeまたはMagick::Image#composite!が使えます。DIFFを意識した重ね合わせができて、同じところは黒くなるので、黒くないところが違うところ、という形で確認できます。

使ってみる

require 'rmagick'

img_a = Magick::Image.read('./sample-a.jpg').first
img_b = Magick::Image.read('./sample-b.jpg').first

img_a.composite(img_b,Magick::NorthWestGravity,Magick::DifferenceCompositeOp).write('./image_diff.jpg')

compositeメソッドについて

  • 最初の引数は、合成する画像データです

  • 2つ目の引数は、画像を合成する位置の指定です。0,0とかでも良いですし、左上からであればMagick::NorthWestGravity、中央からであればMagick::CenterGravityのようにモジュール内に定義してある定数を使うこともできるようです

  • 3つ目の引数は、重ね方の指定です。Magick::DifferenceCompositeOpの指定をすると、同じところが黒くなって重なります。DIFFでなければ、別の値を指定して画像の合成ができるようです

ruby gem を作りました

genius.hateblo.jp

インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針

インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針

Seleniumデザインパターン & ベストプラクティス

Seleniumデザインパターン & ベストプラクティス