techno_memo

個人用の技術メモ。python・ROS・AI系のソフトウェア・ツールなどの情報を記載

前処理① 画像に対する前処理(リサイズ/色補正など)

やりたいこと

 画像に対して下記のような前処理を実施する

  • リサイズ

  • 反転

  • トリミング/パディング

  • グレースケール変換/2値化/ノイズ除去

  • 色補正

 opencvではpandasのようなチートシートがあれば参考としたかったが、見つからなかったので下記のようにまとめてみた。  (ひとまず画像読み込みなどの基本機能から前処理まで。エッジ検出による特徴抽出、物体検出などは別途取り扱う)

f:id:sd08419ttic:20190302203240p:plain

実装

上記機能を1つのクラスにまとめて取り扱えるように実装した。

クラスのインスタンス生成時にファイルから画像を self.imgに取り込み、他関数呼び出しで変換後の画像を出力する仕組みとしている。

リサイズ処理

OpenCV機能を使い、サイズ直接入力・比率の両方で指定できるように実装

    def resize_img_aspect_ratio(self,ratio=1.0):
        '''
        画像を拡大縮小する (比率指定)
        '''
        if self.img is None:
            return None
        else:
            result = cv2.resize(self.img , (int(self.img.shape[0]*ratio), int(self.img.shape[1]* ratio)))
        return result

    def resize_img_pix_size(self,height = 200, width = 150):
        '''
        画像を拡大縮小する (ピクセルサイズ指定)
        '''
        if self.img is None:
            return None
        else:
            result = cv2.resize(self.img , (int(width), int(height)))
        return result

反転処理

横・縦・両方の軸をパラメータ指定して反転できるように実装した。

    def flip_img(self,mode="x"):
        '''
        画像を反転させる
        x: 横方向反転、y:縦方向反転 xy:両方向反転
        '''
        if self.img is None:
            return None
        else:
            if mode == "x": #X軸方向反転
                result = cv2.flip(self.img,1)
            elif mode == "y": #Y軸方向反転
                result = cv2.flip(self.img,0)
            elif mode == "xy": #両方向反転
                result = cv2.flip(self.img,-1)
            else:
                result = self.img
        return result

トリミング

比率ベースのトリミング/座標直接指定のトリミングの両方ができるように実装した。

    def trim_img_aspect_ratio(self,left=0.10, right = 0.10, up=0.10, down = 0.10):
        '''
        画像をトリミングする (左右上下それぞれでカットする部分の比率を0-1.0の間で指定)
        '''
        if self.img is None:
            return None
        else:
            left = max(min(0.5,left),0.0)
            right = max(min(0.5,right),0.0)
            up = max(min(0.5,up),0.0)
            down = max(min(0.5,down),0.0)
            left_edge = int(self.img.shape[0]*left)
            right_edge = int(self.img.shape[0] - self.img.shape[0]*right)
            up_edge = int(self.img.shape[1]*up)
            down_edge = int(self.img.shape[1] - self.img.shape[1]*down)
            result = self.img[up_edge:down_edge,left_edge:right_edge]
        return result

    def trim_img_pix_size(self,left_edge = 0, right_edge = 0,  up_edge = 0, down_edge = 0):
        '''
        画像をトリミングする (ピクセル指定。画像サイズ以上の場合は無視)
        left_edge,right_edge,up_edge,down_edge
        '''
        if self.img is None:
            return None
        else:
            left = max(min(self.img.shape[0],left_edge),0.0)
            right = max(min(self.img.shape[0],right_edge),0.0)
            up = max(min(self.img.shape[1],up_edge),0.0)
            down = max(min(self.img.shape[1],down_edge),0.0)
            result = self.img[up:down,left:right]
        return result

パディング

指定幅だけ上下左右同じ幅ゼロ埋めする関数とした。 (その他反転するパディングなども可能。opencv公式リファレンスを参照のこと)

    def zero_padding_img(self,pad_width = 10):
        '''
        画像の境界領域をゼロ埋めする
        pad_width:パディング幅サイズ
        '''
        if self.img is None:
            return None
        else:
            PAD_COL = [0,0,0]   #ゼロパディング
            result= cv2.copyMakeBorder(self.img,pad_width,pad_width,pad_width,pad_width,cv2.BORDER_CONSTANT,value=PAD_COL)
        return result

