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

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

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

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

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

カスケード分類器もディープラーニングも機械学習の一種なので、正解・不正解を学習する点では共通しています。しかし、ディープラーニングはより複雑なものを認識できるので、誰かを判別する「顔認識」で使われています!
カスケード分類器の種類
画像のどの特徴を捉えるのかによって、カスケード分類器はいくつか種類があります。
後ほどラズパイを用いて顔検知しますが、今回は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」の引数です。
引数 | 説明 | 補足 |
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’) とは?
先ほど説明を省略したこちらについて解説します。
■OpenCVの関数で、キー入力した値をUnicodeのコードポイント(整数)で戻します。
■Unicodeとは世界中の文字をPCが認識できるよう固有の数字を割り当てた仕組みで、コードポイントはその数字になります。一覧はこちらで確認できます。
■例えば、「q」を入力すると71が戻されます。
■戻した値のbit数はラズパイのOSに依存します。
そのため、32 bit か 64 bit のどちらかになります。
例えばRaspbian 64bit版をインストールしていた場合、ラズパイ側では64 bit で認識します。
■引数はキー入力した値を取得する時間間隔 [ms] で、今回は 1 ms 毎になります。
■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 のみを取得しています。

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