はじめに
Raspberry PIにmotionというソフトウェアをインストールして監視カメラとして使用し、映った人が誰かを識別するという取り組みをしているのですが、保存された静止画を使って識別するだけなので、少々物足りない感じがしているこの頃。
せっかくなので、動画をリアルタイムで処理して何かできないかと思って情報収集していたら、関係ありそうな記事があったので参考にしながらリアルタイムに顔検出するスクリプトを作ってみました。
OpenCVでWebカメラ/ビデオカメラの動画をリアルタイムに変換してみる
OpenCVのインストール
手前味噌ですが、インストール手順は以下の記事をご参照下さい。
Macで深層学習の環境をさくっと作る手順 with TensorFlow and OpenCV
また、顔検出に使う識別器を以下のコマンドで作業フォルダにコピーしておきます。
$ cp /usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt2.xml ./
スクリプトの作成
参考サイトにて公開されていたスクリプトを修正し、以下のとおりとします。
import cv2
if __name__ == '__main__':
# 定数定義
ESC_KEY = 27 # Escキー
INTERVAL= 33 # 待ち時間
FRAME_RATE = 30 # fps
ORG_WINDOW_NAME = "org"
GAUSSIAN_WINDOW_NAME = "gaussian"
DEVICE_ID = 0
# 分類器の指定
cascade_file = "haarcascade_frontalface_alt2.xml"
cascade = cv2.CascadeClassifier(cascade_file)
# カメラ映像取得
cap = cv2.VideoCapture(DEVICE_ID)
# 初期フレームの読込
end_flag, c_frame = cap.read()
height, width, channels = c_frame.shape
# ウィンドウの準備
cv2.namedWindow(ORG_WINDOW_NAME)
cv2.namedWindow(GAUSSIAN_WINDOW_NAME)
# 変換処理ループ
while end_flag == True:
# 画像の取得と顔の検出
img = c_frame
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face_list = cascade.detectMultiScale(img_gray, minSize=(100, 100))
# 検出した顔に印を付ける
for (x, y, w, h) in face_list:
color = (0, 0, 225)
pen_w = 3
cv2.rectangle(img_gray, (x, y), (x+w, y+h), color, thickness = pen_w)
# フレーム表示
cv2.imshow(ORG_WINDOW_NAME, c_frame)
cv2.imshow(GAUSSIAN_WINDOW_NAME, img_gray)
# Escキーで終了
key = cv2.waitKey(INTERVAL)
if key == ESC_KEY:
break
# 次のフレーム読み込み
end_flag, c_frame = cap.read()
# 終了処理
cv2.destroyAllWindows()
cap.release()
動作確認
以下のコマンドを実行するとウインドウが二つ表示され、元画像とグレイスケールに変換されて顔に四角が描画されたものが表示されるはず。
$ python sebcam.py
できた(^-^)/
おまけ(オプティカルフロー)
リアルタイムに検出した物体を追尾するというサンプルもあったので、ついでに試してみました。
OpenCVでオプティカルフローをリアルタイムに描画する(Shi-Tomasi法、Lucas-Kanade法)
参考先の動画読込元が動画ファイルだったので、これをWebカメラに変更してみました。
そして、検出した特徴点がなくなると再検出するよう変更してあります。
import numpy as np
import cv2
DEVICE_ID = 0
cap = cv2.VideoCapture(DEVICE_ID)
# Shi-Tomasiのコーナー検出パラメータ
feature_params = dict( maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7,
blockSize = 7 )
# Lucas-Kanade法のパラメータ
lk_params = dict( winSize = (15,15),
maxLevel = 2,
criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# ランダムに色を100個生成(値0~255の範囲で100行3列のランダムなndarrayを生成)
color = np.random.randint(0, 255, (100, 3))
# 最初のフレームの処理
end_flag, frame = cap.read()
gray_prev = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
feature_prev = cv2.goodFeaturesToTrack(gray_prev, mask = None, **feature_params)
mask = np.zeros_like(frame)
while(end_flag):
# グレースケールに変換
gray_next = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# オプティカルフロー検出
feature_next, status, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray_next, feature_prev, None, **lk_params)
if len(feature_next[status == 1]) == 0:
feature_prev = cv2.goodFeaturesToTrack(gray_prev, mask = None, **feature_params)
mask = np.zeros_like(frame)
feature_next, status, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray_next, feature_prev, None, **lk_params)
# オプティカルフローを検出した特徴点を選別(0:検出せず、1:検出した)
good_prev = feature_prev[status == 1]
good_next = feature_next[status == 1]
# オプティカルフローを描画
for i, (next_point, prev_point) in enumerate(zip(good_next, good_prev)):
prev_x, prev_y = prev_point.ravel()
next_x, next_y = next_point.ravel()
mask = cv2.line(mask, (next_x, next_y), (prev_x, prev_y), color[i].tolist(), 2)
frame = cv2.circle(frame, (next_x, next_y), 25, color[i].tolist(), -1)
img = cv2.add(frame, mask)
# ウィンドウに表示
cv2.imshow('window', img)
# ESCキー押下で終了
if cv2.waitKey(30) & 0xff == 27:
break
# 次のフレーム、ポイントの準備
gray_prev = gray_next.copy()
feature_prev = good_next.reshape(-1, 1, 2)
end_flag, frame = cap.read()
# 終了処理
cv2.destroyAllWindows()
cap.release()
できた!