web-dev-qa-db-ja.com

2つの画像の違いを定量化するにはどうすればよいですか?

これが私がやりたいことです:

定期的にウェブカメラで写真を撮っています。タイムラプスのようなものの並べ替え。ただし、実際に何も変更されていない場合、つまり、画像がほとんどlooksと同じ場合、最新のスナップショットを保存したくありません。

違いを定量化する何らかの方法があると想像し、経験的にしきい値を決定する必要があります。

私は完璧ではなく、シンプルさを探しています。私はpythonを使用しています。

164
carrier

一般的なアイデア

オプション1:両方の画像を配列(scipy.misc.imread)としてロードし、要素ごと(ピクセルごと)の差を計算します。差のノルムを計算します。

オプション2:両方の画像を読み込みます。それらのそれぞれについていくつかの特徴ベクトルを計算します(ヒストグラムなど)。画像ではなく特徴ベクトル間の距離を計算します。

ただし、最初に行う決定がいくつかあります。

ご質問

最初にこれらの質問に答えるべきです:

  • 画像の形状と寸法は同じですか?

    そうでない場合は、サイズ変更またはトリミングが必要になる場合があります。 PILライブラリは、Pythonでそれを行うのに役立ちます。

    同じ設定と同じデバイスで撮影された場合、おそらく同じです。

  • 画像は適切に調整されていますか?

    そうでない場合は、最初に相互相関を実行して、最初に最適なアライメントを見つけることができます。 SciPyにはそれを行う機能があります。

    カメラとシーンが静止している場合、画像は適切に調整されている可能性があります。

  • 画像の露出は常に同じですか? (明度/コントラストは同じですか?)

    そうでない場合は、 正規化するため イメージが必要な場合があります。

    しかし、注意してください、状況によってはこれは良いことよりも悪いことをするかもしれません。たとえば、暗い背景に単一の明るいピクセルがあると、正規化された画像は大きく異なります。

  • 色情報は重要ですか?

    色の変化に注目したい場合は、グレースケール画像のようなスカラー値ではなく、ポイントごとの色値のベクトルがあります。このようなコードを書くときは、さらに注意が必要です。

  • 画像に明確なエッジがありますか?彼らは動きそうですか?

    はいの場合、最初にエッジ検出アルゴリズムを適用し(たとえば、SobelまたはPrewitt変換で勾配を計算し、いくつかのしきい値を適用)、最初の画像のエッジを2番目の画像のエッジと比較できます.

  • 画像にノイズはありますか?

    すべてのセンサーは、ある程度のノイズで画像を汚染します。低コストのセンサーには、より多くのノイズがあります。画像を比較する前に、ノイズリダクションを適用することもできます。ここでは、ぼかしが最も簡単な(ただし、最良ではない)アプローチです。

  • どんな変化に気づきたいですか?

    これは、画像間の違いに使用する標準の選択に影響する場合があります。

    マンハッタンのノルム(絶対値の合計)またはゼロのノルム(ゼロに等しくない要素の数)を使用して、画像がどの程度変化したかを測定することを検討してください。前者は画像がどれだけずれているかを示し、後者はピクセル数の違いのみを示します。

あなたの画像は、サイズと形が同じで、露出が異なる可能性があります。簡単にするために、それらがカラー(RGB)画像であってもグレースケールに変換します。

次のインポートが必要になります。

import sys

from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average

主な機能、2つの画像の読み取り、グレースケールへの変換、結果の比較と印刷:

def main():
    file1, file2 = sys.argv[1:1+2]
    # read images as 2D arrays (convert to grayscale for simplicity)
    img1 = to_grayscale(imread(file1).astype(float))
    img2 = to_grayscale(imread(file2).astype(float))
    # compare
    n_m, n_0 = compare_images(img1, img2)
    print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
    print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size

比較する方法。 img1img2はここの2D SciPy配列です:

