2018年7月8日日曜日

PythonでOpenCVを呼び出して、Raspiカメラに写る顔を認識してみた


背景

Raspberry Piとは、数千円で購入できる、Linuxが動くコンピュータです。

OpenCVとは、いろんなOS(Windows, Mac, Linux)で動かせて、いろんなプログラミング言語(C, C++, Java, Python ..)で書ける、画像処理ライブラリです。
Raspberry Piでも動きます。

Raspberry Piで遊ぶ会#18.07の開催にあたり、OpenCVがライブラリを提供している言語のひとつであるpythonを利用して、OpneCVのプログラムを書く方法を共有します。

Pythonを選択する理由は、c言語などに比べると実行速後は劣りますが、実行時にコンパイルが不要なことと、他の言語に比べて少ない記述量でプログラムを動かせるためです。

使うもの

カメラの画像を表示して動作確認したいので、今回はデスクトップPCとしてRaspberry Piを動かす環境を使います。
  • Raspberry Pi + Raspbian +5V電源
  • USBカメラ もしくは Raspカメラ
  • モニタ + HDMIケーブル
  • キーボード + マウス

Raspberry Piを初期設定

以前書いた下記の記事やネットの情報を参考に、Raspberry PiにインストールしたRaspbian(Linux OSの一つ)のWiFiやキーボードの言語などの初期設定を行ってください。

Raspberry PiでLEDの点滅、SSHログインする方法

カメラを設定

USBカメラの場合

Raspberry Piに接続します。

下記のコマンドでデバイスが認識されていれば準備完了です。
ls /dev/video*

Raspiカメラの場合

インストール直後のRaspbianはカメラが無効になっています。
下記の操作でカメラを有効にしてください。

raspi-configを起動します。
sudo raspi-config

下記のように選択して、カメラを有効にします。
5 Interfacing Options -> P1 Camera -> Yex

右矢印でFinishを選択して、racpi-configを閉じます。

Raspberry Piを再起動すると、カメラが有効になります。

カメラが有効になったら、USBカメラと同じように扱えるようにするため、下記のコマンドを実行してRaspiカメラをマウントします。
sudo modprobe bcm2835-v4l2 

下記のコマンドでデバイスが認識されていれば準備完了です。
ls /dev/video*

上記の設定だとRaspberry Piを起動するたびに実行が必要になりますが、「bcm2835-v4l2」を/etc/modulesに追加すると、Raspberry Pi起動時にRaspberry Pi用カメラが自走的にv4l2デバイスとしてマウントされます。
設定を持続させたい場合は、この設定をどうぞ。
sudo vi /etc/modules
/etc/modules
bcm2835-v4l2

間に挟まるプログラムが増えると処理速度が低下するため、このブログのようにpicameraというライブラリを利用する方が速いのだと思います。(未検証です。)
しかし、今回はプログラムの記述量を減らすことを優先したいので、picameraは使わず、USBカメラと同じように扱えるようにしました。
処理速度を上げたい場合は。picmaeraを利用した情報取得を試してみてください。

Python3の環境をインストール

Python3とPythonのパッケージマネージャー(ライブラリをインストールしやすくしてくれる便利プログラム)であるpipを下記のコマンドでインストールします。
sudo apt install python3-pip

OpenCVをインストール

OpenCVのインストールにはいくつかの方法が存在します。
  • OSのパッケージマネージャーでインストール
    2018.07時点のRaspberry Piの場合: 「sudo apt install python-opencv」でPython2系向けにOpenCV2系をインストールできます。
  • 言語のパッケージマネージャーでインストール
    2018.07時点の場合: Python3系なら、最新の安定版3.4.1をインストールできます。
  • ソースコードをダウンロード・コンパイルしてインストール
    好きなようにできますが、ビルドに数時間かかります。
    行う場合は本家のドキュメントが参考になると思います。
    Installation in Linux
後者になるほど手間がかかりますが、最新版を使えたり、環境に最適化させて動作を高速化できたりします。

今回は最新版を使いつつもビルドするよりお手軽にインストールができる、言語のパッケージマネージャーを使う方法を共有します。

OpenCVをソースコードからビルドする手順で紹介されている関連プログラムをインストールします。
sudo apt install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

pipを使ってopencvをインストールします。
sudo pip3 install opencv-python

