Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jsk_perception] Add human mesh recovery(estimate people 3d pose from 2d image) #2332

Merged
merged 9 commits into from Nov 1, 2018
82 changes: 82 additions & 0 deletions doc/jsk_perception/nodes/human_mesh_recovery.rst
@@ -0,0 +1,82 @@
human_mesh_recovery.py
======================


What is this?
-------------

.. image:: ./images/human_mesh_recovery.png

Ros Wrapper of Human Mesh Recovery.
See: End-to-end Recovery of Human Shape and Pose (`https://arxiv.org/abs/1712.06584`)


Subscribing Topic
-----------------

* ``~input`` (``sensor_msgs/Image``)

Input image.

* ``~input/pose`` (``jsk_recognition_msgs/PeoplePoseArray``)

Input 2d people pose array.
This is used only when param ``~with_people_pose`` is ``true``.

Publishing Topic
----------------

* ``~output/pose`` (``jsk_recognition_msgs/PeoplePoseArray``)

Estimated 3d people pose array.

Parameters
----------

* ``~gpu`` (Int, Default: ``-1``)

GPU id. ``-1`` represents CPU mode.

* ``~num_stage`` (Int, Default: ``3``)

Number of stage size (forwarding iteration count).

* ``~smpl_model_file`` (String, Required)

Trained SMPL model file path.

* ``~resnet_v2_50_model_file`` (String, Required)

Trained ResNet_V2_50 model file path.

* ``~encoder_model_file`` (String, Required)

Trained Encoder model file path.

* ``~with_people_pose`` (Bool, Default: ``False``)

If this param is ``true``, subscribe ``~input/poes``.

* ``~approximate_sync`` (Bool, Default: ``True``)

Use approximate synchronization policy.
This is used only when param ``~with_people_pose`` is ``true``.

* ``~queue_size`` (Int, Default: ``10``)

How many messages you allow about the subscriber to keep in the queue.
This should be big when there is much difference about delay between two topics.
This is used only when param ``~with_people_pose`` is ``true``.

* ``~slop`` (Float, Default: ``0.1``)

Slop for approximate sync.
This is used only when param ``~with_people_pose`` and ``~approximate_sync`` are ``true``.


Example
-------

.. code-block:: bash

roslaunch jsk_perception sample_human_mesh_recovery.launch GPU:=0
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions jsk_perception/CMakeLists.txt
Expand Up @@ -429,6 +429,7 @@ if(CATKIN_ENABLE_TESTING)
jsk_add_rostest(test/mask_image_to_label.test)
jsk_add_rostest(test/overlay_image_color_on_mono.test)
jsk_add_rostest(test/people_pose_estimation_2d.test)
jsk_add_rostest(test/human_mesh_recovery.test)
jsk_add_rostest(test/probability_image_classifier.test)
jsk_add_rostest(test/rect_array_to_image_marker.test)
jsk_add_rostest(test/regional_feature_based_object_recognition.test)
Expand Down
Empty file.
23 changes: 23 additions & 0 deletions jsk_perception/node_scripts/hmr/net.py
@@ -0,0 +1,23 @@
import chainer
import chainer.functions as F
import chainer.links as L


class EncoderFC3Dropout(chainer.Chain):

def __init__(self):
super(EncoderFC3Dropout, self).__init__()
with self.init_scope():
self.fc1 = L.Linear(2133, 1024)
self.fc2 = L.Linear(1024, 1024)
self.fc3 = L.Linear(1024, 85)

def __call__(self, x):
h = self.fc1(x)
h = F.relu(h)
h = F.dropout(h)
h = self.fc2(h)
h = F.relu(h)
h = F.dropout(h)
h = self.fc3(h)
return h
110 changes: 110 additions & 0 deletions jsk_perception/node_scripts/hmr/resnet_v2_50.py
@@ -0,0 +1,110 @@
import chainer
import chainer.functions as F
from chainer import initializers
import chainer.links as L


class BottleNeckA(chainer.Chain):

def __init__(self, in_size, ch, out_size, stride=2):
super(BottleNeckA, self).__init__()
initialW = initializers.HeNormal()
self.stride = stride

with self.init_scope():
self.preact = L.BatchNormalization(in_size)
self.conv1 = L.Convolution2D(
in_size, ch, 1, stride, 0, initialW=initialW, nobias=True)
self.bn1 = L.BatchNormalization(ch)
self.conv2 = L.Convolution2D(
ch, ch, 3, stride, 1, initialW=initialW, nobias=True)
self.bn2 = L.BatchNormalization(ch)
self.conv3 = L.Convolution2D(
ch, out_size, 1, 1, 0, initialW=initialW, nobias=False)
self.conv4 = L.Convolution2D(
in_size, out_size, 1, stride, 0,
initialW=initialW, nobias=False)

def __call__(self, x):
preact = F.relu(self.preact(x))
shortcut = self.conv4(preact)
h = self.conv1(preact)
h = F.relu(self.bn1(h))
h = F.relu(self.bn2(self.conv2(h)))
h = self.conv3(h)
return shortcut + h


class BottleNeckB(chainer.Chain):

def __init__(self, in_size, ch, stride=2):
super(BottleNeckB, self).__init__()
initialW = initializers.HeNormal()
self.stride = stride

with self.init_scope():
self.preact = L.BatchNormalization(in_size)
self.conv1 = L.Convolution2D(
in_size, ch, 1, 1, 0, initialW=initialW, nobias=True)
self.bn1 = L.BatchNormalization(ch)
self.conv2 = L.Convolution2D(
ch, ch, 3, self.stride, 1, initialW=initialW, nobias=True)
self.bn2 = L.BatchNormalization(ch)
self.conv3 = L.Convolution2D(
ch, in_size, 1, 1, 0, initialW=initialW, nobias=False)

def __call__(self, x):
preact = F.relu(self.preact(x))
if self.stride == 1:
shortcut = x
else:
shortcut = F.max_pooling_2d(
x, 1, stride=self.stride, cover_all=False)
h = self.conv1(preact)
h = F.relu(self.bn1(h))
h = F.relu(self.bn2(self.conv2(h)))
h = self.conv3(h)
return shortcut + h


class Block(chainer.ChainList):

def __init__(self, layer, in_size, ch, out_size, stride=2):
super(Block, self).__init__()
self.add_link(BottleNeckA(in_size, ch, out_size, 1))
for i in range(layer - 2):
self.add_link(BottleNeckB(out_size, ch, stride=1))
self.add_link(BottleNeckB(out_size, ch, stride=stride))

def __call__(self, x):
for f in self.children():
x = f(x)
return x


class ResNet_v2_50(chainer.Chain):

insize = 224

def __init__(self):
super(ResNet_v2_50, self).__init__()
with self.init_scope():
self.conv1 = L.Convolution2D(
3, 64, 7, 2, 3, initialW=initializers.HeNormal())
self.res2 = Block(3, 64, 64, 256, stride=2)
self.res3 = Block(4, 256, 128, 512, stride=2)
self.res4 = Block(6, 512, 256, 1024, stride=2)
self.res5 = Block(3, 1024, 512, 2048, stride=1)
self.postnorm = L.BatchNormalization(2048)

def __call__(self, x):
h = self.conv1(x)
h = F.max_pooling_2d(h, 3, stride=2)
h = self.res2(h)
h = self.res3(h)
h = self.res4(h)
h = self.res5(h)
h = self.postnorm(h)
h = F.relu(h)
h = F.average_pooling_2d(h, 7, stride=1)
return h