手を検出してみました

2021.05.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

手を検出してみました。

使用させて頂いたのは、下記です。
Real-time Hand-Detection using Neural Networks (SSD) on Tensorflow.

こちらで公開されているモデルは、SSDによる物体検出モデルで、Tensorflowで作成されています。 今回は、こちらをOpenVINOで利用可能な形式に変換して利用してみました。

最初に動作している様子です。

2 detect_single_threaded.py

githubのリポジトリをcloneして、detect_single_threaded.pyを実行する事で、直ちに動作を確認することができます。

  • git clone
% git clone https://github.com/victordibia/handtracking.git
% cd handtracking
% python3 ./detect_single_threaded.py

なお、Tensorflowのバージョンは、1.4.0-rc0用となっています。

Tensorflow 2.3.2では、いくつかのオブジェクトが見つからないというエラーとなってしまったので、変更して動作を確認しました。

  • Version
% python3
>>> import tensorflow
>>> tensorflow.__version__
'2.3.2'
  • error
AttributeError: module 'tensorflow' has no attribute 'gfile'

tf.gfile.GFile -> tf.io.gfile.GFile

AttributeError: module 'tensorflow' has no attribute 'GraphDef'

tf.GraphDef() -> tf.compat.v1.GraphDef()

AttributeError: module 'tensorflow' has no attribute 'Session'

tf.Session() -> tf.compat.v1.Session()

動作を確認している様子です。

3 モデル変換

OpenVINOルーツキットを使用して、IR 形式へ変換する手順は、以下の通りです。

(1) ツールキットへのパス

OpenVINOツールキットへのパスを環境変数INTEL_OPENVINO_DIRにセットします。

ツールキットのバージョンによって、パスが変わりますので、ご注意ください。

% export INTEL_OPENVINO_DIR=/opt/intel/openvino_2021
% echo $INTEL_OPENVINO_DIR
/opt/intel/openvino_2021

(2) Tensorflowのモデル配置場所

モデルは、hand_inference_graphの中にあります。

% ls -la hand_inference_graph
total 76392
drwxr-xr-x  10 sin  staff       320  5 29 10:26 .
drwxr-xr-x  18 sin  staff       576  5 29 10:37 ..
-rw-r--r--   1 sin  staff        77  5 29 10:26 checkpoint
-rw-r--r--   1 sin  staff  19135409  5 29 10:26 frozen_inference_graph.pb
-rw-r--r--   1 sin  staff        32  5 29 10:26 hand_label_map.pbtxt
-rw-r--r--   1 sin  staff  18594336  5 29 10:26 model.ckpt.data-00000-of-00001
-rw-r--r--   1 sin  staff     14088  5 29 10:26 model.ckpt.index
-rw-r--r--   1 sin  staff   1345055  5 29 10:26 model.ckpt.meta
-rw-r--r--   1 sin  staff      4276  5 29 10:26 pipeline.config
drwxr-xr-x   3 sin  staff        96  5 29 10:26 saved_model

こちらへのパスを環境変数MODEL_DIRにセットしています。

% export MODEL_DIR=hand_inference_graph
% echo $MODEL_DIR
hand_inference_graph

(3) 変換

ツールキットによるTensorflowモデルの変換は以下のとおりです。

% python $INTEL_OPENVINO_DIR/deployment_tools/model_optimizer/mo.py \
--input_model $MODEL_DIR/frozen_inference_graph.pb \
--tensorflow_object_detection_api_pipeline_config $MODEL_DIR/pipeline.config \
--reverse_input_channels \
--transformations_config $INTEL_OPENVINO_DIR/deployment_tools/model_optimizer/extensions/front/tf/ssd_support_api_v1.15.json \
--data_type FP32 --output_dir FP32

.bin, .xmlが出力されていれば成功です。

