Help us understand the problem. What is going on with this article?

Raspberry Pi CameraモジュールとOpenCVで人間の顔を認識する

More than 1 year has passed since last update.

環境

  • Raspberry Pi 3B (Raspbian 9.4) + Camera Module V2
  • OpenCV 3.4.3

OpenCVのインストール

インストールはチュートリアルのとおりでいけました。
https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html

NOTE: aptリポジトリにもpython-opencvがあるので、古いバージョンでもよければこちらを入れた方が簡単ですね。検出性能は...低いかもしれません。

pi@raspberrypi:~$ apt search python-opencv
ソート中... 完了
全文検索... 完了  
python-opencv/stable 2.4.9.1+dfsg1-2 armhf
  Python bindings for the computer vision library

必要パッケージのインストール

# apt-get install build-essential
# apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
# apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

OpenCVのダウンロード

最新のソースはこちらから

# curl -OL https://github.com/opencv/opencv/archive/3.4.3.zip
# unzip 3.4.3.zip
# cd opencv-3.4.3/

ビルドとインストール

makeにはかなり時間がかかります。私の環境では一度端末がフリーズしてしまいました...

# # IN "opencv-3.4.3/" DIR
# mkdir build
# cd build
# cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
# make
# make install

Pythonのモジュールは /usr/local/lib/PYTHON_VERSION/dist-packages/ にインストールされています。

pi@raspberrypi:~$ ls /usr/local/lib/{python2.7,python3.5}/dist-packages/
/usr/local/lib/python2.7/dist-packages/:
cv2.so

/usr/local/lib/python3.5/dist-packages/:
cv2.cpython-35m-arm-linux-gnueabihf.so

インストールの検証

$ python3 -c "import cv2; print(cv2.__version__)"
3.4.3

カメラの準備

接続とデバイスの有効化

カメラモジュールを接続したら、デバイスを有効化する必要があります。
カメラの接続方法とGUIからデバイスを有効化する方法は公式ページにステップバイステップ手順があります。
https://projects.raspberrypi.org/en/projects/getting-started-with-picamera

コマンドラインから行う場合は、カメラ接続後 raspi-config を起動して "5 Interfacing Options" ---> "P1 Camera" メニューでカメラデバイスを有効化します。設定変更後デバイスを再起動します。

$ sudo raspi-config

カメラのテスト

$ raspistill -v -o test.jpg

カメラで撮った画像から人間の顔を探す

公式の顔検出サンプルコードは下記URLにあります。下記コードは公式のサンプルコードを一部改変したものです。
https://docs.opencv.org/3.4.3/d7/d8b/tutorial_py_face_detection.html

Cameraモジュールで写真を撮る

Cameraモジュールを起動してカレントディレクトリに写真を保存します。
写真を撮る前に3カウントしています。

pic_file = './picamera_work.jpg'
if os.path.exists(pic_file):
    os.remove(pic_file)

camera = PiCamera()
for i in range(3, 0, -1):
    print(i)
    sleep(1)
camera.capture(pic_file)

camera.capture(output)はカメラで撮った写真をoutputで指定されたパスに保存します。ファイルパスの代わりにファイルオブジェクトを渡すこともできます。
保存する画像のフォーマットはファイル名から推測してくれます (例: *.jpg=>JPEGファイル)。format=FORMATで指定することもできます。
https://picamera.readthedocs.io/en/release-1.13/api_camera.html#picamera

OpenCVの学習済分類器をロードする

OpenCVでは学習済の分類器をいくつか提供しています。
分類器は/usr/local/share/OpenCV/haarcascades/にインストールされています。

分類器をロードするにはCascadeClassifierコンストラクタにパラメータとして渡すか、loadメソッドを別途呼び出します。
下記コードサンプルでは目と顔の分類器をそれぞれロードしています。

face_classifier = "/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(face_classifier)
eye_classifier = "/usr/local/share/OpenCV/haarcascades/haarcascade_eye.xml"
eye_cascade = cv2.CascadeClassifier(eye_classifier)

顔を検出する

detectMultiScaleで顔を検出します。
http://opencv.jp/opencv-2.1/cpp/object_detection.html#cv-cascadeclassifier-detectmultiscale
detectMultiScaleは与えられた画像を縮小しながら何度も検出を試みます。パラメーターscaleFactorには画像の縮小量を設定します。
最終的に検出される物体は少なくともminNeighborsで指定された分だけ近傍矩形を含む必要があります。minNeighborsに小さな値を指定すると検出できる割合は増えますが、誤検出も増えます。

pic = cv2.imread(pic_file)
pic_gray = cv2.cvtColor(pic, cv2.COLOR_BGR2GRAY)

faces = face_cascade.detectMultiScale(
    pic_gray,
    scaleFactor=1.1,
    minNeighbors=5,
    minSize=(30, 30))

scaleFactorの働きはこちらのQAが理解の助けになりました。
http://answers.opencv.org/question/10654/how-does-the-parameter-scalefactor-in-detectmultiscale-affect-face-detection/

検出したオブジェクトを表示する

検出した顔のエリアを線で囲っています。さらにそのエリア内で目を検出して、見つかった場合はそのエリアも線で囲います。

for (x, y, w, h) in faces:
    cv2.rectangle(pic, (x, y), (x + w, y + h), (255, 0, 0), 2)
    roi_gray = pic_gray[y:y+h, x:x+w]
    roi_color = pic[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_gray)
    for (ex, ey, ew, eh) in eyes:
        cv2.rectangle(roi_color, (ex,ey), (ex+ew,ey+eh), (0,255,0), 2)

cv2.imshow("Raspberry pi camera detects {0} faces".format(len(faces)), pic)
cv2.waitKey(0)
cv2.destroyAllWindows()

コード全体

from picamera import PiCamera
from time import sleep
import cv2
import os
import sys

pic_file = './picamera_work.jpg'

if os.path.exists(pic_file):
    os.remove(pic_file)

camera = PiCamera()
for i in range(3, 0, -1):
    print(i)
    sleep(1)
camera.capture(pic_file)

face_classifier = "/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(face_classifier)
eye_classifier = "/usr/local/share/OpenCV/haarcascades/haarcascade_eye.xml"
eye_cascade = cv2.CascadeClassifier(eye_classifier)

pic = cv2.imread(pic_file)
pic_gray = cv2.cvtColor(pic, cv2.COLOR_BGR2GRAY)

faces = face_cascade.detectMultiScale(
    pic_gray,
    scaleFactor=1.1,
    minNeighbors=5,
    minSize=(30, 30))

for (x, y, w, h) in faces:
    cv2.rectangle(pic, (x, y), (x + w, y + h), (255, 0, 0), 2)
    roi_gray = pic_gray[y:y+h, x:x+w]
    roi_color = pic[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_gray)
    for (ex, ey, ew, eh) in eyes:
        cv2.rectangle(roi_color, (ex,ey), (ex+ew,ey+eh), (0,255,0), 2)

cv2.imshow("Raspberry pi camera detects {0} faces".format(len(faces)), pic)
cv2.waitKey(0)
cv2.destroyAllWindows()

おまけ

imwriteを使って切り出した顔を画像として保存することもできます。

count = 0;
for (x, y, w, h) in faces:
        count += 1
        cv2.rectangle(pic, (x, y), (x + w, y + h), (255, 0, 0), 2)
        roi_gray = pic_gray[y:y+h, x:x+w]
        roi_color = pic[y:y+h, x:x+w]
        cv2.imwrite("face_{0}.jpg".format(count), roi_color)
        ...
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away