

[Python3
OpenCV3で背景差分を求める](https://www.blog.umentu.work/python3-opencv3%E3%81%A7%E8%83%8C%E6%99%AF%E5%B7%AE%E5%88%86%E3%82%92%E6%B1%82%E3%82%81%E3%82%8B/)

上記サイトの方法でPythonのOpenCVを使った背景差分画像の作成を行うことができたのですが、  
画像の差分を数値化させ、その数値を使って一定以上であるか否かを行いたいのですが、方法が分からず困っています。  
何か良い方法を教えていただけないでしょうか？  
よろしくお願いします。


CC BY-SA 4.0
2018-09-02T05:08:14.683
48019
2018-09-07T14:06:58.670
2018-09-03T01:50:09.907
3060
25104
question
python python3 opencv
Pythonを使ったOpenCVでの背景差分の数値化の方法


コメントでも言いましたが、質問内容の「画像の差分を数値化させ、その数値を使って一定以上であるか否か」を決めるという内容は、質問にあるリンク先のサイトで行われています。  
なので、リンク先のサイトで行われている処理を解説します。  
（質問者が何が分からないのか分からないのでなるべく基本から解説しましたが、もしまだ分からない部分があるなら、コメントしてください。追記します。）

### 画像の差分を数値化

まず、そもそもデジタル画像は2次元の数字（画素値）の並びとして表現されます。  
これをI(x,y)と表すことにしましょう（x,yが座標で、Iが画素値）。  
普通は画素値としては0 - 255の値が使われます。  
2枚の画像I1, I2の差分を数値化するには、  
I_diff(x,y) = |I1(x,y) - I2(x,y)|  
とすればいいでしょう。  
ここで、差分がマイナスになってしまうのを防ぐために絶対値を取っています。  
これを実装するには、自分で

[code]

    for y in range(width):
      for x in range(height): 
        I_diff[y,x] = abs(I1[y,x]-I2[y,x])
    
[/code]

みたいな感じで実装してもいいのですが、そういった基本的な画像処理を使えるライブラリが存在して、その一つが[OpenCV](https://opencv.org/)です。  
OpenCV自体はc/c++で実装されていて、それをPythonからでも呼び出すことができます（当然c++から使うこともできます）。  
自分でPythonでfor文などを使って実装するより高速に動くので、特別な理由がない限りOpenCVなどのライブラリを利用して画像処理をするのがおすすめです。  
PythonからOpenCVを使うには、まず最初に

[code]

    import cv2
    
[/code]

でライブラリをインポートします。するとcv2.function_nameみたいな形でライブラリの関数を呼び出すことができます。  
「差分を数値化」を行う関数がcv2.absdiffです。  
従って、

[code]

    img_diff = cv2.absdiff(img_src2, img_src1)
    
[/code]

の部分で計算したimg_diffが「差分を数値化」した画像（差分画像）になっています。

### その数値を使って一定以上であるか否か

「違う部分」を見つけるには、前節で計算した差分画像の値があるしきい値より大きい場合には「違う部分」とすればいいでしょう。  
このように、0 or 1（今回の場合は「同じ部分」と「違う部分」）で表される画像に2値画像といい、何らかの処理で得た2値画像をマスクなどと言ったりもします。  
違う部分を取り出す処理は

[code]

    I_mask = 1 if I_diff(x,y) < threshold
             0 otherwise
    
[/code]

となります。これもfor文などを使って自分で実装してもいいのですが、当然OpenCVにこの処理をしてくれる関数があり、cv2.threshold(img,
thresh, maxVal,
type)です（引数は[ここ](http://lang.sist.chukyo-u.ac.jp/classes/OpenCV/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html)を参考に）。  
リンク先では

[code]

    img_diffm = cv2.threshold(img_diff, 20, 255, cv2.THRESH_BINARY)[1]
    
[/code]

で「違う部分」を計算しているので、このimg_diffmが質問者が求めていた答えになります。

### 後処理

こういった差分画像に対するしきい値処理（2値化）で「違う部分」を求めると画像のノイズなどにより綺麗に「違う部分」が求められないことがあります。  
そこで、リンク先では「膨張処理、収縮処理」を行うことで計算した「違う部分」を綺麗にしています。なぜ、膨張収縮処理で綺麗にできるかは例えば[こちら](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html)を参考にしてください。  
綺麗にしたり安定化させる方法は、リンク先の方法以外にもあり、例えば

  * 2値化を単純なしきい値処理から、大津の方法などに変える
  * Blurをかけてから2値化する

などが考えられます。  
最後にリンク先では、「同じ部分」は黒で、「違う部分」は2つ目の入力画像にすることで可視化しています。

追記：  
リンク先におかしいところがあったので修正しました。  
リンク先ではカラーのまま2値化しているので、マスクもカラーになっています。  
そのため、最後の出力もRGBごとにマスクがことなっていて、色が変になっています。  
普通、2値化する前にグレー画像にします。  
後処理は省略してます。

[code]

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    img_src1 = cv2.imread("./image/dambo1.jpg", 1)
    img_src2 = cv2.imread("./image/dambo2.jpg", 1)
    
    img_diff = cv2.absdiff(img_src2, img_src1)
    img_diff = cv2.cvtColor(img_diff, cv2.COLOR_BGR2GRAY)
    img_diffm = cv2.threshold(img_diff, 20, 255, cv2.THRESH_BINARY)[1]
    
    img_diff = cv2.cvtColor(img_diff, cv2.COLOR_GRAY2RGB)
    img_diffm = cv2.cvtColor(img_diffm, cv2.COLOR_GRAY2RGB)
    plt.subplot(121)
    plt.imshow(img_diff)
    plt.title("diff image")
    plt.subplot(122)
    plt.imshow(img_diffm)
    plt.title("mask")
    plt.show()
    
[/code]

[![結果画像](https://i.stack.imgur.com/MJtOy.png)](https://i.stack.imgur.com/MJtOy.png)


CC BY-SA 4.0
2018-09-07T13:39:11.390
48189
2018-09-07T14:06:58.670
2018-09-07T14:06:58.670
18595
18595
48019
answer


[nohzen
さんの回答](https://ja.stackoverflow.com/a/48189/19110)に補足して、「全体の中で違う部分がどれだけあったのか」を調べるためには、たとえば配列
`img_diffm` の全要素の和を計算するという手法が考えられます。`img_diffm` の中身は (少なくとも最初の段階では) 同じか違うかの
0/1
配列なので、しきい値より上だったピクセルの数を和として数えることができます。全体のピクセル数で割れば、「違ったピクセル」の割合を計算することもできます。あとは割合のしきい値を決めて大小比較することで、前の画像に比べて大きく変わったのかどうかを判定することができます。


CC BY-SA 4.0
2018-09-07T13:57:50.403
48190
2018-09-07T13:57:50.403
19110
48019
answer