def compare_images(img1, img2):
    # normalize to compensate for exposure difference, this may be unnecessary
    # consider disabling it
    img1 = normalize(img1)
    img2 = normalize(img2)
    # calculate the difference and its norms
    diff = img1 - img2  # elementwise for scipy arrays
    m_norm = sum(abs(diff))  # Manhattan norm
    z_norm = norm(diff.ravel(), 0)  # Zero norm
    return (m_norm, z_norm)

ファイルがカラー画像の場合、imreadは、強度を取得するために3D配列、平均RGBチャネル(最後の配列軸)を返します。グレースケール画像に対してそれを行う必要はありません(例:.pgm):

def to_grayscale(arr):
    "If arr is a color image (3D array), convert it to grayscale (2D array)."
    if len(arr.shape) == 3:
        return average(arr, -1)  # average over the last axis (color channels)
    else:
        return arr

正規化は簡単です。[0,255]の代わりに[0,1]に正規化することもできます。 arrはここではSciPy配列であるため、すべての操作は要素ごとに行われます。

def normalize(arr):
    rng = arr.max()-arr.min()
    amin = arr.min()
    return (arr-amin)*255/rng

main関数を実行します。

if __== "__main__":
    main()

これをすべてスクリプトに入れて、2つのイメージに対して実行できます。イメージをそれ自体と比較した場合、違いはありません。

$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0

画像をぼかして元の画像と比較すると、いくつかの違いがあります。

$ python compare.py one.jpg one-blurred.jpg 
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0

追伸全体 compare.py スクリプト。

更新:関連するテクニック

質問はフレームがほぼ同じである可能性が高いビデオシーケンスに関するものであり、異常なものを探しているので、関連する可能性のあるいくつかの代替アプローチに言及したいと思います。

  • バックグラウンドの減算とセグメンテーション(前景オブジェクトを検出するため)
  • スパースオプティカルフロー(動きを検出するため)
  • 画像の代わりにヒストグラムまたは他の統計を比較する

「Learning OpenCV」ブックの第9章(画像部分とセグメンテーション)と10(追跡とモーション)をご覧になることを強くお勧めします。前者はバックグラウンド減算法を使用することを教え、後者はオプティカルフロー法に関する情報を提供します。すべてのメソッドはOpenCVライブラリに実装されています。 Pythonを使用する場合は、OpenCV≥2.3およびそのcv2 Pythonモジュールを使用することをお勧めします。

バックグラウンド減算の最も単純なバージョン:

  • 背景のすべてのピクセルの平均値μと標準偏差σを学習する
  • 現在のピクセル値を(μ-2σ、μ+2σ)または(μ-σ、μ+σ)の範囲と比較する

より高度なバージョンでは、すべてのピクセルの時系列を考慮し、非静的なシーン(木や草の動きなど)を処理します。

オプティカルフローのアイデアは、2つ以上のフレームを取得し、速度ベクトルをすべてのピクセル(高密度オプティカルフロー)またはそれらの一部(スパースオプティカルフロー)に割り当てることです。スパースオプティカルフローを推定するには、 Lucas-Kanade method を使用できます(OpenCVでも実装されています)。明らかに、多くのフロー(速度フィールドの最大値に対する高い平均)がある場合、フレーム内で何かが動いており、後続の画像はより異なっています。

ヒストグラムを比較すると、連続するフレーム間の突然の変化を検出するのに役立ちます。このアプローチは Courbon et al、201 で使用されました。

連続したフレームの類似性。2つの連続したフレーム間の距離が測定されます。高すぎる場合、2番目のフレームが破損しているため、画像が削除されていることを意味します。 Kullback–Leibler distance 、または相互エントロピー、2つのフレームのヒストグラム:

$$ d(p,q) = \sum_i p(i) \log (p(i)/q(i)) $$

ここで、pおよびqは、使用されるフレームのヒストグラムです。しきい値は0.2に固定されています。

