4
11

More than 3 years have passed since last update.

OpenCV 差分画像

Last updated at Posted at 2020-02-08

はじめに

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

参考
Python, OpenCV, NumPyで画像を比較(完全一致か判定、差分取得など)

コード

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


from pathlib import Path 

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

%matplotlib inline

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
lena_low.jpg

#lena_lowは解像度を落としたデータ
DiffImage.diff_image('lena.jpg','lena_low.jpg')

output_3_0.png

#Histgramも表示してみます。
DiffImage.image_hist('lena.jpg')

output_4_0.png

4
11
2

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
4
11