PR

カスケード分類器とは?ラズパイとUSBカメラを使った顔検出で試してみた

こんにちは!メカ旦那です。

スマホの本人認証など、様々な所で顔認識が使われています。
仕組みとしては、画像の中から顔を検知 → 誰の顔か認識する という流れですが、最初の検知に関しては、「カスケード分類器」という手法が使われています。ラズパイとUSBカメラを使って手軽にできるので、ザックリ解説します!

カスケード分類器について

カスケード分類器とは?

顔認識は一般的に、画像入力→顔検出→特徴量算出→顔認識の流れになります。

カスケード分類器はこの中でも「顔検出」に使われる技術です。
顔というのは、目や口など明暗差が特徴的な部分が存在します。人種、性別、年齢、表情に関係なく共通で、他の物体には無いので、これを画像の中から探し出す事で、顔が検知できます。

とはいえ、個々人で違いはありますし、顔に似たような模様もあります。ですので、カスケード分類器ではたくさんの顔画像と、そうではない画像をPCに機械学習させます。PC自身で判別基準(識別器)を作成し、改善を重ねる事で検知精度を高めています。

カスケード分類器とディープラーニング(AI)の違い

メカ坊や
メカ坊や

顔検出と言うと、AIやディープラーニングもよく聞きますが、どんな違いがあるんですか~?

メカ旦那
メカ旦那

カスケード分類器もディープラーニングも機械学習の一種なので、正解・不正解を学習する点では共通しています。しかし、ディープラーニングはより複雑なものを認識できるので、誰かを判別する「顔認識」で使われています!

カスケード分類器とディープラーニングの違い
  • カスケード分類器  
    1. 処理速度が速い
    2. CPUでの演算量が少ない
    3. 顔、食べ物等の異なる物体の同時検知は苦手
    4. リアルタイムでの顔検出に利用される。
  • ディープラーニング
    1. 複雑な認識ができる
    2. 認識精度が高い
    3. アルゴリズムがブラックボックス化され、判断や予測の根拠がわかりにくい
    4. 機械翻訳や顔認識に利用される。

カスケード分類器の種類

画像のどの特徴を捉えるのかによって、カスケード分類器はいくつか種類があります。

後ほどラズパイを用いて顔検知しますが、今回はHaar-Likeを用います。
どれも、OpenCVというライブラリにオープンソースがあり、Git Hubで公開されてますので、リンクを上記に貼っておきます。

またHaar-Like の中でも、顔だけでなく、身体全体や猫の顔が検知できるソースもあります。Git Hub内にありますので、良かったら探してみてください。

ラズパイとUSBカメラで顔検出してみた

メカ坊や
メカ坊や

カスケード分類器、説明だけだとまだピンと来ないですね・・・

メカ旦那
メカ旦那

それなら実際に手を動かしてみましょう!カスケード分類器はOpenCVを使うことで、簡単に実行できます!今回は電子工作でもよく使われるマイコン Raspberry Pi(ラズパイ)と一般的なUSBカメラを用いて実行してみました。

使うもの

使うのは、Raspberry Pi 3 a+、USBカメラ、パソコンです。
USBカメラはリモート会議でも使われるような一般的なものです。
カメラはラズパイとUSBで接続します。
撮影動画はラズパイに送り、画像内の顔を検知、四角い枠で囲むという流れになります。

ちなみに、USBカメラの代わりに、ラズパイ用カメラモジュールでもできるらしいですが、私はどうにも上手くいきませんでした・・・ご興味のある方はお試し下さい。こちらはラズパイ3,4向けのモジュールです。

コード

次に、ラズパイに書き込むコードになります。
言語はPythonを用いて、ラズパイ用エディタ「Geany」に入力しています。

import cv2 
 
haar_file = \
"/usr/local/lib/python3.9/dist-packages/cv2/data/"\
"haarcascade_frontalface_alt.xml"

cascade = cv2.CascadeClassifier(haar_file)

cap = cv2.VideoCapture(0)

while(True):
    ret, frame = cap.read()
    
    face = cascade.detectMultiScale(frame)

    for x, y, w, h in face:
        cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),1)

    cv2.imshow('Capture',frame)
  
    if cv2.waitKey(1) & 0xff == ord('q'):
  
        break

cap.release()
cv2.destroyAllWindows()

各関数について解説します。

import cv2 

今回はカスケード分類器を用いて画像検知を行います。1からコードを作るのは大変なので、ライブラリ 「OpenCV」 を利用します。インポートする際のライブラリ名は「cv2」になります。

haar_file = \
"/usr/local/lib/python3.9/dist-packages/cv2/data/"\
"haarcascade_frontalface_alt.xml"

カスケード分類器には様々な種類がありますが、今回は「Haar-like」を用います。Git Hubで公開されている顔検知ソースのパス名を引数「haar_file」に代入します。

メカ旦那
メカ旦那

ちなみに身体全体や、猫の顔が検知できるソースもあり、同じくGit hubで公開されています。こちらにアクセス後、お好きなファイル名をクリックし、右上の「・・・」からパスをコピーします。上記 “haarcascade_frontalface_alt.xml” を書き換えれば完了です。良かったら試してみてください!

