【ラズパイ】OpenCVで顔検出してみた

 

ラズパイとOpenCVを使って、顔を検出したら写真を撮るようなコードをPythonで書いてみました。

 

usbカメラでなく、ラズパイのカメラを使っています。

ラズパイカメラだと写真撮影にはpicameraというライブラリを使う方法もありますがコードが少し複雑になります。

この記事では、picameraを使わずにシンプルにOpenCVで書いて行きます。

 

コード

 

なぜかラズパイカメラだと反転するので、最初に上下反転する処理を入れています。

キャスケードファイルは、人の顔を認識するためのファイルを指定します。

以下のサイトからファイルをダウンロード。

https://github.com/opencv/opencv/tree/master/data/haarcascades

pythonのコードと同じディレクトリに置いて実行。

顔を検出したら、写真を保存して3秒待ちます。

import cv2
from datetime import datetime
from time import sleep

MIN_SIZE = (150, 150)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")

camera = cv2.VideoCapture(0)

try:
    while True:
        _, img = camera.read()
        # raspiのカメラだと反転しているので修正
        img = cv2.flip(img, -1)
        igray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = cascade.detectMultiScale(igray, minSize=MIN_SIZE)
        if len(faces) == 0:
            continue

        for (x, y, w, h) in faces:
            color = (255, 0, 0)
            cv2.rectangle(img, (x, y), (x+w, y+h),
                          color, thickness=8)

        t = datetime.now().strftime("%Y-%m-%d_%H_%M_%S")
        fname = "face" + t + ".jpg"
        cv2.imwrite(fname, img)
        print("detect face")
        sleep(3)

except KeyboardInterrupt:
    print("key interrupt!!!")

 

以下のエラーが発生する場合はカスケードファイルがダウンロードしてあるか、ファイル名があっているか確認しましょう。

OpenCV Error: Assertion failed (!empty()) in detectMultiScale,

error: (-215) !empty() in function detectMultiScale

 

実行結果

 

撮影した画像はこんな感じ。

ipadに映った顔を認識してますね。

 

フレームを表示する

 

ファイル出力だと数が多くなるので、ディスプレイにフレームを表示するように改造してみたいと思います。

 

フレームを表示するときはSSHやVNCなどのリモート実行では表示されないので注意してください。

実行すると以下のようなエラーが出ます。

: cannot connect to X server

 

  • 顔を検出しても、しなくてもフレームをディスプレイに表示する
  • 「q」が押されたら終了

 

import cv2
from datetime import datetime
from time import sleep

MIN_SIZE = (150, 150)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")

camera = cv2.VideoCapture(0)

while True:

    # フレームがアクティブになった状態で「q」が押されたら終了。
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

    _, img = camera.read()
    # raspiのカメラだと反転しているので修正
    img = cv2.flip(img, -1)

    igray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = cascade.detectMultiScale(igray, minSize=MIN_SIZE)

    if len(faces) == 0:
        # 顔が検出されなかった時でもフレームを表示
        cv2.imshow('frame', img)
        continue

    for (x, y, w, h) in faces:
        color = (255, 0, 0)
        cv2.rectangle(img, (x, y), (x+w, y+h),
                      color, thickness=8)

    t = datetime.now().strftime("%Y-%m-%d_%H_%M_%S")
    fname = "face" + t + ".jpg"

    print("detect face")

    # 顔が検出されたら顔の周りに枠を表示してフレームを表示
    cv2.imshow('frame', img)

    # ファイル出力したい場合は以下を使用、ファイル数が多くなりすぎないように3秒待つ
    # cv2.imwrite(fname, img)
    # sleep(3)

# ビデオキャプチャを終了して、フレームを表示していたウィンドウを閉じる。
camera.release()
cv2.destroyAllWindows()

 

 

こっちの方が直感的で分かりやすいと思います。

 

おわり。

 

参考