249
sastanin

簡単な解決策:

イメージをjpegとしてエンコードし、filesizeの大幅な変更を探します。

ビデオサムネイルで同様の機能を実装しましたが、多くの成功とスケーラビリティがありました。

75
keparo

PIL の関数を使用して2つの画像を比較できます。

import Image
import ImageChops

im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")

diff = ImageChops.difference(im2, im1)

Diffオブジェクトは、すべてのピクセルが、最初の画像から2番目の画像のピクセルの色値を減算した結果の画像です。差分画像を使用すると、いくつかのことができます。最も簡単なものは、diff.getbbox()関数です。 2つの画像間のすべての変更を含む最小の長方形が表示されます。

PILの関数を使用して、ここで説明した他のものの近似を実装することもできます。

55
elifiner

2つの一般的で比較的単純な方法は、(a)すでに提案されているユークリッド距離、または(b)正規化された相互相関です。正規化された相互相関は、単純な相互相関よりも照明の変化に対して顕著に堅牢になる傾向があります。ウィキペディアでは、 正規化相互相関 の式を提供しています。より洗練された方法もありますが、かなり多くの作業が必要です。

Numpyのような構文を使用して、

 dist_euclidean = sqrt(sum((i1-i2)^ 2))/ i1.size 
 
 dist_manhattan = sum(abs(i1-i2))/ i1.size 
 
 dist_ncc = sum((i1-mean(i1))*(i2-mean(i2)))/(
(i1.size-1)* stdev(i1) * stdev(i2))

i1i2が2Dグレースケール画像配列であると仮定します。

19
Mr Fooz

簡単なこと:

両方の画像を小さなサムネイル(例:64 x 64)にリサンプルし、サムネイルをピクセルごとに特定のしきい値と比較します。元の画像がほぼ同じ場合、リサンプリングされたサムネイルは非常に似ているか、まったく同じです。この方法は、特に暗いシーンで発生する可能性のあるノイズを処理します。グレースケールにするとさらに良いかもしれません。

13
Ates Goral

それらが「十分に異なる」場合の計算方法の問題に具体的に取り組んでいます。ピクセルを1つずつ減算する方法を理解できると思います。

最初に、nothingが変更された画像の束を取得し、キャプチャの変化、ノイズのためにピクセルが変化する最大量を見つけますイメージングシステム、JPEG圧縮アーチファクト、および照明の瞬間的な変化。おそらく、何も動かない場合でも、1ビットまたは2ビットの違いが予想されます。

次に、「実際の」テストでは、次のような基準が必要です。

  • 最大Pピクセルの差がE以下の場合も同じです。

したがって、おそらく、E = 0.02、P = 1000の場合、1つのピクセルが5単位を超えて変化する場合(8ビット画像を想定)、または1000を超える場合は(ほぼ)「異なる」ことを意味します。ピクセルにはまったくエラーがありました。

これは主に、さらなる検査を必要としないほど十分に近い画像を迅速に識別するための優れた「トリアージ」技術として意図されています。 「失敗」した画像は、たとえば、カメラがビットを横に振った場合や、照明の変化に対してより堅牢だった場合に、誤検知を起こさない、より精巧で高価な手法になります。

オープンソースプロジェクト OpenImageIO を実行します。このプロジェクトには、差異をこのようなしきい値と比較する「idiff」というユーティリティが含まれています(実際にはさらに複雑です)。このソフトウェアを使用したくない場合でも、ソースを見て、どのようにそれを行ったかを確認することができます。これは商業的にかなり使用されており、このしきい値技術は、プラットフォーム間でわずかな違いがあるか、または微調整を行った「参照画像」を含むレンダリングおよび画像処理ソフトウェアのテストスイートを作成できるように開発されましたアルゴリズムなので、「許容範囲内で一致」操作が必要でした。

7
Larry Gritz

与えられた答えのほとんどは、照明レベルを扱っていません。

