学生エンジニア小話

プログラミングのお話

RaspberryPiとOpenCVで顔認識してパフォーマンスも改善してみる

大学でロボットを作る機会ができたので家で埃をかぶっていたPlaystationEyeを使って顔認識を試して見た。

使ったもの

セットアップ

  • sudo apt-get install libopencv-dev
  • sudo apt-get install python-opencv
  • wget http://eclecti.cc/files/2008/03/haarcascade_frontalface_alt.xml
  • USBにカメラを挿す

簡単なコード

import cv2.cv as cv
import cv2

cv.NamedWindow("camera", 1)

capture = cv2.VideoCapture(0)
faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')

while True:
    _, img = capture.read()
    img = cv2.resize(img, (320, 240))
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=3,
        minSize=(30, 30),
        flags=cv.CV_HAAR_SCALE_IMAGE
    )
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
    cv2.imshow("camera", img)
    if cv.WaitKey(10) > 0:
        break
cv.DestroyAllWindows()

ここで使われている顔認識のアルゴリズムは訓練済みデータを用いたHaar Cascadesというアルゴリズムらしいです。

qiita.com

ちょっと改善してみる

このままでも顔認識されているのが確認できますが、かなりカクカクしていると思います。

ロボットに使うことを考えて、用途によりますがリアルタイムで顔が認識されている必要はないかもしれません。そこで次の戦略でプレビューを滑らかにします。

  • 30フレームに1回顔認識する。1回認識されてから次の30フレームはそこにいると仮定する。
  • 顔認識の処理は別スレッドで行う

ビデオは大体30fpsだと思うので1秒間に一度程度で顔認識を行います。また、この程度の解像度なら1秒以内に顔認識の処理は終わるのでスレッドで溢れてしまうことはないはず。(テストなので今回はマルチスレッドのデータレースなども気にしない)

改善後コード

import cv2.cv as cv
import cv2
import threading

cv.NamedWindow("camera", 1)

capture = cv2.VideoCapture(0)

count = 0
faces = []

class DetectThread(threading.Thread):
    def __init__(self, img, faces):
        super(DetectThread, self).__init__()
        self.img = img
        self.faces = faces
    def run(self):
        gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        detectedFaces = cv2.CascadeClassifier('cascade.xml').detectMultiScale(
            gray,
            scaleFactor=1.1,
            minNeighbors=3,
            minSize=(30, 30),
            flags=cv.CV_HAAR_SCALE_IMAGE
        )
        self.faces[:] = detectedFaces

while True:
    _, img = capture.read()
    img = cv2.resize(img, (320, 240))
    if count == 30:
        thread = DetectThread(img, faces)
        thread.start()
        count = 0
    else:
        count += 1
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
    cv2.imshow("camera", img)
    if cv.WaitKey(10) > 0:
      break
cv.DestroyAllWindows()

実際にこのコードを大事な場面で行うとデータレースやエラーの処理を考えなければならないのでとりあえず先ほどの戦略でパフォーマンスが改善されているか確認だけということで。

まとめ

PythonOpenCVのおかげでかなり手軽にRaspberryPiで顔認識ができることがわかると思います。また、ロボットなど実際に動作するものを作るときにある程度精度を犠牲にしてパフォーマンスを追求するというのも一つの醍醐味だと思います。