BETA

OpenCVで人検出と行動を追跡をしてみた

投稿日:2019-10-18
最終更新:2019-11-01

OpenCVで動画ファイルから人検出と人の行動するのを追跡してみた
使ったツールバージョンは以下

  • MacOS Catalina / python3.5.6 / Opencv3.4.2 / VS code1.38.1

コメント

  • 人追跡はサンプルコードをほぼ流用して動作できた
  • 人追跡はオプティカルフローを使用している
  • オプティカルフローとは、二枚以上の時間的に連続する画像を用いて、その画像内で共通して写っている部分などから動作やあるパターンが移動する方向を推定しベクトルにしたもの。ロボットの物体追跡やドローンの位置制御推定、物体の速度を測定などに使えるみたい
  • ラズパイでも動作可能
  • 今回はサンプルコードでも使用しているOpenCVで提供している動画ファイルを活用
  • 恐らく人検出と人追跡の特徴量検出が異なるので、片方だけ検出できないケースある(原因は不明)

概要

  • 人は全身用のカスケードファイル「haarcascade_fullbody.xml」を使用
  • 人を判定する最低サイズは minSize=(40, 40)
  • オプティカルフローで隣接フレーム間の物体の動きを検出
  • 追跡した箇所を線で描画
  • 人検出した箇所を矩型で描画
  • 人検出した数を左下画面に表示

イメージ画像1

イメージ画像2

イメージ画像3

ファイル構成

  • human_detect.py : 人検出/人追跡ソースファイル(下のソースコード)
  • 768x576.avi:動画ファイル

ソースコード

import numpy as np  
import cv2  

# 体全体のカスケードファイル  
fullbody_detector = cv2.CascadeClassifier("/Users/local/source/opencv/face_recognition/data_xml/haarcascade_fullbody.xml")  
# サンプル画像  
cap = cv2.VideoCapture('768x576.avi')  

# 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)  
# 元の配列と同じ形にして0を代入  
mask = np.zeros_like(frame)  

while(end_flag):  
    # グレースケールに変換  
    gray_next = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  

    # 全身の人を検出   
    # minSize:物体が取り得る最小サイズ。これよりも小さい物体は無視される  
    # minNeighbors:物体候補となる矩形は,最低でもこの数だけの近傍矩形を含む  
    body = fullbody_detector.detectMultiScale(gray_next,scaleFactor=1.1, minNeighbors=3, minSize=(40, 40))  

    # オプティカルフロー検出  
    # オプティカルフローとは物体やカメラの移動によって生じる隣接フレーム間の物体の動きの見え方のパターン  
    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), 5, color[i].tolist(), -1)  
    img = cv2.add(frame, mask)  

    # 人検出した数表示のため変数初期化  
    human_cnt = 0  
    # 人検出した部分を長方形で囲う  
    for (x, y, w, h) in body:  
        cv2.rectangle(img, (x, y),(x+w, y+h),(0,255,0),2)  
        # 人検出した数を加算  
        human_cnt += 1  

    # 人検出した数を表示  
    cv2.putText(img, "Human Cnt:{}".format(int(human_cnt)),(10,550), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)  
    # ウィンドウに表示  
    cv2.imshow('human_view', img)  

   # ESCキー  
    k = cv2.waitKey(1)  
    if k == 27:  
        break  

    # 次のフレーム、ポイントの準備  
    gray_prev = gray_next.copy()  
    feature_prev = good_next.reshape(-1, 1, 2)  
    end_flag, frame = cap.read()  

# 終了処理  
cv2.destroyAllWindows()  
cap.release()  

まだ中身が理解できていない箇所あり。もっと勉強しないといけない......

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@主にプログラム技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!