はじめに
画像の差異検出とは、2つの画像間で何が変化したのかを検出する技術です。
具体的には、ピクセル値(画素値)の差分を計算し、その差分が一定の閾値を超えた部分を変化部分として抽出します。
OpenCVのcv2.absdiff
関数は画素ごとの差異を計算するための関数です。
動体検出や画像の変化を検出する際によく使われます。
cv2.absdiff関数
cv2.absdiff
関数は2つの配列または画像の要素ごとの絶対差を計算します。
数学的に表現すると以下の様になります。
$$dst(I)=|src1(I) - sec2(I)|$$
ここで、I は配列の各要素のインデックスを表します。
引数
名称 | 説明 |
---|---|
src1(必須) | ・最初の入力配列または画像 ・ numpy.ndarray オブジェクト |
src2(必須) | ・2番目の入力配列または画像 ・ numpy.ndarray オブジェクト |
dst(オプション) | ・結果がこの変数に格納されます。指定しない場合は、入力画像と同じサイズ・型で新しい配列が作成されて結果が格納され、戻り値として取得できます。 |
戻り値
計算された絶対差を表す配列が返されます。
使い方
以前の記事でcv2.subtract
関数を使った画像の間違い探しのサンプルコードを紹介しましたが、同じことをcv2.absdiff
関数でやってみます。
入力画像はcv2.subtract
関数の時と同じ画像です。
左の画像を元に少し変更を加えた画像が右になります。
異なる箇所は下の3つの赤丸です。
これらの差異を検出するサンプルコードがこちら。
import cv2 from matplotlib import pyplot as plt # 入力画像1を読み込む image1 = cv2.imread("image1.jpg") # 入力画像2を読み込む image2 = cv2.imread("image2.jpg") # 画像のサイズが同じであることを確認 if image1.shape != image2.shape: print("画像のサイズが異なります。リサイズが必要です。") exit # BGRのチャンネル並びをRGBの並びに変更(matplotlibで結果を表示するため) rgb_image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB) rgb_image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2RGB) # 差分を計算 diff = cv2.absdiff(rgb_image1, rgb_image2) # グレースケールに変換 diff_gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) # 二値化で異なる部分を強調、背景が白になる様にcv2.THRESH_BINARY_INVを指定 retval, result = cv2.threshold(diff_gray, 50, 255, cv2.THRESH_BINARY) # 結果の可視化 plt.rcParams["figure.figsize"] = [18,7] # ウィンドウサイズを設定 title = "cv2.absdiff: codevace.com" plt.figure(title) # ウィンドウタイトルを設定 plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95) # 余白を設定 plt.subplot(131) # 1行3列の1番目の領域にプロットを設定 plt.imshow(rgb_image1) # 入力画像1を表示 plt.title('image1') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.subplot(132) # 1行3列の2番目の領域にプロットを設定 plt.imshow(rgb_image2) # 入力画像2を表示 plt.title('image2') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.subplot(133) # 1行3列の3番目の領域にプロットを設定 plt.imshow(result, cmap='gray') # 差分の画像を表示 plt.title('subrtacted') # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.show()
このコードでは次の手順で処理しています。
- 5〜8行目:2つの入力画像の読み込み
- 11〜13行目:2つの入力画像のサイズが同じであるか確認。サイズが異なる場合はプログラムを終了
- 16〜17行目:matplotlibで結果を確認するため、入力画像のチャンネルの並びをOpenCV標準のBGRじからRGBに変換。matplotlibではBGRの並びの画像は正しく表示できないため。
- 20行目:差分計算
- 23〜25行目:差分を強調表示するためグレースケールに変換後、二値画像にします。しきい値は50に設定しています。
- 28〜44行目:結果を表示
実行した結果は、次のように期待通り3つの異なる箇所を検出できました。
右の画像の白い部分が2つの画像間で異なる箇所を示しています。
画像の差異をより詳細に分析したい場合や、特定の閾値を調整したい場合は、このコードをベースに機能を追加することができます。
例えば、差異の面積を計算したり、特定の領域のみを比較したりすることも可能です。
これらの記事が参考になるかもしれません。
cv2.subtract関数とcv2.absdiff関数の違いについて
両者は画像の差分を計算するために使用されますが、その動作と結果には違いがあります。
基本的な違い
- cv2.subtract関数
- 2つの配列または画像の各要素の差を計算します。
- 結果は符号付きで、負の値も含みます(データ型による)。
- 数式:
dst(I) = saturate(src1(I) - src2(I))
- cv2.absdiff関数
- 2つの配列または画像の各要素の差の絶対値を計算します。
- 結果は常に非負です。
- 数式:
dst(I) = |src1(I) - src2(I)|
動作の詳細
- cv2.subtract関数
- 飽和演算を行います。つまり、結果が指定されたデータ型の範囲を超える場合、その型の最大値または最小値にクリップされます。
- 8ビット符号なし整数(uint8)の場合、負の結果は0にクリップされます。
- cv2.absdiff関数
- 常に非負の結果を返します。
- オーバーフローの問題が少ないです(ただし、完全に回避されるわけではありません)。
応用と使い分け
- cv2.subtract関数
- 背景除去:静的な背景画像から現在のフレームを引く場合
- 画像の明るさ調整:一定値を引くことで画像全体を暗くする
- マスク処理:特定の領域を除外する際に使用
- cv2.absdiff関数
- 動体検出:連続するフレーム間の差を検出する場合
- エッジ検出:画像のシャープな変化を検出する際に使用
- 画像の類似性評価:2つの画像がどの程度異なるかを測定する場合。
おわりに
cv2.absdiff
関数は常に非負の結果を返し、変化の大きさを測定するのに適しています。
2つの画像や配列の差を効率的に計算できるため、様々な画像処理タスクの基礎となっています。
適切に使用することで、動体検出や変化検出など、多くの高度なアプリケーションを実現できます。
ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。
■(広告)OpenCVの参考書としてどうぞ!■
参考リンク
OpenCV: absdiff()
Calculates the per-element absolute difference between two arrays or between an array and a scalar.