【Python・OpenCV】画像の差異検出するには(cv2.absdiff)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】画像の差異検出するには(cv2.absdiff)

はじめに

画像の差異検出とは、2つの画像間で何が変化したのかを検出する技術です。
具体的には、ピクセル値(画素値)の差分を計算し、その差分が一定の閾値を超えた部分を変化部分として抽出します。
OpenCVのcv2.absdiff関数は画素ごとの差異を計算するための関数です。
動体検出や画像の変化を検出する際によく使われます。

(広告) OpenCV関連書籍をAmazonで探す

cv2.absdiff関数

cv2.absdiff関数は2つの配列または画像の要素ごとの絶対差を計算します。
数学的に表現すると以下の様になります。

$$dst(I)=|src1(I) - sec2(I)|$$

ここで、I は配列の各要素のインデックスを表します。

cv2.absdiff(src1, src2[, dist])

引数

名称説明
src1(必須)・最初の入力配列または画像
numpy.ndarrayオブジェクト
src2(必須)・2番目の入力配列または画像
numpy.ndarrayオブジェクト
dst(オプション)・結果がこの変数に格納されます。指定しない場合は、入力画像と同じサイズ・型で新しい配列が作成されて結果が格納され、戻り値として取得できます。

戻り値

計算された絶対差を表す配列が返されます。

ポイント

  • src1src2
    • 同じサイズと同じ型である必要があります。
    • カラー画像、マルチチャンネル配列に対応
  • dst, 戻り値
    • src1, src2と同じサイズと型になります

使い方

以前の記事cv2.subtract関数を使った画像の間違い探しのサンプルコードを紹介しましたが、同じことをcv2.absdiff関数でやってみます。

入力画像はcv2.subtract関数の時と同じ画像です。
左の画像を元に少し変更を加えた画像が右になります。

引用元
Watercolor world animal day illustration

異なる箇所は下の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()

このコードでは次の手順で処理しています。

  1. 5〜8行目:2つの入力画像の読み込み
  2. 11〜13行目:2つの入力画像のサイズが同じであるか確認。サイズが異なる場合はプログラムを終了
  3. 16〜17行目:matplotlibで結果を確認するため、入力画像のチャンネルの並びをOpenCV標準のBGRじからRGBに変換。matplotlibではBGRの並びの画像は正しく表示できないため。
  4. 20行目:差分計算
  5. 23〜25行目:差分を強調表示するためグレースケールに変換後、二値画像にします。しきい値は50に設定しています。
  6. 28〜44行目:結果を表示

実行した結果は、次のように期待通り3つの異なる箇所を検出できました。
右の画像の白い部分が2つの画像間で異なる箇所を示しています。

cv2.absdiff サンプルコードの結果

画像の差異をより詳細に分析したい場合や、特定の閾値を調整したい場合は、このコードをベースに機能を追加することができます。
例えば、差異の面積を計算したり、特定の領域のみを比較したりすることも可能です。
これらの記事が参考になるかもしれません。

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の参考書としてどうぞ!■

参考リンク

-Python, プログラミング, 画像処理
-