OpenCV　差分画像

###はじめに

OpenCVを使って、差分画像の表示、保存する一連の動作を以下の記事を参考にまとめてみました。
関数でよいのですが、クラスのStaticmethodを使ってみたかったので、クラスでまとめてみました。
ご指摘を踏まえ、画像コードの一部（差分画像のlogによる強調）を削除しています。

参考
[Python, OpenCV, NumPyで画像を比較（完全一致か判定、差分取得など）](https://note.nkmk.me/python-opencv-numpy-image-difference/)

###コード
こちらに同じコードがJupyterファイルであります。
https://github.com/SGyutan/opencv_diff_image

```python

from pathlib import Path 

import numpy as np
import cv2
import matplotlib.pyplot as plt

%matplotlib inline
```
```python

class DiffImage():
    """
    差分画像解析
    
    """
    
    @staticmethod
    def three_image_show(image1, image2, image3 ):
        
        plt.figure(figsize=(20,20),dpi=200) # figsize
        
        plt.subplot(131)
        plt.imshow(image1,cmap='gray')
        plt.title('before'), plt.xticks([]), plt.yticks([])
        plt.subplot(132)
        plt.imshow(image2,cmap='gray')
        plt.title('after'), plt.xticks([]), plt.yticks([])
        plt.subplot(133)
        plt.imshow(image3,cmap='gray')
        plt.title('sub image'), plt.xticks([]), plt.yticks([])
        plt.show()
    
        
    @staticmethod 
    def diff_image(image1,image2,bit=8,opt_dir=None):
        """
        Make difference image
        image2-image1 
        
        image1,2: file name is string. example:'image4.tif'
        bit: 8bit->8, 16bit -> 16
        opt_dir: file path ,string,  example:'C:/user/'

        """
        if opt_dir != None:
            p_dir= Path(opt_dir)
            p_dir_img1= p_dir/image1
            p_dir_img2= p_dir/image2
        else:
            p_dir_img1=Path(image1)
            p_dir_img2=Path(image2)
            
            
        if bit==8:    
            img1=cv2.imread(str(p_dir_img1),0)
    #         print(img1.shape,img1.dtype)
            img2=cv2.imread(str(p_dir_img2),0)
        #     print(img2.shape,img2.dtype)
        else:
            #16bit 
            img1=cv2.imread(str(p_dir_img1),-1)
    #         print(img1.shape,img1.dtype)
            img2=cv2.imread(str(p_dir_img2),-1)
        #     print(img2.shape,img2.dtype)
            

        if img1.shape == img2.shape:
            ###画像が同じ形状、幅・高さが同じか判定
            # print(np.array_equal(img1, img2))
            """
            差分の絶対値は同じ形状のndarrayは-演算子で差分を算出できる。
            cv2.imread()で読み込んだndarrayのデータ型は符号なし整数8ビットunit8
            そのままだとマイナスの値を扱えない。astype()で符号ありのintに変換する。
            """
            im_diff = img2.astype(int)-img1.astype(int)
#             print(im_diff.dtype)
#             print(img2.astype(int).dtype)
            ## 差分を画像として保存したい場合はnp.abs()で絶対値をとる。
            im_diff_abs = np.abs(im_diff)

            diff_file_name='diff_{}_{}.png'.format(image1.split('.')[0],image2.split('.')[0])
            
            # save the substraction data 
            cv2.imwrite(diff_file_name, im_diff_abs)
            
           
            #最大値で規格化して差分を強調する
            im_diff_abs_norm = DiffImage.enhance_image(im_diff_abs)

            diff_nor_file_name='diff_nor_{}_{}.png'.format(image1.split('.')[0],image2.split('.')[0])
    
            # save the substraction data 
            cv2.imwrite(diff_nor_file_name, im_diff_abs_norm)
            
           # DiffImage.three_image_show(img1, img2, im_diff_abs)
        
            DiffImage.three_image_show(img1, img2, im_diff_abs_norm)
            
        else:
            print('The shape of the image is not the same. ')

    @staticmethod
    def enhance_image(img0):
        """
        最大値で規格化して差分を強調する
        img0: ndarray

        """
        im_abs = np.abs(img0)
        image_bit=img0.dtype
    
        if image_bit==np.uint8:
            max=255
                
        elif image_bit == np.uint16:
            max=65535
                
        else:
            max=255
            
#         print(max)
            
        if im_abs.max() == 0 :
            #0で割るとエラーになるので
            im_abs_norm = (im_abs / 1) * max
            
        else:
            im_abs_norm = (im_abs / im_abs.max()) * max          
        
        return im_abs_norm
    
   
        
    @staticmethod  
    def diff_image_opencv(image1,image2,bit=8,opt_dir=None):
        """
        OpenCVにある関数を利用
        参考差分関数：
         cv2.absdiff(ndarray1, ndarray2)
        image2-image1 


        """
        if opt_dir != None:
            p_dir= Path(opt_dir)
            p_dir_img1= p_dir/image1
            p_dir_img2= p_dir/image2
        else:
            p_dir_img1=Path(image1)
            p_dir_img2=Path(image2)
            
            
        if bit==8:    
            img1=cv2.imread(str(p_dir_img1),0)
    #         print(img1.shape,img1.dtype)
            img2=cv2.imread(str(p_dir_img2),0)
        #     print(img2.shape,img2.dtype)
        else:
            img1=cv2.imread(str(p_dir_img1),-1)
    #         print(img1.shape,img1.dtype)
            img2=cv2.imread(str(p_dir_img2),-1)
        #     print(img2.shape,img2.dtype)
            

        if img1.shape == img2.shape:
            ###画像が同じ形状、幅・高さが同じか判定
            # print(np.array_equal(img1, img2))
            
            #参考差分関数：
            im_diff_abs = cv2.absdiff(img1, img2)

            diff_file_name='diff_'+image1.split('.')[0] + '_' +image2.split('.')[0] +'.png'
            cv2.imwrite(diff_file_name, im_diff_abs)

             #差分が小さくて画像で確認しにくいときは最大値で規格化する
            im_diff_abs_norm = DiffImage.enhance_image(im_diff_abs)

            diff_nor_file_name='diff_nor_'+image1.split('.')[0] + '_' +image2.split('.')[0] +'.png'
    
            # save the substraction data 
            cv2.imwrite(diff_nor_file_name, im_diff_abs_norm)
            
           # DiffImage.three_image_show(img1, img2, im_diff_abs)
        
            DiffImage.three_image_show(img1, img2, im_diff_abs_norm)

           
    @staticmethod
    def image_hist(image0,opt_dir=None):
        """
        8bitのみ
        example: DiffImage.image_hist("109.bmp")

        """
        
        if opt_dir != None:
            p_dir= Path(opt_dir)
            p_dir_img1= p_dir/image0

        else:
            p_dir_img1=Path(image0)

        img1=cv2.imread(str(p_dir_img1),0)
#         print(img1.shape,img1.dtype)
        hist, bins = np.histogram(img1.flatten(), bins=256)
        plt.figure(figsize=(20,10))
        plt.plot(hist, marker='o')
        plt.grid(True)
        plt.xlabel('pixel value', fontsize = 20)
        plt.ylabel('number of pixels', fontsize = 20)
        plt.xlim(0, 255)  # 0〜255までplot
        plt.title('Histgram, {}'.format(image0), fontsize = 20)
```
###例題
例題として読み込むファイルは、Lenaさんの画像、解像度を落としたLenaさんの画像を使用しました。
![lena.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/b88ccb54-7417-952d-806b-911f620d156b.jpeg)
![lena_low.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/d415eb46-a505-3852-2f50-3a7fbff583eb.jpeg)

```
#lena_lowは解像度を落としたデータ
DiffImage.diff_image('lena.jpg','lena_low.jpg')
```
![output_3_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/dbe6469f-3610-abd4-d723-7feb9d6d09e1.png)

```
#Histgramも表示してみます。
DiffImage.image_hist('lena.jpg')
```
![output_4_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165987/369b192c-3b7f-1d64-7f90-bf336ecd5559.png)