KIHARA BLOG:社内ラズコンでめざせ優勝!

Raspberry Pi(ラズパイ)で機械学習とか音声認識とか姿勢推定とかしながら組み込みシステムを構築して、社内ラズコンで優勝をめざすブログです。勉強中:Raspberry Pi、Linux、Python、Coral EdgeTPU、PoseNet、Julius

ラズパイ7日目①:カメラモジュールから動画を撮影してジェスチャーを判定する

今回は、Raspberry Pi + Edge TPU + PoseNet を使ったジェスチャー判定について勉強しました。

作ったもの


20200119PoseNet

右手を水平に左方向に動かす(スマホでスワイプするイメージ)と「RIGHT SCROLL GESTURE」を出力するプログラムです。前回同様、顔部分には円形オブジェクトを重ねています。

ポーズとジェスチャーの違い

ポーズとジェスチャーの違いですが、自分なりの解釈では
 ポーズ判定 ⇒ 関節の角度や座標から特定の姿勢を判定すること。1フレームのみ解析する。
 ジェスチャー判定 ⇒ 関節の角度や座標の遷移から特定の動きを判定すること。複数フレームを解析する。
きちんとした勉強をしたわけじゃないのであくまでイメージですが。

なお前回のポーズ判定の話はコチラ

kihara0223.hatenablog.com

ジェスチャーの判定方法

ポーズ判定の回と同様、ネットにはあまりやり方が載っていないため、自力でがんばる日々…
ゲームプログラミングの本とかに解説されてないものかしら~

とにかく1フレームでやってたポーズ判定を、複数フレーム介してやればいいだけなハズ!
以下の条件を満たした場合に『右スクロール』とジェスチャー判定させました。

  • 右手が前フレームよりも左側にある(つまり右手を左方向に移動させている)
  • 上記の動作を5フレーム連続で行っている

なぜ5フレームかというと、1秒未満でジェスチャー判定させたかったから。私の環境ではPoseNetのフレームレートが6〜7fpsだったので5フレームとしました。30fpsなら20フレームにするなど適当に調整してください。

KEYPOINTS = (
  'nose',
  'left eye',
  'right eye',
  'left ear',
  'right ear',
  'left shoulder',
  'right shoulder',
  'left elbow',
  'right elbow',
  'left wrist',
  'right wrist',
  'left hip',
  'right hip',
  'left knee',
  'right knee',
  'left ankle',
  'right ankle'
)

dict_KEYPOINTS = {
  'nose':0,
  'left eye':1,
  'right eye':2,
  'left ear':3,
  'right ear':4,
  'left shoulder':5,
  'right shoulder':6,
  'left elbow':7,
  'right elbow':8,
  'left wrist':9,
  'right wrist':10,
  'left hip':11,
  'right hip':12,
  'left knee':13,
  'right knee':14,
  'left ankle':15,
  'right ankle':16
}

def copy_to_prepose(pose, prepose):
    tmp=copy.copy(prepose)
    prepose=[]
    
    if len(tmp) == 0:
        for i in range(17):
            tmp.append((0,0))
            
    for point_i in KEYPOINTS:
        prepose.append(((pose.keypoints.get(point_i).yx[1]),(pose.keypoints.get(point_i).yx[0])))
        if prepose[dict_KEYPOINTS[point_i]][0] == '':
            prepose[dict_KEYPOINTS[point_i]][0] = tmp[dict_KEYPOINTS[point_i]][0]
        if prepose[dict_KEYPOINTS[point_i]][1] == '':
            prepose[dict_KEYPOINTS[point_i]][1] = tmp[dict_KEYPOINTS[point_i]][1]

    return prepose


def gesture_check(pose,prepose,frameCnt):
    #初回は前フレームが存在しないため何もせず終了
    if len(prepose) == 0:
        return frameCnt
    
    #右手が前フレームよりも左側に移動した場合、frameCntを+1する
    if pose.keypoints.get('right wrist').yx[1] > prepose[dict_KEYPOINTS['right wrist']][0]:
        frameCnt += 1
    else:
        frameCnt = 0
    
    #5フレーム連続で同じ動作を検知した場合、右スクロールジェスチャーと判定する
    if frameCnt >= 5:
        print('RIGHT SCROLL GESTURE')
        frameCnt = 0
    
    return frameCnt


def main():
    ・・・略・・・
    prepose = []
    frameCnt = 0
    ・・・略・・・
    def render_overlay(engine, output, src_size, inference_box):
        nonlocal n, sum_process_time, sum_inference_time, fps_counter, prepose, frameCnt
     ・・・略・・・
        for pose in outputs:
      ・・・略・・・
           frameCnt = gesture_check(pose,prepose,frameCnt)
           prepose=copy_to_prepose(pose,prepose)
      ・・・略・・・

一番ややこしかったのが、前フレームのKeyPoint情報をどうやって取得するかということ。次フレームのKeyPoint情報取得が始まる前に、現在のKeyPoint情報を退避させておけばいいのだけど、Pythonグローバル変数とローカル変数の仕組みやら、ListとTupleの違いやらに躓きました~。Pythonなかなか癖あるなぁ。

ところではてなブログのコードハイライトがいまいちなんだけどどうにかならんものか。