回答:
オプション1:両方の画像を配列として読み込み(scipy.misc.imread
)、要素ごとの(ピクセルごとの)差を計算します。差のノルムを計算します。
オプション2:両方の画像を読み込みます。それぞれの特徴ベクトルを(ヒストグラムのように)計算します。画像ではなく特徴ベクトル間の距離を計算します。
ただし、最初に行うべき決定がいくつかあります。
最初にこれらの質問に答える必要があります。
画像の形状と寸法は同じですか?
そうでない場合は、サイズを変更するか、トリミングする必要があります。PILライブラリは、Pythonでそれを行うのに役立ちます。
同じ設定と同じデバイスで撮影した場合、おそらく同じです。
画像は適切に配置されていますか?
そうでない場合は、最初に相互相関を実行して、最初に最適な配置を見つけることができます。SciPyにはそれを行う機能があります。
カメラとシーンが静止している場合は、画像の位置が合っている可能性があります。
画像の露出は常に同じですか?(明るさ/コントラストは同じですか?)
そうでない場合は、画像を正規化することができます。
しかし、注意してください。状況によっては、これは良いことよりも悪いことをするかもしれません。たとえば、暗い背景に1つの明るいピクセルがあると、正規化された画像が大きく異なります。
色情報は重要ですか?
色の変化に気づきたい場合は、グレースケール画像のようなスカラー値ではなく、ポイントごとの色値のベクトルを使用します。そのようなコードを書くときはもっと注意が必要です。
画像にはっきりとしたエッジはありますか?彼らは動きそうですか?
はいの場合、最初にエッジ検出アルゴリズムを適用し(例: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 Entire compare.pyスクリプト。
質問はビデオシーケンスに関するものであり、フレームはほとんど同じである可能性が高く、異常なものを探しているため、関連する可能性のあるいくつかの代替アプローチについて説明します。
「Learning OpenCV」の本の第9章(画像のパーツとセグメンテーション)と10(トラッキングとモーション)をご覧になることを強くお勧めします。前者はバックグラウンド減算法を使用することを教え、後者はオプティカルフロー法に関するいくつかの情報を提供します。すべてのメソッドはOpenCVライブラリに実装されています。Pythonを使用している場合は、OpenCV≥2.3とそのcv2
Pythonモジュールを使用することをお勧めします。
背景減算の最も単純なバージョン:
より高度なバージョンでは、すべてのピクセルの時系列が考慮され、静的なシーン(動く木や草など)が処理されます。
オプティカルフローの考え方は、2つ以上のフレームを取り、速度ベクトルをすべてのピクセル(密なオプティカルフロー)またはそれらの一部(スパースオプティカルフロー)に割り当てることです。スパースオプティカルフローを推定するには、Lucas-Kanadeメソッドを使用できます(これはOpenCVにも実装されています)。明らかに、大量のフローがある場合(速度フィールドの最大値より高い平均)、フレーム内で何かが動いているため、後続の画像はより異なります。
ヒストグラムの比較は、連続するフレーム間の突然の変化を検出するのに役立ちます。このアプローチは、Courbon et al、2010で使用されました。
連続するフレームの類似性。2つの連続するフレーム間の距離が測定されます。高すぎる場合は、2番目のフレームが破損しているため、画像が削除されています。2つのフレームのヒストグラムのカルバックライバー距離または相互エントロピー:
ここで、pとqは、使用されるフレームのヒストグラムです。しきい値は0.2に固定されています。
RuntimeWarning: invalid value encountered in double_scalars
ライン44(上のreturn (arr-amin)*255/rng
)とValueError: array must not contain infs or NaNs
ライン30(上z_norm = norm(diff.ravel(), 0)
)
rng
ゼロの場合。小切手とセットを追加するだけrng = 1
簡単な解決策:
画像をjpegとしてエンコードし、ファイルサイズの大幅な変更を探します。
私はビデオのサムネイルに同様のものを実装しましたが、多くの成功とスケーラビリティがありました。
PILの関数を使用して2つの画像を比較できます。
import Image
import ImageChops
im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")
diff = ImageChops.difference(im2, im1)
差分オブジェクトは、すべてのピクセルが最初の画像から2番目の画像のそのピクセルのカラー値を減算した結果である画像です。差分画像を使用すると、いくつかのことができます。最も簡単なのはdiff.getbbox()
関数です。2つの画像間のすべての変更を含む最小の長方形が表示されます。
PILの関数を使用して、ここで説明されている他のものの近似を実装することもできます。
(a)すでに提案されているユークリッド距離、または(b)正規化された相互相関の2つの一般的で比較的単純な方法があります。正規化された相互相関は、単純な相互相関よりも照明の変化に対して著しく堅牢になる傾向があります。ウィキペディアは、正規化された相互相関の公式を提供します。より洗練された方法も存在しますが、かなり多くの作業が必要です。
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など)にリサンプリングし、サムネイルをピクセルごとに特定のしきい値と比較します。元の画像がほぼ同じである場合、リサンプリングされたサムネイルは非常に似ているか、まったく同じになります。この方法は、特に暗い場所で発生する可能性のあるノイズを処理します。グレースケールにするとさらに良いかもしれません。
私は、それらが「十分に異なる」かどうかを計算する方法の問題に具体的に取り組んでいます。ピクセルを1つずつ減算する方法を理解できると思います。
まず、何も変更されていない一連の画像を取得し、キャプチャの変化、イメージングシステムのノイズ、JPEG圧縮のアーティファクト、および瞬間的な瞬間の照明の変化によってピクセルが変化する最大量を調べます。 。何も動かない場合でも、1ビットまたは2ビットの違いが予想されることに気付くでしょう。
次に、「実際の」テストでは、次のような基準が必要です。
したがって、おそらく、E = 0.02、P = 1000の場合、1つのピクセルの変化が5ユニットを超える場合(8ビットのイメージを想定)、または1000を超える場合、(おおよそ)「異なる」ことになります。ピクセルにエラーがあった。
これは主に、さらなる調査を必要としないほど十分に近い画像をすばやく識別するための優れた「トリアージ」手法として意図されています。「失敗」した画像は、たとえばカメラが少し揺れたり、照明の変化に対してより強固だったりした場合に誤検知が発生しない、より精巧で高価な手法になる可能性があります。
私は、オープンソースプロジェクト、実行OpenImageIOこのような閾値(もっと手の込んだ、実際に)との違いを比較し、「idiff」と呼ばれるユーティリティが含まれています、。このソフトウェアを使用したくない場合でも、ソースを見て、どのようにそれを実行したかを確認することをお勧めします。これは商業的にかなり使用されており、このしきい値処理手法は、レンダリングおよび画像処理ソフトウェアのテストスイートを作成できるように開発されたもので、プラットフォームからプラットフォームへの小さな違いがあるか、または微調整を行った「参照画像」アルゴリズムなので、「許容範囲内の一致」操作が必要でした。
職場でも同様の問題があり、画像変換エンドポイントを書き直していて、新しいバージョンが古いバージョンと同じまたはほぼ同じ出力を生成していることを確認したいと思いました。だから私はこれを書いた:
https://github.com/nicolashahn/diffimg
これは、同じサイズの画像でピクセル単位のレベルで動作し、各チャネルの値の差を測定します:R、G、B(、A)、それらのチャネルの平均差を取得し、その差を平均しますすべてのピクセル、および比率を返します。
たとえば、白いピクセルの10x10画像で、同じ画像で1つのピクセルが赤に変わった場合、そのピクセルでの差は1/3または0.33 ...(RGB 0,0,0対255,0,0) )、他のすべてのピクセルは0です。合計100ピクセルの場合、0.33 ... / 100 =画像の〜0.33%の差です。
私はこれがOPのプロジェクトで完全に機能すると信じています(これは非常に古い投稿ですが、Pythonで画像を比較したい将来のStackOverflowersへの投稿です)。
与えられた答えのほとんどは照明レベルを扱いません。
比較を行う前に、まず画像を標準の光レベルに正規化します。
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)
他の人が画像の類似性を比較するより強力な方法に興味がある場合は、チュートリアルとWeb アプリを組み合わせ Tensorflowを使用して類似の画像を測定および視覚化するためのを。
skimage
このアプリケーションで使用すると本当に便利です。よく使うfrom skimage.measure import compare_ssim, compare_mse
。skimage.measure docs。
類似した画像を見つけるためのアルゴリズムに関する質問をご覧になりましたか?それをチェックして提案を見てください。
フレームのウェーブレット変換を提案します(Haar変換を使用して、そのためのC拡張を作成しました)。次に、2つの画像間で最大の(比例的に)ウェーブレット係数のインデックスを比較すると、類似度の数値近似が得られます。
お返事が間に合わなくて申し訳ありませんが、似たようなことをやっているので、なんとか貢献できると思いました。
OpenCVを使用すると、テンプレートマッチングを使用できます。あなたが言ったようにあなたがウェブカメラを使っていると仮定します:
ヒント: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
それが役に立てば幸い。
私は三脚で同じカメラで撮ったjpg画像で(1)大幅に単純化(幅が3000ピクセルから100ピクセルまたはそれ以下になるように)など、多くの幸運をもたらしてきました(2)各jpg配列を1つにフラット化ベクトル(3)相関係数を取得するための単純な相関アルゴリズムによるペアワイズ相関シーケンシャル画像(4)r二乗(つまり、次の変動によって説明される1つの画像の変動率)(5)一般に私のアプリケーションr-square <0.9の場合、2つの画像が異なり、その間に何かが起こったと言います。
これは堅牢で高速です(Mathematica 7)
すべての画像をその小さな領域にトリミングすることで、関心のある画像の部分をいじって、それに焦点を当てる価値があります。そうしないと、カメラから離れていますが、重要な変更は行われません。
Pythonの使い方はわかりませんが、相関関係もあると思いますか?
両方の画像のヒストグラムを計算してからBhattacharyya係数を計算できます。これは非常に高速なアルゴリズムであり、クリケットビデオ(CでopenCVを使用)でショットの変化を検出するために使用しました
isk-daemonによるHaar Waveletの実装方法を確認してください。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', 7))
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つの画像がどのように見えるか/どの程度見えるかを評価するための指標は多数あります。
技術的な問題以外の科学的な問題であるべきだと思うので、ここではコードを使用しません。
一般に、質問は画像に対する人間の知覚に関連しているため、各アルゴリズムは人間の視覚システムの特性をサポートしています。
古典的なアプローチは次のとおりです。
目に見える差異予測:画像の忠実度を評価するためのアルゴリズム(https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000/Visible-differences-predictor--an-algorithm-for-the- validation-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(イメージ品質評価:エラーの可視性から構造的類似性)は、計算が最も簡単で、オーバーヘッドも小さいです。 .org / paper / Image-Quality-Assessment-Based-on-Gradient-Liu-Lin / 2b819bef80c02d5d4cb56f27b202535e119df988)。
他にも多くのアプローチがあります。アートに興味がある/本当に関心がある場合は、Google Scholarを見て、「視覚的な違い」、「画質評価」などを検索してください。
平均二乗誤差を計算することにより、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