機械学習まであと1歩!写真から顔や物体を取り出すには? 実装編第2回

AI

機械学習まであと1歩!写真から顔や物体を取り出すには? 実装編第2回

みなさんこんにちは。
松下忍です。

OpenCVによって機械学習ができるというですが、いったいどのように実現しているのでしょうか?
OpenCVで機械学習を行うには人物や物体などの入力となる画像と前処理が必要になります。

前回実装編第1回の記事では、前処理のステップ1として、画像の2値化やガウシアンフィルタを行い、これからお話しする「物体認識」の準備としてノイズ除去を行いました。
今回、続きとなる「物体認識」とはどのようなものなのかを明らかにしていきたいと思います。

※前回実装編第1回の記事はこちら
https://future-tech-association.org/2017/12/05/ai_by_opencv/


ステップ2.物体検出をする

認識したい画像を明確にしたら、そこから物体を検出します。
OpenCVではとても簡単に物体を検出できるのですが、検出には画像処理をしながら自力で行う方法と「学習済みモデル」を使って行う方法の2通りあります。

まず、自力で検出を行う方法ですが、「物体を認識」した後、「輪郭を綺麗」にすればよいだけなのです。

物体認識は、OpenCVにある findContoursという機能によって、輪郭を検出してくれます。検出した輪郭は drawContours で表示することができ、抽出した輪郭の対象となる物体をわかりやすく四角形で囲んで描画といったこともできるのです。

OpenCVのプログラムと合わせてひとつひとつ紐解いてみましょう。
ここでは鳥の画像を例に取ってみました。

・パソコンなどにある画像ファイル pathを読み込み、contoured と birdsという変数で取得します。birdsは鳥の輪郭抽出用、contouredは元画像と鳥の輪郭を抽出した結果を合成するためのものです。

birds = cv2.imread(path)
contoured = cv2.imread(path)

・binary_threshold_for_birds では、画像処理がいくつか行われます。この関数には、実装編第1回でおこなった前処理をまとめました。

birds = binary_threshold_for_birds(birds)

簡潔に説明しますと、「画像の2値化」、「2値化画像から背景を白にする」、「2値化画像から抽出したい物体の輪郭を黒で協調化する」、「画像を平滑化(スムーシング)してあいまいな画素を検出から除外する」といったことを行います。プログラムコードではこのように実装しました。

def binary_threshold_for_birds(img):
grayed = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #画像の2値化
under_thresh = 114 #この値より明るい画素(背景)を白にするパラメータ
upper_thresh = 150 #この値より明るい画素を黒で強調する(輪郭強調の)パラメータ
maxValue = 255
th, drop_back = cv2.threshold(grayed, under_thresh, maxValue, cv2.THRESH_BINARY)
th, clarify_born = cv2.threshold(grayed, upper_thresh, maxValue, cv2.THRESH_BINARY_INV)
merged = np.minimum(drop_back, clarify_born)
morphed = morph_and_blur(merged)
return morphed

#画像を平滑化する関数
def morph_and_blur(img):
kernel = np.ones((3, 3),np.uint8)
m = cv2.GaussianBlur(img, (3, 3), 0)
m = cv2.morphologyEx(m, cv2.MORPH_OPEN, kernel, iterations=2)
m = cv2.GaussianBlur(m, (5, 5), 0)
return m