比較を行う前に、まず画像を標準の光レベルに正規化します。

5
Loren Pechtel

2つの画像間の類似性を測定するもう1つの素敵で簡単な方法:

import sys
from skimage.measure import compare_ssim
from skimage.transform import resize
from scipy.ndimage import imread

# get two images - resize both to 1024 x 1024
img_a = resize(imread(sys.argv[1]), (2**10, 2**10))
img_b = resize(imread(sys.argv[2]), (2**10, 2**10))

# score: {-1:1} measure of the structural similarity between the images
score, diff = compare_ssim(img_a, img_b, full=True)
print(score)

他の人が画像の類似性を比較するより強力な方法に興味がある場合、Tensorflowを使用して同様の画像を測定および視覚化するために tutorial とweb app をまとめました。

4
duhaime

職場で同様の問題があり、イメージ変換エンドポイントを書き換えていたため、新しいバージョンが古いバージョンと同じまたはほぼ同じ出力を生成しているかどうかを確認したかったのです。だから私はこれを書いた:

https://github.com/nicolashahn/diffimg

同じサイズの画像で動作し、ピクセルごとのレベルで、各チャンネルの値の差を測定します:R、G、B(、A)、それらのチャンネルの平均差を取り、その差を平均しますすべてのピクセル、および比率を返します。

たとえば、10x10の白いピクセルの画像で、同じ画像が1つのピクセルが赤に変わった場合、そのピクセルの差は1/3または0.33 ...(RGB 0,0,0 vs 255,0,0 )および他のすべてのピクセルは0です。合計100ピクセルの場合、0.33 .../100 =画像の差は約0.33%です。

これはOPのプロジェクトには完璧に機能すると信じています(これは非常に古い投稿ですが、Pythonで画像を比較したい将来のStackOverflowersに投稿していることに気づきました)。

4
nicolashahn

同様の画像を見つけるためのアルゴリズム の質問を見ましたか?提案を参照してください。

フレームのウェーブレット変換をお勧めします(Haar変換を使用して、そのためのC拡張を記述しました)。次に、2つの画像間で最大の(比例して)ウェーブレットファクターのインデックスを比較すると、数値の類似度近似が得られます。

3
tzot

返信するには遅すぎることをおizeびしますが、似たようなことをしているので、なんとか貢献できると思いました。

たぶん、OpenCVでテンプレートマッチングを使用できます。あなたが言ったようにウェブカメラを使用していると仮定します:

  1. 画像を単純化する(しきい値を設定するのか?)
  2. テンプレートマッチングを適用し、minMaxLocでmax_valを確認します

ヒント:max_val(または使用する方法に応じてmin_val)は、大きな数字を表示します。パーセンテージの差を取得するには、同じ画像でテンプレートマッチングを使用します。結果は100%になります。

例示する擬似コード:

previous_screenshot = ...
current_screenshot = ...

# simplify both images somehow

# get the 100% corresponding value
res = matchTemplate(previous_screenshot, previous_screenshot, TM_CCOEFF)
_, hundred_p_val, _, _ = minMaxLoc(res)

# hundred_p_val is now the 100%

res = matchTemplate(previous_screenshot, current_screenshot, TM_CCOEFF)
_, max_val, _, _ = minMaxLoc(res)

difference_percentage = max_val / hundred_p_val

# the tolerance is now up to you

それが役に立てば幸い。

2
zanfranceschi

地球を動かす人の距離 は、まさにあなたが必要とするものかもしれません。ただし、リアルタイムで実装するのは少々重いかもしれません。

1
shoosh

2つの画像の マンハッタン距離 の計算についてはどうですか。これにより、n * nの値が得られます。次に、行平均のようなものを使用してn個の値に減らし、その関数を使用して1つの値を取得します。

1
Tobias
import os
from PIL import Image
from PIL import ImageFile
import imagehash