OpenCVを動かす前に、実行時に利用するプログラムをインストールします。
sudo apt install libcblas-dev libatlas3-base libilmbase12 libopenexr22 libgstreamer1.0-0 libqtgui4 libqttest4-perl

上記の手順でインストールできたら、python3を立ち上げて、OpenCVのバージョンを確認します。
python3
import cv2
cv2.__version__

自分が動作確認したときにインストールできたOpenCVのバージョンは3.4.1でした。


確認できたら Ctr-dを押すか、exit() と入力して、python3を終了します。

必要なプログラムがOSにインストールされていない場合、import cv2を実行したときに下記のようなエラーが出ます。
>>> import cv2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.5/dist-packages/cv2/__init__.py", line 3, in <module>
    from .cv2 import *
ImportError: libQtTest.so.4: cannot open shared object file: No such file or directory
>>>

こうなったときは「debian [エラーになったライブラリ名]」で検索して、出てきたパッケージをインストールすることで解決できます。

上記のエラー場合は「debian libQtTest.so.4」で検索するとlibqt4-testに含まれることが分かったので、
sudo apt install libqt4-test
を実行してプログラムをインストールすると、上記のエラーが出なくなりました。
他にも足りないプログラムがある場合は、別のライブラリ名で同じようなエラーが出ます。その場合も同様に対応すれば、エラーを解決できると思います。

画像表示プログラムを作成・実行

imshowなどの関数を使うと、画像を表示できます。
画像を表示するプログラムを作ってみましょう。

参考: Getting Started with Images

プログラムの作成と実行を繰り返す際は、プログラム編集用のターミナルと、実行用のターミナルを別に立ち上げておくと、編集と実行を繰り返しやすくなるので、便利です。

この記事では、opencv_programsという名前のディレクトリを作り、その中にプログラムを作ることにします。
mkdir ~/opencv_programs
cd ~/opencv_programs

表示する画像としてOpenCVの動作確認に利用されることが多いLenaさんの画像を使います。
wget https://github.com/opencv/opencv/raw/master/samples/data/lena.jpg

プログラムを作成します。
vi show_image.py

show_image.py
import numpy as np
import cv2

img = cv2.imread('lena.jpg')
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

できたら、下記のコマンドで実行できます。
python3 show_image.py

カメラの画像表示プログラムを作成・実行

VideoCaptureなどの関数を利用すると、カメラの画像を取得できます。
カメラの画像を表示するプログラムを作ってみましょう。

参考: Getting Started with Videos

show_camera.pyというプログラムを作ります。
cd ~/opencv_programs
vi show_camera.py

show_camera.py
import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while(True):
    ret, frame = cap.read()
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

できたら下記のコマンドで実行できます。
python3 show_camera.py 

顔認識プログラムを作成・実行

学習済みのデータを利用して、顔が写っている領域を認識できます。

参考: Face Detection using Haar Cascades

Lenaさんに対して、この手法を適用してみます。

学習済みのデータををダウンロードします、
cd ~/opencv_programs
wget https://github.com/opencv/opencv/raw/master/data/haarcascades/haarcascade_eye.xml
wget https://github.com/opencv/opencv/raw/master/data/haarcascades/haarcascade_frontalface_default.xml

プログラムを作ります。
vi detect_face_image.py

detect_face_image.py
import numpy as np
import cv2

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')
img = cv2.imread('lena.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[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('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

プログラムができたら、実行します。
python3 detect_face_image.py

成功すると、このように顔と目の領域に四角が表示されます。


カメラの画像の顔を認識するプログラムを作成・実行

顔認識プログラムとカメラの画像表示プログラムを組み合わせて、カメラで写している画像に含まれる顔を認識するプログラムを作ってみます。

重複する記述が多いので、顔認識プログラムを「detect_facecamera.py」というファイルとしてコピーして、そのファイルを編集します。
cd ~/opencv_programs
cp detect_face_image.py detect_face_camera.py
vi detect_face_camera.py

detect_face_camera.py
import numpy as np
import cv2

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')
cap = cv2.VideoCapture(0)

while(True):
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)    
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    for (x,y,w,h) in faces:
        cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = frame[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('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

プログラムができたら、下記のコマンドで実行できます。
python3 detect_face_camera.py

成功すると、カメラに写った顔が枠で囲われます。

まとめ

Raspberry PiにOpenCVをインストールして、カメラの画像に対して顔認識処理を適用できました。

何かの参考になれば嬉しいです。

0 件のコメント :