落胆がらくた街

Dr.Rootはサポート終了製品です。

tf-openposeで実際に遊んで見る

がいよう

前回の記事↓で動かすところまで書いたtf-openposeで実際に遊んでみる。
tf-openposeをWindowsで動かす - 落胆がらくた街
動作確認はWindows 8.1(64bit)、GPUはGTX750Tiで行ったが、このグラボメモリが2GBしか無くはっきり言ってギリッギリだ。
今からハードウェアを調達するなら最低でもメモリが4GB乗ってるGTX1050Tiあたりがいいだろう(欲しい)。

以下、具体的に試してみたtf-openposeの改造について書く。出来上がったソースコードも記事末尾に纏めて載せてあるぞ。

デフォルトでの動作

tf-openposeのディレクトリにrun_webcam.pyというファイルがあるので、改造する事を見越してこいつを複製しrun_webcam2.pyとする。
次に起動用のバッチファイルを作成しrun.batとする。内容は下記の通り。

@echo off
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin;%PATH%
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\CUPTI\libx64;%PATH%
py -u run_webcam2.py --model=mobilenet_thin --resize=256x192 --resize-out-ratio=4.0
pause

頭の2行は環境変数の設定で、CuDnnのインストールされたCUDAへのパスを通している(前回記事参照)。
3行目が実際にopenposeを叩いている部分で、コマンドライン引数の意味は下記の通り。

--model=mobilenet_thin…動作モデルをmobilenet_thinに設定する。デフォルトでインストールされているモデルで、デフォルトのcmu(追加でモデルのDLが必要)と比べ高速で動作する。
--resize=256x192...入力された画像のリサイズを行う。各値は16の倍数にする必要がある。デフォルトでは432x384。
--resize-out-ratio=4.0...デフォルトで4.0だが一応明記している。ヒートマップ生成の細かさを表す。大きいほど遅いが高精度。ただし大きすぎてもオーバーフィットする為要調整。
この他に、接続されているカメラを指定する--camera=0やより細かいデバッグ情報を出す--show-process=Falseがある。

追記:pythonコマンドに渡している「-u」は標準出力を別アプリにパイプする際バッファしないようする命令。

run_webcamを改造する

デフォルトでこれを動作させても、元映像に検出された人の骨格が上書きされて表示されるだけなので、各座標の出力をコンソールに吐くよう改造する。
ついでに他にも幾つか改造する。

1. 標準エラー出力に出力されるデバッグ情報を削除する(見た目がさっぱりするので)
2. 標準出力に検出された座標の数値を出力する
3. ウィンドウに表示される画像を黒塗り+骨格にする(自分の顔を見たくない為)
4. GPUのメモリを必要な分だけ確保するようにする(デフォルトだとGPUの空きメモリ全部食う)
5. 動作速度を制限し、FPSをざっくり均一にする

すると最終的にこのように動作する。
f:id:suzumodoki:20190211210207j:plain
コンソールの標準出力に値がどかーっと吐き出されるので、他のアプリケーションでも標準出力をリダイレクトすればデータを引っこ抜けるぞ。
例えばC#アプリでプロセス介して実行すればUnityに取り込んだりもできる。
ちなみにパーツの部位番号に関してはtf_pose/common.pyの13行目を観るとわかるぞ。

run_webcam2.pyの全貌

import argparse
import logging
import time
import pprint   #配列出力用
import tensorflow as tf #config設定用
from time import sleep 
import cv2
import numpy as np

from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh

logger = logging.getLogger('TfPoseEstimator-WebCam')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)

fps_time = 0


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='tf-pose-estimation realtime webcam')
    parser.add_argument('--camera', type=int, default=0)

    parser.add_argument('--resize', type=str, default='0x0',
                        help='if provided, resize images before they are processed. default=0x0, Recommends : 432x368 or 656x368 or 1312x736 ')
    parser.add_argument('--resize-out-ratio', type=float, default=4.0,
                        help='if provided, resize heatmaps before they are post-processed. default=1.0')

    parser.add_argument('--model', type=str, default='mobilenet_thin', help='cmu / mobilenet_thin')
    parser.add_argument('--show-process', type=bool, default=False,
                        help='for debug purpose, if enabled, speed for inference is dropped.')
    args = parser.parse_args()

    logger.debug('initialization %s : %s' % (args.model, get_graph_path(args.model)))
    w, h = model_wh(args.resize)
    
    # gpuのメモリ確保を必要な分だけ取ってくるように変更
    config = tf.ConfigProto(gpu_options=tf.GPUOptions(allow_growth=True))
    
    if w > 0 and h > 0:
        e = TfPoseEstimator(get_graph_path(args.model), target_size=(w, h),tf_config=config)    # configを渡す記述を追加
    else:
        e = TfPoseEstimator(get_graph_path(args.model), target_size=(432, 368),tf_config=config)    # configを渡す記述を追加
    logger.debug('cam read+')
    cam = cv2.VideoCapture(args.camera)
    ret_val, image = cam.read()
    logger.info('cam image=%dx%d' % (image.shape[1], image.shape[0]))
        
    while True:
        ret_val, image = cam.read()
        showit=np.zeros((368,432,3),np.uint8)   # 出力用に空の画像を作成

        #logger.debug('image process+')
        humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=args.resize_out_ratio)

        #logger.debug('postprocess+')
        image = TfPoseEstimator.draw_humans(showit, humans, imgcopy=False)  # 空の画像に骨格を描画

        # 前回からの経過時間を出す
        deltaTime=time.time() - fps_time        
        if deltaTime < 0.042:   # 24fps=42ms per frame
            sleep(0.042 - deltaTime)    # 十分処理が早い場合何もしない
        
        #logger.debug('show+')
        cv2.putText(showit,
                    "FPS: %f" % (1.0 / (time.time() - fps_time)),
                    (10, 10),  cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                    (0, 255, 0), 2)
        cv2.imshow('tf-pose-estimation result', showit) # 画像を画面に出力
        fps_time = time.time()
        
        #コンソール出力周りを追加
        # 形式→[(PartsNumber,X,Y,Score),...]
        _list=[]        
        for human in humans:
            for _part in human.body_parts:
                _v = human.body_parts[_part]
                _list.append((_v.part_idx,_v.x,_v.y,_v.score))
            pprint.pprint(_list)
        
        if cv2.waitKey(1) == 27:
            break
        #logger.debug('finished+')
        

    cv2.destroyAllWindows()