環境
- 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)
...