% ls -la FP32
-rw-r--r--   1 sin  staff  18369096  5 29 13:12 frozen_inference_graph.bin
-rw-r--r--   1 sin  staff     69537  5 29 13:12 frozen_inference_graph.mapping
-rw-r--r--   1 sin  staff    270972  5 29 13:12 frozen_inference_graph.xml

4 推論

OpenVINOで推論しているコードです。

import numpy as np
import cv2
import time
from openvino.inference_engine import IECore

class Model:
    def __init__(self, ie, device, model):
        net = ie.read_network(model=model+'.xml', weights=model+'.bin')
        self.__input_blob_name  = list(net.inputs.keys())[0]
        self.__output_blob_name = list(net.outputs.keys())[0]
        self.__exec_net = ie.load_network(network=net, device_name=device, num_requests=1)
        self.__input_size = net.inputs[self.__input_blob_name].shape
    def infer(self, data):
        prob = self.__exec_net.infer(inputs={self.__input_blob_name: data})
        return prob[self.__output_blob_name]

    @property
    def input_size(self):
        return self.__input_size

class HandDetector(Model):
    def __init__(self, ie, device, model_path):
        super().__init__(ie, device, model_path)
        [_b, _c, h, w]  = super().input_size
        self.__height = h
        self.__width = w

    # 入力インターフェースへの画像変換
    def __prepare_frame(self, frame):
        img = cv2.resize(frame, (self.__width, self.__height)) # 800*600 -> 300*300
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR -> RGB
        img = img.transpose((2, 0, 1)) # 300,300,3 -> 3,300,300
        return img[np.newaxis, :] # 3,300,300 -> 1,3,300,300

    def infer(self, frame):
        height, width = frame.shape[:2]
        frame  = self.__prepare_frame(frame)

        detections = []
        result = super().infer(frame)


        for obj in result[0][0]:
            _, clsid, conf, x1, y1, x2, y2 = obj
            x1 = int(x1 * width/self.__width * self.__width)
            y1 = int(y1 * height/self.__height * self.__height)
            x2 = int(x2 * width/self.__width * self.__width)
            y2 = int(y2 * height/self.__height * self.__height)
            detections.append([int(clsid), conf, x1, y1, x2, y2])
        return detections

def main():

    # OpenVINO
    DEVICE = "CPU"
    MODEL = "./model/FP32/frozen_inference_graph"
    ie = IECore()
    hand_detector = HandDetector(ie, DEVICE, MODEL)

    # OpenCV
    WIDTH = 640
    HEIGHT = 480
    DEVICE_ID = 1
    FPS = 24
    cap = cv2.VideoCapture(DEVICE_ID)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
    cap.set(cv2.CAP_PROP_FPS, FPS)

    # フォーマット・解像度・FPSの取得
    width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print("fps:{} width:{} height:{}".format(fps, width, height))

    while(True):
        _, frame = cap.read()
        if(frame is None):
            continue

        start = time.time() # 時間計測
        detections = hand_detector.infer(frame)
        processing_time = time.time() - start
        print("processing_time {:.2f}sec".format(processing_time))

        for detection in detections:
            _clsid, conf, x1, y1, x2, y2 = detection
            if(conf < 0.8):
                continue
            color = (0, 255, 255)
            cls_name = "hand"
            print("{} {:.1f} ({},{})-({},{})".format(cls_name,  conf, x1, y1, x2, y2))
            cv2.rectangle(frame, (x1, y1), (x2, y2), color, 3)
            text = "{} {:.2f}".format(cls_name, conf)
            cv2.putText(frame, text, (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1, cv2.LINE_AA)

        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()

main()

MacBook Pro 2.3GHz Core i7での推論は、0.01secと高速です。

processing_time 0.01sec
processing_time 0.01sec
processing_time 0.01sec
processing_time 0.01sec
processing_time 0.01sec
processing_time 0.01sec

5 最後に

今回は、Real-time Hand-Detection using Neural Networks (SSD) on Tensorflow.をOpenVINOで試してみました。

現在、手の検出を起点に、手に取った商品の特定ができないかと模索中です。