OpenCVを利用したPythonでの画像処理を引き続き見て行きましょう。
ここでは画像の2値化処理を見て行こうと思います。画像データの2値化処理とは画像の特徴的な部分、関心のある部分を抽出するように変換する処理です。単純に考えると複雑なデータであるカラー画像を白黒画像の2つのデータに変換することです。ただ、そのままグレースケールに変換して表示するだけでは2値化処理にはなりません。
画像の2値化処理には適切な閾値(しきい値)を設定して処理する必要があります。二値化画像の処理は、画素値を8ビットの0(黒)と255(白)で表すのが一般的な方法です。しきい値を例えば2つの中間になる127などに設定して、そこからどっちに近い画像なのかを表すことになります。
カラー画像を使って簡単に2値化処理を見て行きましょう。
画像の閾値処理
ここでは、こちらの画像を使っていきます。作業ディレクトリにimagesフォルダを作って、color.jpgとしています。
jupyter notebookを使って読み込んでいきましょう。
各種ライブラリーをインポートします。
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
画像をimread()で読み込み、imshow()で表示します。
img = cv2.imread('images/color.jpg')
plt.imshow(img)
表示しするとこうなります。
。
RGBではなくBGRで読み込まれているので、元と色が違うのはこれまでも扱ったことなのでわかると思います。
この画像を白黒で読み込んでみましょう。それにはパラーメータを0にとってcmapをgrayにして表示します。
img = cv2.imread('images/color.jpg', 0)
plt.imshow(img,cmap='gray')
表示してみるとこうなります。
これは単純にグレースケールになっただけなので、特徴を取り出すまでにはなっていません。以下で、いろんな閾値処理をして2値化していきましょう。
様々な画像の2値化
画像を2値化するにはthreshold()を使いますが、様々なタイプがあります。パラメータを指定することで変更することができます。
以下、順にみていきましょう。
THRESH_BINARY
threshold()に2値化の手法としてTHRESH_BINARYを指定してみましょう。
THRESH_BINARYは閾値を超える画像ピクセルは最大値に、それ以外のピクセルは0になります。
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
threshold()に画像imgを指定します。閾値をここでは0と255の中間である127を指定してみます。2値化の最大値を255として、THRESH_BINARYを指定しています。変数ret、thresh1はそれぞれデータの分離度が高くなるフィルタとしての閾値(大津の2値化)、画像データが格納されます。
plt.imshow(thresh1,cmap='gray')
グレースケールで画像を表示してみましょう。
閾値を超えた部分が白、それ以外は黒になっているのがわかります。
THRESH_BINARY_INV
次はTHRESH_BINARYの逆のパターンでTHRESH_BINARY_INVです。
同様にやってみましょう。
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
plt.imshow(thresh2,cmap='gray')
手法をTHRESH_BINARY_INVに指定した以外は全く同じです。
THRESH_BINARYと逆になっているのがわかります。
THRESH_TRUNC
次は、THRESH_TRUNCです。Truncationのことで閾値の切り捨てになります。
THRESH_TRUNCは閾値を超える画像ピクセルは閾値に、それ以外のピクセルは変更なしとなります。
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
plt.imshow(thresh3,cmap='gray')
2値化の手法以外はそのままでやってみましょう。
閾値を超えた部分だけが白くなっているのがわかります。
THRESH_TOZERO
次は、THRESH_TOZEROです。
THRESH_TOZEROは閾値を超える画像ピクセルは変更はなく、それ以外のピクセルは0となります。
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
plt.imshow(thresh4,cmap='gray')
同様に表示してみます。
閾値を超えていない部分が全て黒になりました。
THRESH_TOZERO_INV
次はTHRESH_TOZERO_INVで、THRESH_TOZEROの逆になります。
閾値を超える画像ピクセルは0となり、それ以外のピクセルはそのままとなります。
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
plt.imshow(thresh5,cmap='gray')
実行するとこうなります。
THRESH_TOZEROと逆になっているのがわかります。
現実的な場面の処理 – 適応的閾値処理
これまでは単純な閾値処理でしたが、今度は現実的な画像を使って適応的閾値処理を行ってみましょう。
newspaper.jpgとして次のような新聞の画像を用意してみました。
画像を読み込んで行きましょう。
img = cv2.imread("images/newspaper.jpg", 0)
パラメータを0に指定して白黒画像として読み込んでいます。
ここで今回は画像を表示する関数を定義してみます。
def show_img(img):
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111)
ax.imshow(img,cmap='gray')
関数名をshow_img()と定義します。figure()を使ってサイズを適当なスペクト比を指定してオブジェクト化しています。このあたりはMatplotlibの項目で扱ったところです。続けてadd_subplot()で表示する位置を左上(1行1列の1番目)を指定しています。これをimshow()で表示する関数となっています。
show_img(img)
関数を実行して表示してみましょう。
これを、これまでやったようにTHRESH_BINARYで読み込んでみます。
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
show_img(th1)
定義した関数を実行して画像表示する以外はこれまでと同じです。
単純に白黒で2値化されました。白黒がはっきりしてしまって、汚れなどが逆に強調されて見にくくなっています。
これを考慮したのが適応的閾値処理です。
適応的閾値処理
適応的閾値処理にはadaptiveThreshold()を使います。
画像を渡し、閾値の最大値を指定し、適応処理の方法としてADAPTIVE_THRESH_MEAN_Cか、あるいはADAPTIVE_THRESH_GAUSSIAN_Cを使います。そして閾値処理の方法としてTHRESH_BINARYか、あるいはTHRESH_BINARY_INVを指定します。そして、ピクセルに対する閾値を計算するためにの近傍領域のサイズ(3, 5, 7など)を指定し、手法に依存するパラメータ(平均あるいは荷重平均から引かれる定数)を渡します。
詳細は下のドキュメントなどを確認してください。
ADAPTIVE_THRESH_MEAN_C
では、実際に読み込んでみましょう。
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,8)
show_img(th2)
ADAPTIVE_THRESH_MEAN_C、THRESH_BINARYを使っています。ここでは近傍サイズを11、定数を8にしてみました。
読み込んでみましょう。
ADAPTIVE_THRESH_GAUSSIAN_C
今度はADAPTIVE_THRESH_GAUSSIAN_Cを使ってやってみましょう。
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,15,8)
show_img(th3)
近傍サイズを15に変えてみました。
読み込むとこうなります。
このように微妙な違いの画像が表示できました。これを色々と合成することで見やすい表示にすることができます。
画像の合成
addWeighted()を使って画像を合成してみましょう。ここではth1とth2を合成してみます。
blended_img = cv2.addWeighted(src1=th1,alpha=0.7,src2=th2,beta=0.3,gamma=0)
show_img(blended_img)
画像の合成についてはこちらを復習しましょう。
表示してみましょう。
一番見やすい感じにはなったかな? もっといい画像素材を使ったり、パラメータを変えてみるともっと違いがはっきりしてわかりやすいものになると思うので、色々と試してみるのがいいと思います。説明が雑にもなっているのも申し訳無いです。
最後に
OpenCVを利用したPythonでの画像処理について、ここでは画像の2値化処理を見てきました。
画像の2値化処理には適切な閾値(しきい値)を設定して処理する必要があります。
単純な閾値処理は白と黒を使って特徴を際立たせることができますが、微妙な特徴が取り出せないことがあるので、現実的には適応的閾値処理を行って、画像の合成などの処理を行います。