LoginSignup
99
106

画像のdiffをとる(追加:青, 削除:赤で見やすく可視化)

Last updated at Posted at 2024-03-09

初版からアルゴリズムを大幅に変更しました。
初版のアルゴリズムではわずかに色が変化する場合(JPEGで劣化した場合など)に紫色になってしまいましたが、新しいアルゴリズムでは改善しています。また、よりシンプルな計算になりました。

この記事のゴール

画像に追加された部分を青、削除された部分を赤で表示したい。

  1. 入力画像A
2. 入力画像B 3. よくある差分画像(|B-A|, cv2.absdiff) 4. この記事で作成する差分画像C

3の差分画像だと入力画像の色の変化(赤→青とか)を扱える、変更箇所が目立つというメリットがあるが、削除された場所と追加された場所の区別がつかない。また、元画像が見えないので人間が解釈するには元画像と照らし合わせないといけない。

4の差分画像は人間に解釈しやすいというメリットがあり、例えば図面の変更箇所の比較などに使える。ただしモノクロ化して処理するため色の変化は扱えない、ごちゃごちゃした画像の微妙な変化は見落とす可能性があるというデメリットがある。

仕組み

入力画像A, Bはモノクロ画像とする。出力画像はCとする。

  1. 差分B-Aを計算する
  2. Aに差分を足し引きして着色する

ステップ1: 差分B-Aを計算する

Aの各ピクセルの明るさを$g_1$、Bの各ピクセルの明るさを$g_2$とする。最小値は0、最大値は1とする。

\begin{align}
A &= g_1 \\
B &= g_2 \\
\mathrm{diff} &= B - A = g_2 - g_1
\end{align}

ステップ2: Aに差分を足し引きして着色する

出力画像Cはカラー画像で、要素の値は順にR, G, Bを表すものとする。
Aに比べてBが明るくなっている場所については、Cは赤色に着色しつつ明るくすればよい。

\begin{align}
C = (g_1+\mathrm{diff},\; g_1,\; g_1) \;(\mathrm{if}\;\mathrm{diff} \geq 0)
\end{align}

Aに比べてBが暗くなっている場所については、Cは青色に着色しつつ暗くすればよい。

\begin{align}
C = (g_1+\mathrm{diff},\; g_1+\mathrm{diff},\; g_1) \;(\mathrm{if}\;\mathrm{diff} < 0)
\end{align}

Python&OpenCVによる実装

OpenCVではRGBではなくBGRの順であることに気をつけて実装する。

import cv2
import numpy as np

# uint8配列 -> flaot32配列
def tofloat32(img):
    return (img/255.).astype(np.float32)

# float32配列 -> uint8配列
def touint8(img):
    return np.clip(img*255, a_min = 0, a_max = 255).astype(np.uint8)

# diff画像作成
def create_img(img1, img2):
    if img1.shape != img2.shape:
        raise Exception("image shape doesn't match")

    img1f = tofloat32(img1)
    img2f = tofloat32(img2)
    diff = img2f - img1f

    # img1 <= img2(明るくなった部分)のマスク
    mask_img1_lesser_img2 = cv2.cvtColor(
        np.where(diff >= 0, 1, 0).astype(np.float32),
        cv2.COLOR_GRAY2BGR
    )
    # img1 > img2(暗くなった部分)のマスク
    mask_img1_greater_img2 = cv2.cvtColor(
        np.where(diff < 0, 1, 0).astype(np.float32),
        cv2.COLOR_GRAY2BGR
    )

    # img1 <= img2の着色
    result1 = cv2.multiply(
        np.dstack((img1f, img1f, img1f+diff)),
        mask_img1_lesser_img2
    )

    # img1 > img2の着色
    result2 = cv2.multiply(
        np.dstack((img1f, img1f+diff, img1f+diff)),
        mask_img1_greater_img2
    )

    return touint8(result1 + result2)

img1 = cv2.imread("a.png", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("b.png", cv2.IMREAD_GRAYSCALE)
diff = create_img(img1, img2)

cv2.imshow("diff", diff)
cv2.waitKey(0)
cv2.destroyAllWindows()

結果の例

入力画像A

入力画像B

出力画像C

99
106
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
99
106