RaspberryPiとOpenCVで顔認識してパフォーマンスも改善してみる
大学でロボットを作る機会ができたので家で埃をかぶっていたPlaystationEyeを使って顔認識を試して見た。
使ったもの
- Raspberry Pi 3
- OpenCV
- Webカメラ(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
というアルゴリズムらしいです。
ちょっと改善してみる
このままでも顔認識されているのが確認できますが、かなりカクカクしていると思います。
ロボットに使うことを考えて、用途によりますがリアルタイムで顔が認識されている必要はないかもしれません。そこで次の戦略でプレビューを滑らかにします。
- 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()
実際にこのコードを大事な場面で行うとデータレースやエラーの処理を考えなければならないのでとりあえず先ほどの戦略でパフォーマンスが改善されているか確認だけということで。
まとめ
PythonとOpenCVのおかげでかなり手軽にRaspberryPiで顔認識ができることがわかると思います。また、ロボットなど実際に動作するものを作るときにある程度精度を犠牲にしてパフォーマンスを追求するというのも一つの醍醐味だと思います。