2つの画像の違いを定量化するにはどうすればよいですか?
質問
私がやりたいことは次のとおりです。
定期的にウェブカメラで写真を撮っています。タイムラプスのようなものの並べ替え。ただし、実際に何も変更されていない場合、つまり、写真がほとんど同じように見える場合、 、最新のスナップショットを保存しません。
違いを定量化する方法があると思いますが、経験的にしきい値を決定する必要があります。
完璧ではなくシンプルを求めています。 私はpythonを使用しています。
解決
一般的なアイデア
オプション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
比較方法。 img1
およびimg2
はここの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 __name__ == "__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
PS compare.py スクリプト全体。
更新:関連するテクニック
質問はビデオシーケンスに関するものなので、フレームはほとんど同じである可能性が高く、何か異常なものを探しているので、関連する可能性のあるいくつかの代替アプローチに言及します。
- 背景の減算とセグメンテーション(前景オブジェクトを検出するため)
- スパースオプティカルフロー(動きを検出するため)
- 画像の代わりにヒストグラムまたはその他の統計を比較する
<!>#8220; Learning OpenCV <!>#8221;をご覧になることを強くお勧めします。本、第9章(画像部分とセグメンテーション)および10(追跡とモーション)。前者はバックグラウンド減算法を使用することを教え、後者はオプティカルフロー法に関する情報を提供します。すべてのメソッドはOpenCVライブラリに実装されています。 Pythonを使用する場合、OpenCV <!>#8805を使用することをお勧めします。 2.3、およびそのcv2
Pythonモジュール。
バックグラウンド減算の最も単純なバージョン:
- 平均値を学ぶ<!>#956;および標準偏差<!>#963;背景のピクセルごとに
- 現在のピクセル値を(<!>#956; -2 <!>#963;、<!>#956; +2 <!>#963;)または(<!>#956; -<!>#963;、<!>#956; + <!>#963;)
より高度なバージョンでは、すべてのピクセルの時系列を考慮し、非静的なシーン(木や草の動きなど)を処理します。
オプティカルフローの概念は、2つ以上のフレームを取得し、速度ベクトルをすべてのピクセル(高密度オプティカルフロー)またはそれらの一部(スパースオプティカルフロー)に割り当てることです。疎なオプティカルフローを推定するには、 Lucas-Kanadeメソッド(it OpenCVにも実装されています)。明らかに、多くのフローがある場合(速度フィールドの最大値を超える高い平均)、フレーム内で何かが動いており、後続の画像はより異なっています。
ヒストグラムを比較すると、連続するフレーム間の突然の変化を検出するのに役立ちます。このアプローチは、 Courbon et al、2010 :
で使用されました。連続したフレームの類似性。 2つの連続したフレーム間の距離が測定されます。高すぎる場合、2番目のフレームが破損しているため、画像が削除されていることを意味します。 Kullback <!>#8211; Leibler distance 、または相互エントロピー、 2つのフレームのヒストグラム:
p および q は、使用されるフレームのヒストグラムです。しきい値は0.2に固定されています。
他のヒント
簡単な解決策:
画像を jpeg としてエンコードし、ファイルサイズの大幅な変更を探します。
ビデオサムネイルと同様のものを実装しましたが、多くの成功とスケーラビリティがありました。
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の関数を使用して、ここで説明した他のものの近似を実装することもできます。
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) )
i1
およびi2
が2Dグレースケール画像配列であると仮定します。
試してみるべき些細なこと:
両方の画像を小さなサムネイル(64 x 64など)にリサンプリングし、特定のしきい値とピクセルごとにサムネイルを比較します。元の画像がほぼ同じ場合、リサンプリングされたサムネイルは非常に似ているか、まったく同じになります。この方法は、特に暗いシーンで発生する可能性のあるノイズを処理します。グレースケールにするとさらに良くなるかもしれません。
<!> quot;十分に異なる<!> quot;である場合の計算方法の問題に具体的に取り組んでいます。ピクセルを1つずつ減算する方法を理解できると思います。
最初に、何も変更しない画像の束を取り、キャプチャの変動、イメージングシステムのノイズ、JPEG圧縮アーチファクトのためにピクセルが変化する最大量を見つけます、および照明の瞬間的な変化。おそらく、何も動かない場合でも1ビットまたは2ビットの違いが予想されます。
次に<!> quot; real <!> quot;テストでは、次のような基準が必要です。
- 最大Pピクセルの差がE以下であれば同じです。
したがって、おそらく、E = 0.02、P = 1000の場合、それは(ほぼ)<!> quot; different <!> quot;であることを意味します。 1つのピクセルが5ユニット以上変化する場合(8ビット画像を想定)、または1000ピクセルを超えるピクセルにエラーがあった場合。
これは、主に良い<!> quot;トリアージ<!> quot;として意図されています。これ以上の検査を必要としないほど近い画像を迅速に識別する技術。 <!> quot; fail <!> quot;の画像次に、カメラがビットを横に振った場合や、照明の変化に対してより堅牢であった場合に誤検知を起こさない、より精巧な/高価な技術になります。
<!> quot; idiff <!というユーティリティを含むオープンソースプロジェクト OpenImageIO を実行しています。 > quot;違いをこのようなしきい値と比較します(実際にはさらに複雑です)。このソフトウェアを使用したくない場合でも、ソースを見て、どのようにそれを行ったかを確認することができます。これは商業的にかなり使用されており、このしきい値技術は、レンダリングおよび画像処理ソフトウェアのテストスイートを作成できるように開発されました。<!> quot; reference images <!> quot;プラットフォーム間でわずかな違いがある場合や、アルゴリズムに微調整を加えたため、<!> quot; match within tolerance <!> quot;操作。
与えられた答えのほとんどは、照明レベルを扱っていません。
比較を行う前に、最初に画像を標準の光レベルに正規化します。
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)
他の人が画像の類似性を比較するより強力な方法に興味がある場合、チュートリアルおよびウェブアプリを使用して、同様の画像を測定および視覚化するテンソルフロー。
職場で同様の問題がありました。画像変換エンドポイントを書き換えていたため、新しいバージョンが古いバージョンと同じまたはほぼ同じ出力を生成していることを確認したかったのです。だから私はこれを書いた:
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に投稿しています)。
類似画像を見つけるためのアルゴリズムの質問を見ましたか?提案を参照してください。
フレームのウェーブレット変換をお勧めします(Haar変換を使用してC拡張を記述しました)。次に、2つの画像間で最大の(比例して)ウェーブレットファクターのインデックスを比較すると、数値の類似性近似が得られます。
返信するには遅すぎることをおizeびしますが、似たようなことをしているので、なんとか貢献できると思いました。
OpenCVでは、テンプレートマッチングを使用できます。あなたが言ったようにウェブカメラを使用していると仮定します:
- 画像を単純化します(しきい値を設定しますか?)
- テンプレートマッチングを適用し、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
お役に立てば幸いです。
地球の移動距離は、まさにあなたが必要とするものです。 ただし、リアルタイムで実装するには abit 重いかもしれません。
2つの画像のマンハッタン距離の計算はどうですか。これにより、n * nの値が得られます。次に、行の平均などの操作を行ってn個の値に減らし、その関数を使用して1つの値を取得します。
私は三脚で同じカメラで撮影したjpg画像で多くの運を得ています (1)大幅に簡素化する(幅3000ピクセルから幅100ピクセル、またはそれ以下にするなど) (2)各jpg配列を単一のベクトルに平坦化する (3)連続画像を単純相関アルゴリズムでペアワイズ相関して相関係数を取得する (4)相関係数を二乗してr-square(つまり、ある画像の変動の割合が次の変動によって説明される)を得る (5)一般に、r-square <!> ltの場合、私のアプリケーションで。 0.9、2つの画像が異なり、その間に何かが起こったと言います。
これは私の実装では堅牢で高速です(Mathematica 7)
あなたが興味のある画像の部分をいじって、その小さな領域にすべての画像を切り取ることによってそれに焦点を合わせることは価値があります。
Pythonの使用方法はわかりませんが、相関も行うと確信していますか?
両方の画像のヒストグラムを計算し、 Bhattacharyya Coefficient を計算できます。これは非常に高速なアルゴリズムであり、CでopenCVを使用してクリケットビデオのショットの変更を検出するために使用しました
isk-daemon によるHaar Waveletsの実装方法を確認します。 imgdb C ++コードを使用して、画像の違いをオンザフライで計算できます:
isk-daemonは、コンテンツベースの(視覚的な)画像検索を任意の画像関連のWebサイトまたはソフトウェアに追加できるオープンソースデータベースサーバーです。
このテクノロジーにより、画像関連のWebサイトまたはソフトウェアのユーザーは、検索したい画像をウィジェットにスケッチし、Webサイトに最も類似した画像を返信するか、各画像詳細ページでより類似した写真を要求することができます。
同じ問題があり、枕のImageChopsを使用して2つの同じサイズの画像を比較して、白黒の差分画像を作成し、ヒストグラム値を合計する単純なpythonモジュールを作成しました。
このスコアを直接取得するか、完全な黒と白の差分と比較したパーセンテージ値を取得できます。
これには、単純なis_equal関数も含まれており、画像パスが等しい(およびそれを含む)ファジーしきい値を提供する可能性があります。
このアプローチはあまり精巧ではありませんが、同じ問題に苦しんでいる他の人々に役立つかもしれません。
もう少し原理的なアプローチは、グローバル記述子を使用して、GISTやCENTRISTなどの画像を比較することです。 こちらで説明されているハッシュ関数同様のソリューションを提供します。
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 __name__ == '__main__':
main()
2つの画像の輝度間のユークリッド距離(つまり、sqrt(差の二乗の合計、ピクセルごと))を単純に計算し、これが経験的しきい値を下回った場合に等しいと見なすことができると思います。そして、C関数をラップすることをお勧めします。
2つの画像がどのように見えるか/どの程度見えるかを評価するための多くのメトリックがあります。
ここではコードには触れません。技術的な問題ではなく、科学的な問題だと思うからです。
一般に、質問は画像に対する人間の知覚に関連しているため、各アルゴリズムは人間の視覚システム特性をサポートしています。
クラシックアプローチは次のとおりです。
Visible Difference Predictor:画像の忠実度を評価するアルゴリズム( https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000 /Visible-differences-predictor---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(画像品質評価:エラーの可視性から構造的類似性まで)は計算が最も簡単であり、別の論文<!> quot;勾配類似性に基づく画像評価<! > quot; ( https://www.semanticscholar .org / paper / Image-Quality-Assessment-Based-On-Gradient-Liu-Lin / 2b819bef80c02d5d4cb56f27b202535e119df988 )。
他にも多くのアプローチがあります。 Google Scholarを見て、<!> quot; visual difference <!> quot;、<!> quot; image quality assessment <!> quot;などのようなものを検索してください。 。