#just use to the size diferent picture
def compare_image(img_file1, img_file2):
    if img_file1 == img_file2:
        return True
    fp1 = open(img_file1, 'rb')
    fp2 = open(img_file2, 'rb')

    img1 = Image.open(fp1)
    img2 = Image.open(fp2)

    ImageFile.LOAD_TRUNCATED_IMAGES = True
    b = img1 == img2

    fp1.close()
    fp2.close()

    return b





#through picturu hash to compare
def get_hash_dict(dir):
    hash_dict = {}
    image_quantity = 0
    for _, _, files in os.walk(dir):
        for i, fileName in enumerate(files):
            with open(dir + fileName, 'rb') as fp:
                hash_dict[dir + fileName] = imagehash.average_hash(Image.open(fp))
                image_quantity += 1

    return hash_dict, image_quantity

def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
    recommend to use
    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_1 = None
    hash_2 = None
    with open(image_file_name_1, 'rb') as fp:
        hash_1 = imagehash.average_hash(Image.open(fp))
    with open(image_file_name_2, 'rb') as fp:
        hash_2 = imagehash.average_hash(Image.open(fp))
    dif = hash_1 - hash_2
    if dif < 0:
        dif = -dif
    if dif <= max_dif:
        return True
    else:
        return False


def compare_image_dir_with_hash(dir_1, dir_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.

    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_dict_1, image_quantity_1 = get_hash_dict(dir_1)
    hash_dict_2, image_quantity_2 = get_hash_dict(dir_2)

    if image_quantity_1 > image_quantity_2:
        tmp = image_quantity_1
        image_quantity_1 = image_quantity_2
        image_quantity_2 = tmp

        tmp = hash_dict_1
        hash_dict_1 = hash_dict_2
        hash_dict_2 = tmp

    result_dict = {}

    for k in hash_dict_1.keys():
        result_dict[k] = None

    for dif_i in range(0, max_dif + 1):
        have_none = False

        for k_1 in result_dict.keys():
            if result_dict.get(k_1) is None:
                have_none = True

        if not have_none:
            return result_dict

        for k_1, v_1 in hash_dict_1.items():
            for k_2, v_2 in hash_dict_2.items():
                sub = (v_1 - v_2)
                if sub < 0:
                    sub = -sub
                if sub == dif_i and result_dict.get(k_1) is None:
                    result_dict[k_1] = k_2
                    break
    return result_dict


def main():
    print(compare_image('image1\\815.jpg', 'image2\\5.jpg'))
    print(compare_image_with_hash('image1\\815.jpg', 'image2\\5.jpg', 6))
    r = compare_image_dir_with_hash('image1\\', image2\\', 10)
    for k in r.keys():
        print(k, r.get(k))


if __== '__main__':
    main()
  • 出力:

    False
    True
    image2\5.jpg image1\815.jpg
    image2\6.jpg image1\819.jpg
    image2\7.jpg image1\900.jpg
    image2\8.jpg image1\998.jpg
    image2\9.jpg image1\1012.jpg

  • サンプル画像:

    • 815.jpg
      815.jpg

    • 5.jpg
      5.jpg

1
admin

Haar Waveletsが isk-daemon によってどのように実装されているかを確認してください。 imgdb C++コードを使用して、画像の違いをオンザフライで計算できます。

isk-daemonは、画像ベースのWebサイトまたはソフトウェアにコンテンツベースの(視覚的な)画像検索を追加できるオープンソースのデータベースサーバーです。

このテクノロジーにより、画像関連のウェブサイトまたはソフトウェアのユーザーは、検索したい画像をウィジェットにスケッチし、ウェブサイトに最も類似した画像を返信するか、各画像詳細ページでより類似した写真を単にリクエストすることができます。

1
Ricardo Cabral

ややより原則的なアプローチは、GistやCENTRISTなどの画像を比較するためにグローバル記述子を使用することです。 here で説明されているハッシュ関数も同様のソリューションを提供します。

1
Felix Goldberg

同じ問題があり、枕のImageChopsを使用して2つの同じサイズの画像を比較して白黒の差分画像を作成し、ヒストグラム値を合計する簡単なpythonモジュールを作成しました。

このスコアを直接取得するか、完全な黒と白の差分と比較したパーセンテージ値を取得できます。

また、単純なis_equal関数が含まれており、画像パスが等しい(およびそれを含む)ファジーしきい値を提供する可能性があります。

このアプローチはあまり精巧ではありませんが、同じ問題に苦しんでいる他の人々に役立つかもしれません。

https://pypi.python.org/pypi/imgcompare/

1
datenhahn

両方の画像のヒストグラムを計算してから Bhattacharyya Coefficient を計算できます。これは非常に高速なアルゴリズムであり、クリケットビデオ(CでopenCVを使用)でショットの変化を検出するために使用しました

1
vishalv2050

私は、(1)大幅に単純化する(幅3000ピクセルから幅100ピクセルまたはそれ以下にするなど)(2)各jpg配列を単一に平坦化することにより、三脚上で同じカメラで撮影したjpg画像で多くの運を得てきましたベクトル(3)相関係数を取得するために単純な相関アルゴリズムで連続画像をペアワイズ相関する(4)r平方を取得する相関係数を二乗する(つまり、1つの画像の変動の割合は次の変動によって説明される)(5) r-square <0.9の場合、2つの画像が異なり、その間に何かが起こったと言います。

私の実装ではこれは堅牢で高速です(Mathematica 7)

興味のある画像の一部をいじって、その小さな領域にすべての画像をトリミングすることで、それに焦点を合わせてみる価値があります。そうしないと、カメラから離れていますが、重要な変更を見逃します。

Pythonの使用方法はわかりませんが、相関関係もあると確信していますか?

1
Roman Dial

2つの画像の輝度間のユークリッド距離(すなわち、sqrt(差の二乗の合計、ピクセルごと))を単純に計算し、これが何らかの経験的しきい値を下回った場合に等しいと見なすことができると思います。そして、C関数をラップすることをお勧めします。

0

2つの画像がどのように見えるか/どの程度見えるかを評価するための多くのメトリックがあります。

技術的な問題ではなく、科学的な問題であると思うので、ここではコードには触れません。

一般的に、質問は画像に対する人間の知覚に関連しているため、各アルゴリズムは人間の視覚システム特性をサポートしています。

従来のアプローチは次のとおりです。

可視差予測:画像忠実度の評価のためのアルゴリズム( https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000/Visible-differences-predictor--an-algorithm- for-the-assessment-of/10.1117/12.135952.short?SSO = 1

画質評価:エラーの可視性から構造の類似性まで( http://www.cns.nyu.edu/pub/lcv/wang03-reprint.pdf

FSIM:画像品質評価の機能類似性インデックス( https://www4.comp.polyu.edu.hk/~cslzhang/IQA/TIP_IQA_FSIM.pdf

中でも、SSIM(画質評価:エラーの可視性から構造的類似性まで)は計算が最も簡単であり、オーバーヘッドも小さいことが、別の論文「勾配類似性に基づく画質評価」( https:/ /www.semanticscholar.org/paper/Image-Quality-Assessment-Based-on-Gradient-Liu-Lin/2b819bef80c02d5d4cb56f27b202535e119df988 )。

他にも多くのアプローチがあります。 Google Scholarをご覧になり、アートに興味がある/本当に関心がある場合は、「視覚的な違い」、「画質評価」などを検索してください。

0
cyfex

平均二乗誤差を計算することにより、numpyを使用した簡単で高速なソリューションがあります。

before = np.array(get_picture())
while True:
    now = np.array(get_picture())
    MSE = np.mean((now - before)**2)

    if  MSE > threshold:
        break

    before = now
0
Arian Soltani