グレースケール・2値化処理

入力画像をグレースケース変換し、2値化アルゴリズムで0 or 255の2値に変換する処理を作成した。 2値化アルゴリズムはパラメータ切り替えで変更できる (デフォルトは大津の二値化)

    def get_gray_img(self,color_im= "None"):
        '''
        グレースケール画像を取得する (color_im:カラー画像データ(option))
        '''
        if color_im is "None":
            if self.img is None:
                return None
            else:
                return cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        else:
            return cv2.cvtColor(color_im, cv2.COLOR_BGR2GRAY)

    def get_binary_img(self,prm="OTSU"):
        '''
        2値化処理 prm: "OTSU":大津の2値化、"BIN":静的な2値化、"ADAPT":ローカル閾値

        '''
        if self.img is None:
            return None

        gray_img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        if prm == "OTSU":
            _, result = cv2.threshold(gray_img, 0, 255, cv2.THRESH_OTSU)
        elif prm == "BIN":
            _, result = cv2.threshold(gray_img, 128, 255, cv2.THRESH_BINARY)
        elif prm == "ADAPT":
            result = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 55, 20)
        else:
            result = self.img
        return result

モルフォジー変換によるノイズ除去

2値データに対して膨張・収縮・オープニング・クロージング (膨張と収縮を組み合わせる)ことでノイズを消去する手法を実装した。

(手書き文字認識などのスキャンデータ、ROSの障害物地図データなどの処理などに利用できる)

パラメータは公式のままなので画像サイズや特性に応じて設定が必要

    def get_morphology_img(self,prm="Ero",binimg="None"):
        '''
        モルフォロジー変換 prm: "Ero":収縮、"Dil":膨張、"Ope":オープニング(膨張+縮小) "Clo":"クロージング(縮小+膨張)"

        '''
        if binimg is "None":
            if self.img is None:
                return None
            gray_img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
            _, bin_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_OTSU)
        else:
            bin_img = binimg

        kernel = np.ones((3,3),np.uint8)

        if prm == "Ero":
            result = cv2.erode(bin_img, kernel,iterations = 2)
        elif prm == "Dil":
            result = cv2.dilate(bin_img, kernel,iterations = 2)
        elif prm == "Ope":
            result = cv2.morphologyEx(bin_img, cv2.MORPH_OPEN, kernel)
        elif prm == "Clo":
            result = cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel)
        else:
            result = self.img
        return result

色補正フィルタ

画像の色を補正するためガンマ補正(色濃淡・明るさ)、ガウシアンフィルタ(ジャギー低減)、鮮鋭化フィルタ(エッジ強調)を実装した。

あくまで一般的な例なので、パラメータや他フィルタについても必要に応じて実装する必要がある。

    def get_gamma_img(self,gamma=1.0):
        '''
        ガンマ補正変換 (濃淡の補正)
        gamma: 1.0がデフォルト 小さくするほど薄く白っぽく、高くするほど濃く黒っぽい画像になる

        '''
        if self.img is None:
            return None
        LTB = np.empty((1,256), np.uint8)
        for i in range(256):
            LTB[0,i] = np.clip(pow( float(i) / 255.0, gamma) * 255.0, 0, 255)   #Look Up Tableの生成
 
        result = cv2.LUT(self.img, LTB) #輝度値のガンマ補正
        return result

    def get_gausian_filter_img(self,filtsize=5):
        '''
        ガウシアンフィルタ画像:ぼかしフィルタ (ジャギーなどの低減に有効)
        filtsize:フィルタサイズ(整数型かつ奇数)
        '''
        if self.img is None:
            return None
        # Look up tableを使って画像の輝度値を変更
        result = cv2.GaussianBlur(self.img,(filtsize,filtsize),0)
        return result


    def get_sharp_filter_img(self):
        '''
        鮮鋭化フィルタ画像:ボケた画像のエッジ・テクスチャ協調に有効
        '''
        if self.img is None:
            return None
        kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]], np.float32)
        result = cv2.filter2D(self.img, -1, kernel)
        return result

ソースコードは下記で公開している。

github.com

参考サイト

labs.eecs.tottori-u.ac.jp

labs.eecs.tottori-u.ac.jp

labs.eecs.tottori-u.ac.jp