画像認識・物体検出の為の画像前処理
【No.6】実践(opencvを使用) 画像の単純化

1. 簡単な処理フロー

画像認識や物体検出、文字認識に関して、筆者がおすすめの前処理を3つピックアップし、opencvを使用して実際に処理を行ってみようと思います。

今回は実践1「画像の単純化(物体検出)」を行っていきます。
以下に今回の簡単な処理フローを記載します。

処理フロー

・グレースケール化
・二値化

2. 画像の単純化について

おすすめの前処理の1つ目として、物体検出の際の画像単純化(二値化)があります。
単純化とは、文字通り画像を単純化する処理のことです。
具体的には、二値化という処理を行い画像を単純化します。二値化とは、画像を0(黒)と1(白)の2つのみに変換する画像処理です。

図1 二値化を行う画像

今回は図1画像中のオブジェクトに存在する「欠けている部分」を物体検出するケースを想定してみようと思います(筆者が過去扱ったケースをベースにしています)。
製品の外観検査でよく使用されるケースです。この場合、欠け部分が分かれば良いので、それ以外は省略しても良いということになります。つまり単純化可能と言えます。

処理後は図2のイメージになります。

図2 二値化処理後の画像

本処理がおすすめの理由が2つあります。1つ目が「物体検出の処理コスト削減」、2つ目が「精度向上」です。

1つ目については、単純化によって入力画像の画素値が0と1のみになります。また、カラー画像の場合3チャンネルから1チャンネルになります。
結果、元画像と比べて大分情報が軽くなります。入力画像が軽くなることで、メイン処理である物体検出にかかる処理コストを減らすことができます。

2つ目について、画像が単純化、つまり余計な情報が削ぎ落とされている為、物体検出の対象である「欠け」の要素を容易に検出しやすくなります。
但し、注意すべきところがあります。物体検出の対象(この場合「欠け」部分)の情報も、単純化によって削ぎ落とされてしまってないか確認する必要があります。
そうなってしまうと、逆に物体検出の精度は落ちますので、見極めが必要です。

3. 処理の実装

では、実際に処理を構築していきたいと思います。
今回はPythonのopencvを使用します。opencvについての説明は割愛させていただきます。また、各関数の全ての引数を記載することはしませんのでご了承いただければと思います。

処理フローは以下の様にします。

処理フロー

・グレースケール化
・二値化

一つずつ見ていきます。

グレースケール化

グレースケール化は、画像のチャンネル数を3から1に変換する処理です。簡単にいうと、カラー画像からモノクロ画像へ変換する処理です。
次の処理の入力がモノクロ画像でなければいけないので、本処理を入れます。
今回のケースでは、オブジェクトの「形体」、つまり「欠けているかどうか」が分かれば良いので、カラー画像でなくても問題ないといえます。

コード1行で実装完了です。簡単ですね。

              
                import cv2

                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
              
            
図3 グレースケール化処理後の画像

二値化

先に少し述べましたが、二値化とは、画像を0(黒)と1(白)の2つのみに変換する画像処理です。閾値を決め、その閾値に従って処理を行います。

              
                import cv2

                ret, img_thresh = cv2.threshold(img, threshold=220, max_value=255, threshold_type=cv2.THRESH_BINARY)
              
            

threshold_type(二値化処理のタイプ)は種類があり、今回はcv2.THRESH_OTSUとcv2.THRESH_BINARYを使用します。

v2.THRESH_BINARYは、自分で閾値を指定するタイプです。
thresholdに閾値を設定します。閾値より大きい値がmax_valueの値に変換され、それ以外の値が0に変換されます。
戻り値のretはthresholdの値(この場合220)が返されます。
img_threshは二値化された画像の配列が出力されます。

cv2.THRESH_OTSUは、入力画像に応じて自動で閾値を決定するタイプです。

              
                import cv2

                ret, img_thresh = cv2.threshold(img, threshold=0, max_value=255, threshold_type=cv2.THRESH_OTSU)
              
            

cv.THRESH_OTSUの場合、引数のthresholdの値は関係ありません。
戻り値のretは自動で設定された閾値が返されます。

おすすめとしては「まずcv2.THRESH_OTSUでやってみて、うまくいかない場合はcv2.THRESH_BINARYで最適な閾値をがんばって探す」やり方です。
最適な閾値は、入力画像によって変わるので、探すのが結構大変だったりします。まずcv2.THRESH_OTSUで画像を確認してみることをお勧めします。

図4 二値化処理後の画像

図4を見ると、二値化がうまくいっている様です。
尚、この際のretの値(算出された閾値)は218です。

一応、今回はcv2.THRESH_BINARYで複数の閾値をした場合もやってみます。

              
                import cv2

                ret1, img_thresh1 = cv2.threshold(img, threshold=210, max_value=255, threshold_type=cv2.THRESH_BINARY)

                ret2, img_thresh2 = cv2.threshold(img, threshold=218, max_value=255, threshold_type=cv2.THRESH_BINARY)

                ret3, img_thresh3 = cv2.threshold(img, threshold=230, max_value=255, threshold_type=cv2.THRESH_BINARY)
              
            
図5 二値化処理後の画像(3つの閾値)

以上が、おすすめの1つ目「画像の単純化」でした。
次回は「細かい黒点ノイズ除去」を行います。

・物体検出AIの導入
・アノテーションサービス
・手書き計算サイト ZONE++ の運営
・技術ブログ LAB++の運営
   上記をメインにおこなっております

詳しくはこちら

Category

Search