これが私がやりたいことです:
定期的にウェブカメラで写真を撮っています。タイムラプスのようなもの。ただし、実際に何も変わっていない場合、つまり、写真がほとんど同じに見える場合は、最新のスナップショットを保存したくありません。
違いを定量化する方法があると思いますが、経験的にしきい値を決定する必要があります。
私は完璧ではなくシンプルさを求めています。私はパイソンを使用しています。
これが私がやりたいことです:
定期的にウェブカメラで写真を撮っています。タイムラプスのようなもの。ただし、実際に何も変わっていない場合、つまり、写真がほとんど同じに見える場合は、最新のスナップショットを保存したくありません。
違いを定量化する方法があると思いますが、経験的にしきい値を決定する必要があります。
私は完璧ではなくシンプルさを求めています。私はパイソンを使用しています。
オプション 1: 両方の画像を配列 ( scipy.misc.imread
) として読み込み、要素ごと (ピクセルごと) の差を計算します。差のノルムを計算します。
オプション 2: 両方のイメージを読み込みます。それぞれの特徴ベクトルを計算します (ヒストグラムなど)。画像ではなく特徴ベクトル間の距離を計算します。
ただし、最初に決定しなければならないことがいくつかあります。
最初に次の質問に答える必要があります。
画像は同じ形と大きさですか?
そうでない場合は、サイズ変更またはトリミングが必要になる場合があります。PIL ライブラリは、Python でそれを行うのに役立ちます。
同じ設定、同じデバイスで撮影された場合、それらはおそらく同じです。
画像は適切に配置されていますか?
そうでない場合は、最初に相互相関を実行して、最適なアライメントを見つけます。SciPy にはそれを行う機能があります。
カメラとシーンが静止している場合、画像は適切に配置されている可能性があります。
画像の露出は常に同じですか?(明度・コントラストは同じ?)
そうでない場合は、画像を正規化することをお勧めします。
ただし、状況によっては、これは良いことよりも悪いことになる可能性があることに注意してください。たとえば、暗い背景に 1 つの明るいピクセルがあると、正規化されたイメージが大きく異なります。
色情報は重要ですか?
色の変化に注目したい場合は、グレースケール イメージのようなスカラー値ではなく、ポイントごとの色値のベクトルが必要です。このようなコードを書くときは、もっと注意が必要です。
画像に明確なエッジがありますか? 彼らは動く可能性がありますか?
はいの場合、最初にエッジ検出アルゴリズムを適用し (たとえば、ソーベルまたはプレウィット変換で勾配を計算し、しきい値を適用)、次に最初の画像のエッジを 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
強度を取得するために RGB チャネル (最後の配列軸) を平均化した 3D 配列を返します。グレースケール画像に対して行う必要はありません (例.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スクリプト全体。
質問は、フレームがほぼ同じである可能性が高いビデオ シーケンスに関するものであり、異常なものを探しているため、関連する可能性のあるいくつかの代替アプローチについて言及したいと思います。
「Learning OpenCV」ブックの第 9 章 (画像のパーツとセグメンテーション) と第 10 章 (トラッキングとモーション) を読むことを強くお勧めします。前者はバックグラウンド減算法を使用することを教え、後者はオプティカル フロー法に関する情報を提供します。すべてのメソッドは OpenCV ライブラリに実装されています。cv2
Python を使用している場合は、OpenCV ≥ 2.3 とそのPython モジュールを使用することをお勧めします。
バックグラウンド減算の最も単純なバージョン:
より高度なバージョンでは、すべてのピクセルの時系列が考慮され、静的でないシーン (木や草の移動など) が処理されます。
オプティカル フローの考え方は、2 つ以上のフレームを取り、すべてのピクセル (高密度オプティカル フロー) またはそれらの一部 (疎オプティカル フロー) に速度ベクトルを割り当てることです。スパース オプティカル フローを推定するには、Lucas-Kanade 法を使用できます(OpenCV にも実装されています)。明らかに、フローが多い場合 (Velocity フィールドの最大値の平均値が高い場合)、フレーム内で何かが動いており、後続の画像はより異なっています。
ヒストグラムを比較すると、連続するフレーム間の突然の変化を検出するのに役立つ場合があります。このアプローチは、Courbon et al, 2010で使用されました。
連続するフレームの類似性。2 つの連続するフレーム間の距離が測定されます。値が高すぎる場合は、2 番目のフレームが破損しているため、画像が削除されていることを意味します。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)
差分オブジェクトは、すべてのピクセルが、最初の画像から 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 - 平均(i1)) * (i2 - 平均(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」というユーティリティが含まれています (実際にはさらに複雑です)。このソフトウェアを使用したくない場合でも、ソースを参照して、私たちがどのようにそれを行ったかを確認することをお勧めします。これは商業的にかなり使用されており、このしきい値処理手法は、レンダリングおよび画像処理ソフトウェア用のテストスイートを用意できるように開発されました。「参照画像」には、プラットフォームごとにわずかな違いがある場合や、微調整を行った場合があります。そのため、「許容範囲内で一致する」操作が必要でした。
与えられた答えのほとんどは、照明レベルを扱っていません。
比較を行う前に、まず画像を標準の光レベルに正規化します。
類似の画像を見つけるためのアルゴリズムの質問を見たことがありますか?提案を確認するためにそれをチェックしてください。
フレームのウェーブレット変換をお勧めします(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
それが役に立てば幸い。
(1) 大幅に単純化する (幅 3000 ピクセルから幅 100 ピクセルまたはそれ以下にするなど) (2) 各 jpg 配列を 1 つにフラット化することにより、三脚で同じカメラで撮影した jpg 画像で多くの幸運が得られました。ベクトル (3) 相関係数を取得するための単純な相関アルゴリズムを使用したペアワイズ相関連続画像 (4) r-2 乗 (つまり、次の変動によって説明される 1 つの画像の変動の割合) を取得するための相関係数の 2 乗 (5) 一般的に私のアプリケーションではr-square < 0.9 の場合、2 つの画像は異なっており、その間に何かが発生したと言えます。
これは私の実装では堅牢で高速です(Mathematica 7)
関心のある画像の部分をいじって、すべての画像をその小さな領域にトリミングすることでその部分に焦点を当てることは価値があります。そうしないと、カメラから離れていても重要な変更が見逃されます。
Python の使用方法はわかりませんが、相関も行うことは確かですよね?
両方の画像のヒストグラムを計算し、Bhattacharyya Coefficientを計算できます。これは非常に高速なアルゴリズムであり、クリケット ビデオのショットの変化を検出するために使用しました (C で openCV を使用)。
Haar ウェーブレットがisk-daemonによってどのように実装されているかを確認してください。その imgdb C++ コードを使用して、オンザフライで画像間の違いを計算できます。
isk-daemon は、画像関連の Web サイトまたはソフトウェアにコンテンツ ベースの (視覚的な) 画像検索を追加できるオープン ソース データベース サーバーです。
この技術により、画像関連の Web サイトまたはソフトウェアのユーザーは、検索したい画像をウィジェットでスケッチし、Web サイトに最も類似した画像を返信させるか、各画像詳細ページでより類似した写真を要求することができます。
アースムーバーの距離はまさにあなたが必要としているものかもしれません. ただし、リアルタイムで実装するには少し重いかもしれません。
2 つの画像のマンハッタン距離の計算についてはどうですか。これにより、n*n 値が得られます。次に、行平均のようなものを実行して n 値に減らし、それを超える関数を使用して 1 つの値を取得できます。
2 つの画像の輝度の間のユークリッド距離 (つまり、sqrt(ピクセルごとの差の平方和)) を単純に計算し、これが経験的なしきい値を下回っている場合は等しいと見なすことができると思います。そして、C関数をラップする方がよいでしょう。