cascade = cv2.CascadeClassifier(haar_file)

カスケード分類器を使うには、cv2のクラス「CascadeClassifier」を使います。先ほどの「haar_file」を引数として代入し、インスタンス「cascade」を生成します。

cap = cv2.VideoCapture(0)

撮影写真を取得するには、cv2のクラス「VideoCapture」を使用します。引数はカメラのデバイスIDになりますが、1台しかカメラ接続していない場合は「0」になります。0を代入し、インスタンス「cap」を生成します。

ここまでは、撮影までの準備です。
この後、実際に撮影→顔検知→画像加工→表示までの処理を行います。

while(True):

無限ループを使い、撮影画像を絶えず読み込み、顔検知するようにします。
後ほど説明しますが、「break」が実行されるまでは処理がずっと続きます。

  ret, frame = cap.read()

「read」を用いて、撮影画像を第2戻り値の「frame」に格納します。ちなみに第1戻り値の「ret」には画像取得の成功可否を2値(True / Fales)で格納します。

  face = cascade.detectMultiScale(frame)

「detectMultiScale」を用いて、画像内の顔を検知します。
変数「face」には、顔の左上座標(x,y)、幅(w)、高さ(h)が配列の形で格納されます。([x, y, w, h])

    for x, y, w, h in face:
        cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),1)

顔の周囲を四角で囲みます。以下、「rectangle」の引数です。

rectangle (img, pt1, pt2, color, thickness)
引数説明補足
img入力画像今回は「frame」です。
pt1左上座標(x,y) です。
pt2右下座標(x+w, y+h) です。
左上を起点にして、幅w、高さhを加算した値になります。
color線の色B,G,Rの順に256階調で指定します。
今回は赤色にしたいので、B,Gを0、Rを255にします。
thickness線の太さピクセルで指定します。
今回は1と記載していますが、入力は必須ではありません。
デフォルトでは1が格納されています。
  cv2.imshow('Capture',frame)

「imshow」を用いて、別ウインドウを立ち上げ、画像をPC上に表示します。
第1引数にはウインドウ上部のタイトル名を入力しますが、今回は「Capture」にします。

さてこれで顔検知ができました。
ここからは、終わらせる処理です。

  if cv2.waitKey(1) & 0xff == ord('q'):
   break

キーボードでqを押すと、「break」で無限ループから抜け出します。
「cv2.waitKey(1) & 0xFF == ord(‘q’):」は少し複雑なので、後ほど説明します。

cap.release()
cv2.destroyAllWindows()

「release」でカメラデバイスを解放します。
(冒頭で設定したVideoCaptureをリセットするイメージです。)
最後に「destroyAllWindows」でウインドウが消えて処理が終了します。

実際に動かしてみると

コードをラズパイに書き込み、実行してみます。
ラズパイとリモートデスクトップで繋いだパソコンで、画像を表示してみました。

恥ずかしがり屋なので顔は隠してますが、実際に検知され赤い四角で囲われています。

cv2.waitKey(1) & 0xFF == ord(‘q’) とは?

先ほど説明を省略したこちらについて解説します。

waitKey()

■OpenCVの関数で、キー入力した値をUnicodeのコードポイント(整数)で戻します。
■Unicodeとは世界中の文字をPCが認識できるよう固有の数字を割り当てた仕組みで、コードポイントはその数字になります。一覧はこちらで確認できます。
■例えば、「q」を入力すると71が戻されます。
■戻した値のbit数はラズパイのOSに依存します。
そのため、32 bit か 64 bit のどちらかになります。

例えばRaspbian 64bit版をインストールしていた場合、ラズパイ側では64 bit で認識します。
■引数はキー入力した値を取得する時間間隔 [ms] で、今回は 1 ms 毎になります。

ord()

■Pythonの関数で、引数のUnicodeのコードポイント(整数)を戻します。
■例えば引数を「’q’」にすると、71が戻されます。
■戻した値のbit数は 8 bitになります。
(Pythonの標準文字コードがUTF-8のため)

メカ旦那
メカ旦那

つまり、キーボードで入力した値(waitKeyで取得した値)が、ordで指定した値と同じになれば、処理を実行するという意味になります。今回ですと「q」を入力すれば、処理を実行します。

メカ坊や
メカ坊や

なるほど!
でも「& 0xFF」とは何でしょうか?
cv2.waitKey(1) == ord(‘q’) ではダメでしょうか?・・・

メカ旦那
メカ旦那

戻り値のbit 数に着目すると、waitKey は32か64、ord は8でした。つまり、bit数が異なるので、合わせる必要があります。「& 0xFF」を加える事で、waitKey と0xFFで論理積を取り、waitKey の下位 8 bit のみを取得しています。

まとめ

カスケード分類器について理解できましたでしょうか?
簡単な電子工作で実感できますので、ぜひ試してみてください!

メカ旦那

旧帝大工学部を卒業後、FA業界で電気制御設計職に従事しています。
電験三種も保持しています。
難しい電気電子を少しでもわかりやすく解説し、皆さんのお役に立てれば幸いです!

メカ旦那をフォローする
Raspberry Pi

コメント