画像認識・物体検出の為の画像前処理
【No.8】実践(opencvを使用) 画像の水増し処理
1. 簡単な処理フロー
画像認識や物体検出、文字認識に関して、筆者がおすすめの前処理を3つピックアップし、opencvを使用して実際に処理を行ってみようと思います。
前々回は実践1「画像の単純化(物体検出)」を行いました。
前回は実践2「細かい黒点ノイズ除去(文字認識)」を行いました。
今回は実践3「画像の水増し処理」を行っていきます。
以下に今回の簡単な処理フローを記載します。
・ガウシアンフィルターでの平滑化処理(独立処理)
・鮮鋭化(独立処理)
※(独立処理)→一つの処理で完結
2. 画像の水増しについて
おすすめの前処理の3つ目として、画像の水増し処理があります。
まず「画像の水増しとは?」という方向けに、簡単に説明します。
画像の水増し処理とは、「画像に対して何かしらの画像処理を施し、画像数を増やすこと」です。
画像認識や物体検出において、学習に使う画像データは非常に膨大です。
AIモデルの性能は日々上がっており、以前より学習データが少なくなってきているとはいえ、やはり精度をある程度出そうとすると、数百から数千のデータ数は必要になるケースが多いです。
そういった中で、学習データを収集することが困難な場合があると思います。
例えば、筆者の経験したものでは「信号機の黄色が点灯している画像」の取得が大変でした。青信号や赤信号は結構容易に収集できるのですが、黄色信号は他2つに比べデータ数が圧倒的に不足してしまっている...みたいなことがありました。
その際、がんばって黄色信号の画像を集めるのもありですが、「元の黄色信号の画像を少しだけ変えて出力し、それを使ってしまおう」というのがここで紹介する水増しです。
水増しでよくあるのが拡大・縮小や回転、平行移動といった幾何変換処理だと思います。
ただ、ここではそれらは使用しません。理由として以下があります。
特に縮小については、縮小したサイズと元のサイズの間のパディングをどうするか問題がある(0パディングだとその部分が真っ黒になり、精度に影響する可能性あり、等)。
・回転に関して、上記の信号機の例をあげると、信号が縦になっていたり、反転していることはあり得ないので、学習データとして適切でない可能性がある。
・平行移動に関して、縮小と似ているが、移動した部分のパディングをどうするかが悩ましい。
上記は、もちろんケースバイケースで使用することもありますし、上記の処理が、データの水増し処理に不向きと言うことを申し上げるつもりは全くありません。
むしろケースによってはもっとも適切な処理かもしれません。あくまでも「筆者の経験上、上記に当てはまる様なケースが多かった為、今回はお勧めしない」と言うことです。ご了承ください。
具体的な水増し処理として、おすすめの処理がガウシアンフィルターでの平滑化と鮮鋭化です。
おすすめの理由として、どちらも処理の実装が楽(コード数行で完結する)だからです。
3. 処理の実装
では、実際に処理を構築していきたいと思います。
今回はPythonのopencvを使用します。opencvについての説明は割愛させていただきます。また、各関数の全ての引数を記載することはしませんのでご了承いただければと思います。
今回、図2の画像を水増ししていきたいと思います。
処理フローは以下の様にします。
処理フロー・ガウシアンフィルターでの平滑化処理(独立処理)
・鮮鋭化(独立処理)
※(独立処理)→一つの処理で完結
今回は各処理を個別に実施し、それぞれの出力画像を持ち合わせて水増し画像とする形です。
一つずつ見ていきます。
ガウシアンフィルターでの平滑化処理
ガウシアンフィルターでの平滑化処理を使用し、「ぼかし」画像を生成していきます。「平滑化」がわからない方はこちらを参照いただければと思います。
カーネルを複数個設定し、それぞれで作成します。
今回は3×3、5×5、7×7の3パターンを生成してみようと思います。
import cv2
Gblur_img_1 = cv2.GaussianBlur(img, ksize=(3, 3), sigmaX=1)
Gblur_img_2 = cv2.GaussianBlur(img, ksize=(5, 5), sigmaX=1)
Gblur_img_3 = cv2.GaussianBlur(img, ksize=(7, 7), sigmaX=1)
ksizeでカーネルのサイズを(幅, 高さ)で指定します。
幅、高さの値は正の奇数の値にします。
sigmaXは、X方向の標準偏差の値を指定します。ここでは詳しい解説は割愛しますが、この値を大きくすることでより「ぼかし」が強くなります。
sigmaYもありますが、省略可能ですので、ここでは省略します。
省略するとsigmaX=sigmaYとなります。
ノイズ除去の際には、カーネルサイズが大きくなればsigmaXやsigmaYも大きくしたりと、要検証しなければいけないパラメータですが、ここでは、目的が「ある程度ぼかされている画像を大量に出力する」ことなので、ここではsigmaX=1で固定し、各カーネルで変化をつけるようにします。
目視では分かりずらいかもしれませんが、それぞれ少しずつ平滑化されています。
鮮鋭化
平滑化とは逆に、鮮鋭化でエッジがはっきりした画像を生成していきます。「鮮鋭化」がわからない方はこちらを参照いただければと思います。
カーネルを複数個設定し、それぞれで作成します。
ガウシアン同様、3×3、5×5、7×7の3パターンを生成してみようと思います。
import cv2
k = 1
kernel1 = np.array([
[-k/9, -k/9, -k/9],
[-k/9, 1+(8*k/9), -k/9],
[-k/9, -k/9, -k/9]])
kernel2 = np.array([
[-k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, 1+(24*k/9), -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9]])
kernel3 = np.array([
[-k/9, -k/9, -k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, 1+(48*k/9), -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9, -k/9, -k/9],
[-k/9, -k/9, -k/9, -k/9, -k/9, -k/9, -k/9]])
sharpen1 = cv2.filter2D(img, ddepth=-1, kernel=kernel1)
sharpen2 = cv2.filter2D(img, ddepth=-1, kernel=kernel2)
sharpen3 = cv2.filter2D(img, ddepth=-1, kernel=kernel3)
ddepthで出力画像の型を指定します。-1の場合は入力画像と同じ型を使用します。
kernelに鮮鋭化用のカーネルを指定します。
上記のkernel1, kernel2, kernel3はkの値を変えることでエッジの具合を調節できます。今回はk=1で処理を行っています。
少しずつ鮮鋭化されていますね。
この様にして、データの水増しを行います。本処理もなかなか泥臭い作業ですが、精度に関わる重要な部分化と思いますので、休み休み頑張りましょう!
以上が、おすすめの3つ目「画像の水増し処理」でした。
Link
Search