ImageMagickで画像の同一性を評価する

撮り貯めた写真が多くて、Google フォトのバックアップが降ってきたものや、スマホやデジカメから転送した写真や、デジカメからスマホ経由で Google フォトにアップロードされたものやら、ブログや SNS 向けにリサイズしたものやら、とにかく大量の写真がある上に、それぞれがいろんな場所で重複しているケースが多く整理が大変です。同じ写真は削除したいところ。

10年前に「ImageMagickを使ったハミング距離による画像の同一性チェック」という記事を書きました。そこでは XBM にして無駄に画像をこねくりまわした形跡がありますが、ImageMagick を使っているのであれば ImageMagick で2つの画像の差分を取る方が早いです。

今回は、実は10年前にブログ記事にしたことを忘れていて、下記のスクリプトを書いていました。

#!/usr/bin/ruby

def diffscore(a, b, size: 256)

  diffimg = "/tmp/diff_#{$$}.png"

  `convert \
    \\( "#{a}" -resize #{size}x#{size}! -write mpr:a +delete \\) \
    \\( "#{b}" -resize #{size}x#{size}! -write mpr:b +delete \\) \
    mpr:a mpr:b -compose difference -composite #{diffimg}`

  score = `identify -verbose #{diffimg}`.
    split(/\n/).
    select {|item| item =~ /^ *entropy: */ }.
    map {|item| item.gsub(/[^0-9.]/, '') }.
    map {|item| item.to_f }.last

  File.delete(diffimg)
  return score
end

p diffscore(*ARGV)

diffscore メソッドは、引数に与えられた2つの画像ファイルを同一のサイズにリサイズ後、差分画像を出力します。convert のオプション中に複数の mpr を使えることを知らなくて、今回は書きながら勉強になりました。丸括弧 () を駆使したら、ちゃんと ffmpegcomprex_filter みたいに処理を構造化できるんですねー。

で、差分画像に含まれている画素の統計情報を identify で取得して、エントロピーの値をスコアとして返します。この値が1に近づくほど差分画像にはっきりと像が出ていて、逆に0に近づくほど、差分箇所が少ないと考えることができます。

完全同一の画像を与えると、差分画像は完全に真っ黒になります。

idetify の出力

  Image statistics:
    Overall:
      min: 0  (0)
      max: 255 (1)
      mean: 146.062 (0.572792)
      standard deviation: 54.4884 (0.21368)
      kurtosis: -0.770926
      skewness: -0.403949
      entropy: 0.959075

それぞれの項目の意味は次のとおり。

項目の意味

最小値 (min):

画像内のピクセル値の最小値です。括弧内の値は、正規化された値(0 から 1 の範囲)を示します。

最大値 (max):

画像内のピクセル値の最大値です。括弧内の値は、正規化された値(0 から 1 の範囲)を示します。

平均値 (mean):

画像内のピクセル値の平均値です。括弧内の値は、正規化された値(0 から 1 の範囲)を示します。

標準偏差 (standard deviation):

画像内のピクセル値のばらつき具合を示す値です。括弧内の値は、正規化された値(0 から 1 の範囲)を示します。

尖度 (kurtosis):

ピクセル値の分布の尖り具合を示す値です。尖度が大きいほど、平均値付近にピクセル値が集中していることを示します。

歪度 (skewness):

ピクセル値の分布の非対称性を示す値です。歪度が正の値の場合は右に歪んだ分布、負の値の場合は左に歪んだ分布であることを示します。

エントロピー (entropy):

画像内のピクセル値のランダムさを示す値です。エントロピーが大きいほど、画像内の情報量が多いことを示します。

課題

完全に同一であったり、同一の画像をリサイズしたもの、さらに JPEG の圧縮品質の差異による劣化部分の混入などがある画像はそこそこ良いスコアが得られるものの、次のケースでは使えません。

  • 同一の画像でも、片方の色相・彩度・明度のいずれかが変更されたもの。全体的に差分が出てしまう。(極端でなければ尖度で判断できそうだが…)
  • 一部をトリミングしたもの、外枠などが追加されたもの。

これらの課題をクリアするには、いよいよ画像の中に描かれたモノを正しく解釈して判断する必要がありそうです。つまり、AIによる同一性判定ですね。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

This site uses Akismet to reduce spam. Learn how your comment data is processed.