From dcf3f0ce11809f5c5f96af129740135debaaf714 Mon Sep 17 00:00:00 2001 From: vikas Date: Thu, 27 Sep 2018 20:10:50 +0530 Subject: [PATCH] added code for Hand Pose Detection --- .../Untitled-checkpoint.ipynb | 6 + HandPose/CMakeLists.txt | 16 + HandPose/getModels.sh | 9 + HandPose/hand.jpg | Bin 0 -> 47432 bytes HandPose/hand/pose_deploy.prototxt | 1756 +++++++++++++++++ HandPose/handPoseImage.cpp | 103 + HandPose/handPoseImage.py | 74 + HandPose/handPoseVideo.cpp | 112 ++ HandPose/handPoseVideo.py | 98 + 9 files changed, 2174 insertions(+) create mode 100644 HandPose/.ipynb_checkpoints/Untitled-checkpoint.ipynb create mode 100644 HandPose/CMakeLists.txt create mode 100755 HandPose/getModels.sh create mode 100644 HandPose/hand.jpg create mode 100644 HandPose/hand/pose_deploy.prototxt create mode 100644 HandPose/handPoseImage.cpp create mode 100644 HandPose/handPoseImage.py create mode 100644 HandPose/handPoseVideo.cpp create mode 100644 HandPose/handPoseVideo.py diff --git a/HandPose/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/HandPose/.ipynb_checkpoints/Untitled-checkpoint.ipynb new file mode 100644 index 000000000..2fd64429b --- /dev/null +++ b/HandPose/.ipynb_checkpoints/Untitled-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/HandPose/CMakeLists.txt b/HandPose/CMakeLists.txt new file mode 100644 index 000000000..ddf474e34 --- /dev/null +++ b/HandPose/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.8.12) + +PROJECT(handPose) + +find_package( OpenCV REQUIRED ) + +include_directories( ${OpenCV_INCLUDE_DIRS}) + +MACRO(add_example name) + ADD_EXECUTABLE(${name} ${name}.cpp) + TARGET_LINK_LIBRARIES(${name} ${OpenCV_LIBS}) +ENDMACRO() + + +add_example(handPoseImage) +add_example(handPoseVideo) diff --git a/HandPose/getModels.sh b/HandPose/getModels.sh new file mode 100755 index 000000000..c444e94bd --- /dev/null +++ b/HandPose/getModels.sh @@ -0,0 +1,9 @@ +# ------------------------- BODY, FACE AND HAND MODELS ------------------------- +# Downloading body pose (COCO and MPI), face and hand models +OPENPOSE_URL="http://posefs1.perception.cs.cmu.edu/OpenPose/models/" +HAND_FOLDER="hand/" + +# "------------------------- HAND MODELS -------------------------" +# Hand +HAND_MODEL=$HAND_FOLDER"pose_iter_102000.caffemodel" +wget -c ${OPENPOSE_URL}${HAND_MODEL} -P ${HAND_FOLDER} diff --git a/HandPose/hand.jpg b/HandPose/hand.jpg new file mode 100644 index 0000000000000000000000000000000000000000..26ec755a3fbaa1425d55585de3ba07ff085fad07 GIT binary patch literal 47432 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 KeG2?H^M3%HD`q$V literal 0 HcmV?d00001 diff --git a/HandPose/hand/pose_deploy.prototxt b/HandPose/hand/pose_deploy.prototxt new file mode 100644 index 000000000..3554c3a02 --- /dev/null +++ b/HandPose/hand/pose_deploy.prototxt @@ -0,0 +1,1756 @@ +input: "image" +input_dim: 1 # Original: 2 +input_dim: 3 # It crashes if not left to 3 +input_dim: 1 # Original: 368 +input_dim: 1 # Original: 368 +layer { + name: "conv1_1" + type: "Convolution" + bottom: "image" + top: "conv1_1" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 64 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu1_1" + type: "ReLU" + bottom: "conv1_1" + top: "conv1_1" +} +layer { + name: "conv1_2" + type: "Convolution" + bottom: "conv1_1" + top: "conv1_2" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 64 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu1_2" + type: "ReLU" + bottom: "conv1_2" + top: "conv1_2" +} +layer { + name: "pool1_stage1" + type: "Pooling" + bottom: "conv1_2" + top: "pool1_stage1" + pooling_param { + pool: MAX + kernel_size: 2 + stride: 2 + } +} +layer { + name: "conv2_1" + type: "Convolution" + bottom: "pool1_stage1" + top: "conv2_1" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu2_1" + type: "ReLU" + bottom: "conv2_1" + top: "conv2_1" +} +layer { + name: "conv2_2" + type: "Convolution" + bottom: "conv2_1" + top: "conv2_2" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu2_2" + type: "ReLU" + bottom: "conv2_2" + top: "conv2_2" +} +layer { + name: "pool2_stage1" + type: "Pooling" + bottom: "conv2_2" + top: "pool2_stage1" + pooling_param { + pool: MAX + kernel_size: 2 + stride: 2 + } +} +layer { + name: "conv3_1" + type: "Convolution" + bottom: "pool2_stage1" + top: "conv3_1" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu3_1" + type: "ReLU" + bottom: "conv3_1" + top: "conv3_1" +} +layer { + name: "conv3_2" + type: "Convolution" + bottom: "conv3_1" + top: "conv3_2" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu3_2" + type: "ReLU" + bottom: "conv3_2" + top: "conv3_2" +} +layer { + name: "conv3_3" + type: "Convolution" + bottom: "conv3_2" + top: "conv3_3" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu3_3" + type: "ReLU" + bottom: "conv3_3" + top: "conv3_3" +} +layer { + name: "conv3_4" + type: "Convolution" + bottom: "conv3_3" + top: "conv3_4" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu3_4" + type: "ReLU" + bottom: "conv3_4" + top: "conv3_4" +} +layer { + name: "pool3_stage1" + type: "Pooling" + bottom: "conv3_4" + top: "pool3_stage1" + pooling_param { + pool: MAX + kernel_size: 2 + stride: 2 + } +} +layer { + name: "conv4_1" + type: "Convolution" + bottom: "pool3_stage1" + top: "conv4_1" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu4_1" + type: "ReLU" + bottom: "conv4_1" + top: "conv4_1" +} +layer { + name: "conv4_2" + type: "Convolution" + bottom: "conv4_1" + top: "conv4_2" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu4_2" + type: "ReLU" + bottom: "conv4_2" + top: "conv4_2" +} +layer { + name: "conv4_3" + type: "Convolution" + bottom: "conv4_2" + top: "conv4_3" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu4_3" + type: "ReLU" + bottom: "conv4_3" + top: "conv4_3" +} +layer { + name: "conv4_4" + type: "Convolution" + bottom: "conv4_3" + top: "conv4_4" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu4_4" + type: "ReLU" + bottom: "conv4_4" + top: "conv4_4" +} +layer { + name: "conv5_1" + type: "Convolution" + bottom: "conv4_4" + top: "conv5_1" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu5_1" + type: "ReLU" + bottom: "conv5_1" + top: "conv5_1" +} +layer { + name: "conv5_2" + type: "Convolution" + bottom: "conv5_1" + top: "conv5_2" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + weight_filler { + type: "xavier" + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu5_2" + type: "ReLU" + bottom: "conv5_2" + top: "conv5_2" +} +layer { + name: "conv5_3_CPM" + type: "Convolution" + bottom: "conv5_2" + top: "conv5_3_CPM" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 1 + kernel_size: 3 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu5_4_stage1_3" + type: "ReLU" + bottom: "conv5_3_CPM" + top: "conv5_3_CPM" +} +layer { + name: "conv6_1_CPM" + type: "Convolution" + bottom: "conv5_3_CPM" + top: "conv6_1_CPM" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 512 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "relu6_4_stage1_1" + type: "ReLU" + bottom: "conv6_1_CPM" + top: "conv6_1_CPM" +} +layer { + name: "conv6_2_CPM" + type: "Convolution" + bottom: "conv6_1_CPM" + top: "conv6_2_CPM" + param { + lr_mult: 1.0 + decay_mult: 1 + } + param { + lr_mult: 2.0 + decay_mult: 0 + } + convolution_param { + num_output: 22 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "concat_stage2" + type: "Concat" + bottom: "conv6_2_CPM" + bottom: "conv5_3_CPM" + top: "concat_stage2" + concat_param { + axis: 1 + } +} +layer { + name: "Mconv1_stage2" + type: "Convolution" + bottom: "concat_stage2" + top: "Mconv1_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_2_stage2_1" + type: "ReLU" + bottom: "Mconv1_stage2" + top: "Mconv1_stage2" +} +layer { + name: "Mconv2_stage2" + type: "Convolution" + bottom: "Mconv1_stage2" + top: "Mconv2_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_3_stage2_2" + type: "ReLU" + bottom: "Mconv2_stage2" + top: "Mconv2_stage2" +} +layer { + name: "Mconv3_stage2" + type: "Convolution" + bottom: "Mconv2_stage2" + top: "Mconv3_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_4_stage2_3" + type: "ReLU" + bottom: "Mconv3_stage2" + top: "Mconv3_stage2" +} +layer { + name: "Mconv4_stage2" + type: "Convolution" + bottom: "Mconv3_stage2" + top: "Mconv4_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_5_stage2_4" + type: "ReLU" + bottom: "Mconv4_stage2" + top: "Mconv4_stage2" +} +layer { + name: "Mconv5_stage2" + type: "Convolution" + bottom: "Mconv4_stage2" + top: "Mconv5_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_6_stage2_5" + type: "ReLU" + bottom: "Mconv5_stage2" + top: "Mconv5_stage2" +} +layer { + name: "Mconv6_stage2" + type: "Convolution" + bottom: "Mconv5_stage2" + top: "Mconv6_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_7_stage2_6" + type: "ReLU" + bottom: "Mconv6_stage2" + top: "Mconv6_stage2" +} +layer { + name: "Mconv7_stage2" + type: "Convolution" + bottom: "Mconv6_stage2" + top: "Mconv7_stage2" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 22 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "concat_stage3" + type: "Concat" + bottom: "Mconv7_stage2" + bottom: "conv5_3_CPM" + top: "concat_stage3" + concat_param { + axis: 1 + } +} +layer { + name: "Mconv1_stage3" + type: "Convolution" + bottom: "concat_stage3" + top: "Mconv1_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_2_stage3_1" + type: "ReLU" + bottom: "Mconv1_stage3" + top: "Mconv1_stage3" +} +layer { + name: "Mconv2_stage3" + type: "Convolution" + bottom: "Mconv1_stage3" + top: "Mconv2_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_3_stage3_2" + type: "ReLU" + bottom: "Mconv2_stage3" + top: "Mconv2_stage3" +} +layer { + name: "Mconv3_stage3" + type: "Convolution" + bottom: "Mconv2_stage3" + top: "Mconv3_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_4_stage3_3" + type: "ReLU" + bottom: "Mconv3_stage3" + top: "Mconv3_stage3" +} +layer { + name: "Mconv4_stage3" + type: "Convolution" + bottom: "Mconv3_stage3" + top: "Mconv4_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_5_stage3_4" + type: "ReLU" + bottom: "Mconv4_stage3" + top: "Mconv4_stage3" +} +layer { + name: "Mconv5_stage3" + type: "Convolution" + bottom: "Mconv4_stage3" + top: "Mconv5_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_6_stage3_5" + type: "ReLU" + bottom: "Mconv5_stage3" + top: "Mconv5_stage3" +} +layer { + name: "Mconv6_stage3" + type: "Convolution" + bottom: "Mconv5_stage3" + top: "Mconv6_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_7_stage3_6" + type: "ReLU" + bottom: "Mconv6_stage3" + top: "Mconv6_stage3" +} +layer { + name: "Mconv7_stage3" + type: "Convolution" + bottom: "Mconv6_stage3" + top: "Mconv7_stage3" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 22 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "concat_stage4" + type: "Concat" + bottom: "Mconv7_stage3" + bottom: "conv5_3_CPM" + top: "concat_stage4" + concat_param { + axis: 1 + } +} +layer { + name: "Mconv1_stage4" + type: "Convolution" + bottom: "concat_stage4" + top: "Mconv1_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_2_stage4_1" + type: "ReLU" + bottom: "Mconv1_stage4" + top: "Mconv1_stage4" +} +layer { + name: "Mconv2_stage4" + type: "Convolution" + bottom: "Mconv1_stage4" + top: "Mconv2_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_3_stage4_2" + type: "ReLU" + bottom: "Mconv2_stage4" + top: "Mconv2_stage4" +} +layer { + name: "Mconv3_stage4" + type: "Convolution" + bottom: "Mconv2_stage4" + top: "Mconv3_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_4_stage4_3" + type: "ReLU" + bottom: "Mconv3_stage4" + top: "Mconv3_stage4" +} +layer { + name: "Mconv4_stage4" + type: "Convolution" + bottom: "Mconv3_stage4" + top: "Mconv4_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_5_stage4_4" + type: "ReLU" + bottom: "Mconv4_stage4" + top: "Mconv4_stage4" +} +layer { + name: "Mconv5_stage4" + type: "Convolution" + bottom: "Mconv4_stage4" + top: "Mconv5_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_6_stage4_5" + type: "ReLU" + bottom: "Mconv5_stage4" + top: "Mconv5_stage4" +} +layer { + name: "Mconv6_stage4" + type: "Convolution" + bottom: "Mconv5_stage4" + top: "Mconv6_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_7_stage4_6" + type: "ReLU" + bottom: "Mconv6_stage4" + top: "Mconv6_stage4" +} +layer { + name: "Mconv7_stage4" + type: "Convolution" + bottom: "Mconv6_stage4" + top: "Mconv7_stage4" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 22 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "concat_stage5" + type: "Concat" + bottom: "Mconv7_stage4" + bottom: "conv5_3_CPM" + top: "concat_stage5" + concat_param { + axis: 1 + } +} +layer { + name: "Mconv1_stage5" + type: "Convolution" + bottom: "concat_stage5" + top: "Mconv1_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_2_stage5_1" + type: "ReLU" + bottom: "Mconv1_stage5" + top: "Mconv1_stage5" +} +layer { + name: "Mconv2_stage5" + type: "Convolution" + bottom: "Mconv1_stage5" + top: "Mconv2_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_3_stage5_2" + type: "ReLU" + bottom: "Mconv2_stage5" + top: "Mconv2_stage5" +} +layer { + name: "Mconv3_stage5" + type: "Convolution" + bottom: "Mconv2_stage5" + top: "Mconv3_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_4_stage5_3" + type: "ReLU" + bottom: "Mconv3_stage5" + top: "Mconv3_stage5" +} +layer { + name: "Mconv4_stage5" + type: "Convolution" + bottom: "Mconv3_stage5" + top: "Mconv4_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_5_stage5_4" + type: "ReLU" + bottom: "Mconv4_stage5" + top: "Mconv4_stage5" +} +layer { + name: "Mconv5_stage5" + type: "Convolution" + bottom: "Mconv4_stage5" + top: "Mconv5_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_6_stage5_5" + type: "ReLU" + bottom: "Mconv5_stage5" + top: "Mconv5_stage5" +} +layer { + name: "Mconv6_stage5" + type: "Convolution" + bottom: "Mconv5_stage5" + top: "Mconv6_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_7_stage5_6" + type: "ReLU" + bottom: "Mconv6_stage5" + top: "Mconv6_stage5" +} +layer { + name: "Mconv7_stage5" + type: "Convolution" + bottom: "Mconv6_stage5" + top: "Mconv7_stage5" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 22 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "concat_stage6" + type: "Concat" + bottom: "Mconv7_stage5" + bottom: "conv5_3_CPM" + top: "concat_stage6" + concat_param { + axis: 1 + } +} +layer { + name: "Mconv1_stage6" + type: "Convolution" + bottom: "concat_stage6" + top: "Mconv1_stage6" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_2_stage6_1" + type: "ReLU" + bottom: "Mconv1_stage6" + top: "Mconv1_stage6" +} +layer { + name: "Mconv2_stage6" + type: "Convolution" + bottom: "Mconv1_stage6" + top: "Mconv2_stage6" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_3_stage6_2" + type: "ReLU" + bottom: "Mconv2_stage6" + top: "Mconv2_stage6" +} +layer { + name: "Mconv3_stage6" + type: "Convolution" + bottom: "Mconv2_stage6" + top: "Mconv3_stage6" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_4_stage6_3" + type: "ReLU" + bottom: "Mconv3_stage6" + top: "Mconv3_stage6" +} +layer { + name: "Mconv4_stage6" + type: "Convolution" + bottom: "Mconv3_stage6" + top: "Mconv4_stage6" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_5_stage6_4" + type: "ReLU" + bottom: "Mconv4_stage6" + top: "Mconv4_stage6" +} +layer { + name: "Mconv5_stage6" + type: "Convolution" + bottom: "Mconv4_stage6" + top: "Mconv5_stage6" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 3 + kernel_size: 7 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_6_stage6_5" + type: "ReLU" + bottom: "Mconv5_stage6" + top: "Mconv5_stage6" +} +layer { + name: "Mconv6_stage6" + type: "Convolution" + bottom: "Mconv5_stage6" + top: "Mconv6_stage6" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 128 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} +layer { + name: "Mrelu1_7_stage6_6" + type: "ReLU" + bottom: "Mconv6_stage6" + top: "Mconv6_stage6" +} +layer { + name: "Mconv7_stage6" + type: "Convolution" + bottom: "Mconv6_stage6" +# top: "Mconv7_stage6" + top: "net_output" + param { + lr_mult: 4.0 + decay_mult: 1 + } + param { + lr_mult: 8.0 + decay_mult: 0 + } + convolution_param { + num_output: 22 + pad: 0 + kernel_size: 1 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + } + dilation: 1 + } +} + diff --git a/HandPose/handPoseImage.cpp b/HandPose/handPoseImage.cpp new file mode 100644 index 000000000..7b05bc7e0 --- /dev/null +++ b/HandPose/handPoseImage.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::dnn; + + +const int POSE_PAIRS[20][2] = +{ + {0,1}, {1,2}, {2,3}, {3,4}, // thumb + {0,5}, {5,6}, {6,7}, {7,8}, // index + {0,9}, {9,10}, {10,11}, {11,12}, // middle + {0,13}, {13,14}, {14,15}, {15,16}, // ring + {0,17}, {17,18}, {18,19}, {19,20} // small +}; + +string protoFile = "hand/pose_deploy.prototxt"; +string weightsFile = "hand/pose_iter_102000.caffemodel"; + +int nPoints = 22; + +int main(int argc, char **argv) +{ + + cout << "USAGE : ./handPoseImage " << endl; + + string imageFile = "hand.jpg"; + // Take arguments from commmand line + if (argc == 2) + { + imageFile = argv[1]; + } + + int inWidth = 368; + int inHeight = 368; + float thresh = 0.01; + + Mat frame = imread(imageFile); + Mat frameCopy = frame.clone(); + int frameWidth = frame.cols; + int frameHeight = frame.rows; + + double t = (double) cv::getTickCount(); + Net net = readNetFromCaffe(protoFile, weightsFile); + + Mat inpBlob = blobFromImage(frame, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false); + + net.setInput(inpBlob); + + Mat output = net.forward(); + + int H = output.size[2]; + int W = output.size[3]; + + // find the position of the body parts + vector points(nPoints); + for (int n=0; n < nPoints; n++) + { + // Probability map of corresponding body's part. + Mat probMap(H, W, CV_32F, output.ptr(0,n)); + resize(probMap, probMap, Size(frameWidth, frameHeight)); + + Point maxLoc; + double prob; + minMaxLoc(probMap, 0, &prob, 0, &maxLoc); + if (prob > thresh) + { + circle(frameCopy, cv::Point((int)maxLoc.x, (int)maxLoc.y), 8, Scalar(0,255,255), -1); + cv::putText(frameCopy, cv::format("%d", n), cv::Point((int)maxLoc.x, (int)maxLoc.y), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 255), 2); + + } + points[n] = maxLoc; + } + + int nPairs = sizeof(POSE_PAIRS)/sizeof(POSE_PAIRS[0]); + + for (int n = 0; n < nPairs; n++) + { + // lookup 2 connected body/hand parts + Point2f partA = points[POSE_PAIRS[n][0]]; + Point2f partB = points[POSE_PAIRS[n][1]]; + + if (partA.x<=0 || partA.y<=0 || partB.x<=0 || partB.y<=0) + continue; + + line(frame, partA, partB, Scalar(0,255,255), 8); + circle(frame, partA, 8, Scalar(0,0,255), -1); + circle(frame, partB, 8, Scalar(0,0,255), -1); + } + + t = ((double)cv::getTickCount() - t)/cv::getTickFrequency(); + cout << "Time Taken = " << t << endl; + imshow("Output-Keypoints", frameCopy); + imshow("Output-Skeleton", frame); + imwrite("Output-Skeleton.jpg", frame); + + waitKey(); + + return 0; +} diff --git a/HandPose/handPoseImage.py b/HandPose/handPoseImage.py new file mode 100644 index 000000000..3de3598bf --- /dev/null +++ b/HandPose/handPoseImage.py @@ -0,0 +1,74 @@ +import cv2 +import time +import numpy as np + +protoFile = "hand/pose_deploy.prototxt" +weightsFile = "hand/pose_iter_102000.caffemodel" +nPoints = 22 +POSE_PAIRS = [ [0,1],[1,2],[2,3],[3,4],[0,5],[5,6],[6,7],[7,8],[0,9],[9,10],[10,11],[11,12],[0,13],[13,14],[14,15],[15,16],[0,17],[17,18],[18,19],[19,20] ] + + +frame = cv2.imread("hand.jpg") +frameCopy = np.copy(frame) +frameWidth = frame.shape[1] +frameHeight = frame.shape[0] +threshold = 0.1 + +net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile) + +t = time.time() +# input image dimensions for the network +inWidth = 368 +inHeight = 368 +inpBlob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), + (0, 0, 0), swapRB=False, crop=False) + +net.setInput(inpBlob) + +output = net.forward() +print("time taken by network : {:.3f}".format(time.time() - t)) + +H = output.shape[2] +W = output.shape[3] + +# Empty list to store the detected keypoints +points = [] + +for i in range(nPoints): + # confidence map of corresponding body's part. + probMap = output[0, i, :, :] + probMap = cv2.resize(probMap, (frameWidth, frameHeight)) + + # Find global maxima of the probMap. + minVal, prob, minLoc, point = cv2.minMaxLoc(probMap) + + if prob > threshold : + cv2.circle(frameCopy, (int(point[0]), int(point[1])), 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED) + cv2.putText(frameCopy, "{}".format(i), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, lineType=cv2.LINE_AA) + + # Add the point to the list if the probability is greater than the threshold + points.append((int(point[0]), int(point[1]))) + else : + points.append(None) + +# Draw Skeleton +for pair in POSE_PAIRS: + partA = pair[0] + partB = pair[1] + + if points[partA] and points[partB]: + cv2.line(frame, points[partA], points[partB], (0, 255, 255), 2) + cv2.circle(frame, points[partA], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED) + cv2.circle(frame, points[partB], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED) + + +cv2.imshow('Output-Keypoints', frameCopy) +cv2.imshow('Output-Skeleton', frame) + + +cv2.imwrite('Output-Keypoints.jpg', frameCopy) +cv2.imwrite('Output-Skeleton.jpg', frame) + +print("Total time taken : {:.3f}".format(time.time() - t)) + +cv2.waitKey(0) diff --git a/HandPose/handPoseVideo.cpp b/HandPose/handPoseVideo.cpp new file mode 100644 index 000000000..aee5a435e --- /dev/null +++ b/HandPose/handPoseVideo.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::dnn; + +const int POSE_PAIRS[20][2] = +{ + {0,1}, {1,2}, {2,3}, {3,4}, // thumb + {0,5}, {5,6}, {6,7}, {7,8}, // index + {0,9}, {9,10}, {10,11}, {11,12}, // middle + {0,13}, {13,14}, {14,15}, {15,16}, // ring + {0,17}, {17,18}, {18,19}, {19,20} // small +}; + +string protoFile = "hand/pose_deploy.prototxt"; +string weightsFile = "hand/pose_iter_102000.caffemodel"; + +int nPoints = 22; + +int main(int argc, char **argv) +{ + int inWidth = 368; + int inHeight = 368; + float thresh = 0.01; + + cv::VideoCapture cap(0); + + if (!cap.isOpened()) + { + cerr << "Unable to connect to camera" << endl; + return 1; + } + + Mat frame, frameCopy; + int frameWidth = cap.get(CAP_PROP_FRAME_WIDTH); + int frameHeight = cap.get(CAP_PROP_FRAME_HEIGHT); + + VideoWriter video("Output-Skeleton.avi",VideoWriter::fourcc('M','J','P','G'), 10, Size(frameWidth,frameHeight)); + + Net net = readNetFromCaffe(protoFile, weightsFile); + + double t=0; + while(1) + { + double t = (double) cv::getTickCount(); + + cap >> frame; + frameCopy = frame.clone(); + Mat inpBlob = blobFromImage(frame, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false); + + net.setInput(inpBlob); + + Mat output = net.forward(); + + int H = output.size[2]; + int W = output.size[3]; + + // find the position of the body parts + vector points(nPoints); + for (int n=0; n < nPoints; n++) + { + // Probability map of corresponding body's part. + Mat probMap(H, W, CV_32F, output.ptr(0,n)); + resize(probMap, probMap, Size(frameWidth, frameHeight)); + + Point maxLoc; + double prob; + minMaxLoc(probMap, 0, &prob, 0, &maxLoc); + if (prob > thresh) + { + circle(frameCopy, cv::Point((int)maxLoc.x, (int)maxLoc.y), 8, Scalar(0,255,255), -1); + cv::putText(frameCopy, cv::format("%d", n), cv::Point((int)maxLoc.x, (int)maxLoc.y), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 255), 2); + + } + points[n] = maxLoc; + } + + int nPairs = sizeof(POSE_PAIRS)/sizeof(POSE_PAIRS[0]); + + for (int n = 0; n < nPairs; n++) + { + // lookup 2 connected body/hand parts + Point2f partA = points[POSE_PAIRS[n][0]]; + Point2f partB = points[POSE_PAIRS[n][1]]; + + if (partA.x<=0 || partA.y<=0 || partB.x<=0 || partB.y<=0) + continue; + + line(frame, partA, partB, Scalar(0,255,255), 8); + circle(frame, partA, 8, Scalar(0,0,255), -1); + circle(frame, partB, 8, Scalar(0,0,255), -1); + } + + t = ((double)cv::getTickCount() - t)/cv::getTickFrequency(); + cv::putText(frame, cv::format("time taken = %.2f sec", t), cv::Point(50, 50), cv::FONT_HERSHEY_COMPLEX, .8, cv::Scalar(255, 50, 0), 2); + // imshow("Output-Keypoints", frameCopy); + imshow("Output-Skeleton", frame); + video.write(frame); + char key = waitKey(1); + if (key==27) + break; + } + // When everything done, release the video capture and write object + cap.release(); + video.release(); + + return 0; +} diff --git a/HandPose/handPoseVideo.py b/HandPose/handPoseVideo.py new file mode 100644 index 000000000..7e6526b38 --- /dev/null +++ b/HandPose/handPoseVideo.py @@ -0,0 +1,98 @@ +import cv2 +import time +import numpy as np + + +protoFile = "hand/pose_deploy.prototxt" +weightsFile = "hand/pose_iter_102000.caffemodel" +nPoints = 22 +POSE_PAIRS = [ [0,1],[1,2],[2,3],[3,4],[0,5],[5,6],[6,7],[7,8],[0,9],[9,10],[10,11],[11,12],[0,13],[13,14],[14,15],[15,16],[0,17],[17,18],[18,19],[19,20] ] + +inWidth = 368 +inHeight = 368 +threshold = 0.1 + + +input_source = 0 +cap = cv2.VideoCapture(input_source) +hasFrame, frame = cap.read() + +vid_writer = cv2.VideoWriter('output.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 15, (frame.shape[1],frame.shape[0])) + +net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile) +k = 0 +while 1: + k+=1 + t = time.time() + hasFrame, frame = cap.read() + frameCopy = np.copy(frame) + if not hasFrame: + cv2.waitKey() + break + + print("imread = {}".format(time.time() - t)) + + frameWidth = frame.shape[1] + frameHeight = frame.shape[0] + + inpBlob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), + (0, 0, 0), swapRB=False, crop=False) + + print("Blob = {}".format(time.time() - t)) + + net.setInput(inpBlob) + + print("setInput = {}".format(time.time() - t)) + + output = net.forward() + + print("forward = {}".format(time.time() - t)) + + # Empty list to store the detected keypoints + points = [] + + for i in range(nPoints): + # confidence map of corresponding body's part. + probMap = output[0, i, :, :] + probMap = cv2.resize(probMap, (frameWidth, frameHeight)) + + # Find global maxima of the probMap. + minVal, prob, minLoc, point = cv2.minMaxLoc(probMap) + + if prob > threshold : + cv2.circle(frameCopy, (int(point[0]), int(point[1])), 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED) + cv2.putText(frameCopy, "{}".format(i), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, lineType=cv2.LINE_AA) + + # Add the point to the list if the probability is greater than the threshold + points.append((int(point[0]), int(point[1]))) + else : + points.append(None) + + print("keypoints = {}".format(time.time() - t)) + + # Draw Skeleton + for pair in POSE_PAIRS: + partA = pair[0] + partB = pair[1] + + if points[partA] and points[partB]: + cv2.line(frame, points[partA], points[partB], (0, 255, 255), 3, lineType=cv2.LINE_AA) + cv2.circle(frame, points[partA], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED) + cv2.circle(frame, points[partB], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED) + + print("skeleton = {}".format(time.time() - t)) + + # cv2.putText(frame, "time taken = {:.2f} sec".format(time.time() - t), (50, 50), cv2.FONT_HERSHEY_COMPLEX, .8, (255, 50, 0), 2, lineType=cv2.LINE_AA) + cv2.putText(frame, "Hand Pose using OpenCV", (50, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 50, 0), 2, lineType=cv2.LINE_AA) + # cv2.imshow('Output-Keypoints', frameCopy) + cv2.imshow('Output-Skeleton', frame) + # cv2.imwrite("video_output/{:03d}.jpg".format(k), frame) + key = cv2.waitKey(1) + if key == 27: + break + + print("total = {}".format(time.time() - t)) + + vid_writer.write(frame) + +vid_writer.release()