・前処理を行った後の画像birdsから輪郭を検出します。

  contours = cv2.findContours(birds, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

「RETR_EXTERNAL」は最も外側の輪郭を採用します。「CHAIN_APPROX_SIMPLE」は、輪郭を圧縮して冗長な点の情報を削除し、コンピュータのメモリを削減できます。

・検出した輪郭を近似して最適化します。

for c in contours:
approx = cv2.approxPolyDP(c, epsilon, True)

輪郭は複数検出されることもあり、c には検出された輪郭が1つ1つ入っています。
ここで、検出した輪郭を表す点はきれいな直線や曲線になっていないことがあります。
OpenCVには「approxPolyDP」という輪郭を近似して綺麗な線として作る機能があります。「epsilon」は近似精度を指定します。これは、元の曲線と近似曲線の誤差の許容範囲に当たります。また、「True」を指定すると曲線が閉じられます(始点と終点がつながります)。

・検知した輪郭を赤い線で囲み、輪郭がある領域を緑色の四角形で囲みます。

for c in contours:
cv2.drawContours(contoured, approx, -1, (0, 0, 255), 3)
cv2.rectangle(contoured, (x, y), (x + w, y + h), (0, 255, 0), 3)

以下、結果です。上が鳥が写っているpath画像、下が検出画像です。
完全に綺麗にとはいきませんが、鳥が検出できました。画像によって閾値を調整するなどして検出率をあげることができます。

ここまで、画像処理をさせながら自力で物体検出をしてみました。
次に、「学習済みモデル」を用いて検出する方法を追っていきます。
学習済みモデルとは、カスケードファイル(Cascade Classfifier)という、顔や車やペットといった物体の特徴を学習させた特徴データが入っているデータです。OpenCVには顔や体など頻繁に検出が行われる物体について、XML形式のファイルとして用意されています。
カスケードファイルは、自分で作成することもできます。

OpenCVでは、「Haar-like特徴分類器」という機械学習の方法を使って画像から特徴を抽出し、カスケードファイルを作成します(Haar-like特徴分類器に関してはOpenCVの公式ページに載っていますが、専門的な内容となっていますので今回は触れません)。
それでは、物体認識の基本的な流れを追ってみます。

・xmlファイルから顔と目について学習させたカスケード情報を取得します。face_cascade が、取得したカスケード情報の顔、eye_cascade が目にあたります。

   face_cascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)
eye_cascade = cv2.CascadeClassifier(“haarcascade_eye_tree_eyeglasses.xml”)

・パソコンなどにある女性の顔画像ファイル(ここではpathという名前)を読み込んでカラー画像とグレースケール画像を取得します。

   img = cv2.imread(path)
grayed = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

・顔のカスケード情報を元に、グレースケール画像から顔認識を行います。

   faces = face_cascade.detectMultiScale(grayed, 1.3, 5)

・顔として認識した領域に四角形の枠を描きます。

   for (x, y, w, h) in faces:
img = cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)

・目のカスケード情報を元に顔領域から目を抽出します。

   for (x, y, w, h) in faces:
face_color = img[y:y+h, x:x+w]
face_gray = grayed[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(face_gray)

・目として認識した領域に四角形の枠を緑色で描きます。

   for (ex, ey, ew, eh) in eyes:
cv2.rectangle(face_color, (ex,ey), (ex + ew, ey + eh), (0, 255, 0), 2)

・ここまでの結果がimgに格納されていますのでimshowで表示します。

   plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

女性の顔が写っている入力画像pathに対し、顔と両目が抽出できました。

いかがでしたでしょうか?
このように、OpenCVでは簡単に物体を検出できるのです。
OpenCVのほかにも、MacなどのApple系の機器でも物体検出ができます。専用の「iOS SDK」という開発ツールがあり、こちらは笑顔やまばたきの検出までも行えるのです。
ただし、検出できるのは、顔、四角形などの単純な物体、文字と、限られているようです。

今回、「学習済みデータ」のところで出てきたカスケードファイルは、たくさんの画像を元に機械学習させながら作成することができます。
もし、人、車、道路の白線、標識などのカスケードファイルが作成できれば、車の自動運転のシステムを作ることも可能になります(もちろん自力で検出するやり方でも可能ですので、OpenCVの機能では物足りない場合は自力で行ってみるのもいいかもしれません)。

次回実装編第3回では、いよいよ機械学習をさせながらカスケードファイルを作成する方法を紹介します。

参考

  • OpenCVによるAIの実装方法 実装編第1回

https://future-tech-association.org/2017/12/05/ai_by_opencv/

  • OpenCVで機械学習をさせてみた!! 実装編第3回

https://future-tech-association.org/2018/01/09/deep_learning_by_opencv_3/

  • 機械学習のためのOpenCV入門

https://qiita.com/icoxfog417/items/53e61496ad980c41a08e

  • 「顔以外」のものを画像認識する on iOS

https://qiita.com/shu223/items/ffd2202eaf92d342f83d

  • 学習済みカスケードファイル (OpenCV付属)

https://github.com/opencv/opencv/tree/master/data/haarcascades/haarcascade_frontalface_default.xml
https://github.com/opencv/opencv/tree/master/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml

  • Face Detection using Haar Cascades(OpenCVの公式サイト 英語)

http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html#face-detection