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

Return heatmaps and backbone features during inference #212

Merged
merged 24 commits into from
Oct 31, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ mmpose/version.py
*.pkl
*.pkl.json
*.log.json
*.npy
work_dirs/

# Pytorch
Expand Down
13 changes: 10 additions & 3 deletions demo/top_down_img_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ def main():

img_keys = list(coco.imgs.keys())

# optional
return_heatmap = False
output_layer_names = ('backbone', ) # default : None
vsatyakumar marked this conversation as resolved.
Show resolved Hide resolved

# process each image
for i in range(len(img_keys)):
# get bounding box annotations
Expand All @@ -66,13 +70,16 @@ def main():
bbox = ann['bbox']
person_bboxes.append(bbox)

# test a single image, with a list of bboxes.
pose_results, heatmaps = inference_top_down_pose_model(
# test a single image, with a list of bboxes
pose_results, returned_outputs = inference_top_down_pose_model(
pose_model,
image_name,
person_bboxes,
bbox_thr=args.bbox_thr,
format='xywh',
dataset=dataset)
dataset=dataset,
return_heatmap=return_heatmap,
outputs=output_layer_names)

if args.out_img_root == '':
out_file = None
Expand Down
11 changes: 9 additions & 2 deletions demo/top_down_img_demo_with_mmdet.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,20 @@ def main():
person_bboxes = det_results[0].copy()

# test a single image, with a list of bboxes.
pose_results, heatmaps = inference_top_down_pose_model(

# optional
return_heatmap = False
output_layer_names = ('backbone', ) # default : None

pose_results, returned_outputs = inference_top_down_pose_model(
pose_model,
image_name,
person_bboxes,
bbox_thr=args.bbox_thr,
format='xyxy',
dataset=dataset)
dataset=dataset,
return_heatmap=return_heatmap,
outputs=output_layer_names)

if args.out_img_root == '':
out_file = None
Expand Down
10 changes: 8 additions & 2 deletions demo/top_down_video_demo_with_mmdet.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ def main():
f'vis_{os.path.basename(args.video_path)}'), fourcc,
fps, size)

# optional
return_heatmap = False
output_layer_names = ('backbone', ) # default : None

while (cap.isOpened()):
flag, img = cap.read()
if not flag:
Expand All @@ -81,13 +85,15 @@ def main():
person_bboxes = det_results[0].copy()

# test a single image, with a list of bboxes.
pose_results, heatmaps = inference_top_down_pose_model(
pose_results, returned_outputs = inference_top_down_pose_model(
pose_model,
img,
person_bboxes,
bbox_thr=args.bbox_thr,
format='xyxy',
dataset=dataset)
dataset=dataset,
return_heatmap=return_heatmap,
outputs=output_layer_names)

# show the results
vis_img = vis_pose_result(
Expand Down
69 changes: 48 additions & 21 deletions mmpose/apis/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from mmpose.datasets.pipelines import Compose
from mmpose.models import build_posenet
from mmpose.utils.hooks import OutputHook

os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

Expand Down Expand Up @@ -132,7 +133,11 @@ def __call__(self, results):
return results


def _inference_single_pose_model(model, img_or_path, bbox, dataset):
def _inference_single_pose_model(model,
img_or_path,
bbox,
dataset,
return_heatmap=False):
"""Inference a single bbox.

num_keypoints: K
Expand All @@ -143,11 +148,14 @@ def _inference_single_pose_model(model, img_or_path, bbox, dataset):
bbox (list | np.ndarray): Bounding boxes (with scores),
shaped (4, ) or (5, ). (left, top, width, height, [score])
dataset (str): Dataset name.
outputs (list[str] | tuple[str]): Names of layers whose output is
to be returned, default: None

Returns:
ndarray[Kx3]: Predicted pose x, y, score.
ndarray[NxKxHxW]: Model output heatmap.
heatmap[N, K, H, W]: Model output heatmap.
"""

cfg = model.cfg
device = next(model.parameters()).device

Expand Down Expand Up @@ -224,8 +232,10 @@ def _inference_single_pose_model(model, img_or_path, bbox, dataset):
# forward the model
with torch.no_grad():
all_preds, _, _, heatmap = model(
return_loss=False, img=data['img'], img_metas=data['img_metas'])

return_loss=False,
return_heatmap=return_heatmap,
img=data['img'],
img_metas=data['img_metas'])
return all_preds[0], heatmap


Expand All @@ -234,7 +244,9 @@ def inference_top_down_pose_model(model,
person_bboxes,
bbox_thr=None,
format='xywh',
dataset='TopDownCocoDataset'):
dataset='TopDownCocoDataset',
return_heatmap=False,
outputs=None):
"""Inference a single image with a list of person bounding boxes.

num_people: P
Expand All @@ -253,38 +265,53 @@ def inference_top_down_pose_model(model,
'xyxy' means (left, top, right, bottom),
'xywh' means (left, top, width, height).
dataset (str): Dataset name, e.g. 'TopDownCocoDataset'.
return_heatmap (bool) : Flag to return heatmap, default: False
outputs (list(str) | tuple(str)) : Names of layers whose outputs
need to be returned, default: None

Returns:
list[dict]: The bbox & pose info.

list[dict]: The bbox & pose info,
Each item in the list is a dictionary,
containing the bbox: (left, top, right, bottom, [score])
and the pose (ndarray[Kx3]): x, y, score
list[dict[np.ndarray[N, K, H, W] | torch.tensor[N, K, H, W]]]:
Output feature maps from layers specified in `outputs`.
Includes 'heatmap' if `return_heatmap` is True.
"""
# only two kinds of bbox format is supported.
assert format in ['xyxy', 'xywh']
# transform the bboxes format to xywh
if format == 'xyxy':
person_bboxes = _xyxy2xywh(np.array(person_bboxes))

pose_results = []
heatmaps = []
returned_outputs = []

if len(person_bboxes) > 0:
if bbox_thr is not None:
person_bboxes = person_bboxes[person_bboxes[:, 4] > bbox_thr]
for bbox in person_bboxes:
pose, heatmap = _inference_single_pose_model(
model, img_or_path, bbox, dataset)
pose_results.append({
'bbox':
_xywh2xyxy(np.expand_dims(np.array(bbox), 0)),
'keypoints':
pose,
})

heatmaps.append(heatmap)

return pose_results, heatmaps

with OutputHook(model, outputs=outputs, as_tensor=True) as h:
for bbox in person_bboxes:
pose, heatmap = _inference_single_pose_model(
model,
img_or_path,
bbox,
dataset,
return_heatmap=return_heatmap)

if return_heatmap:
h.layer_outputs['heatmap'] = heatmap

returned_outputs.append(h.layer_outputs)
pose_results.append({
'bbox':
_xywh2xyxy(np.expand_dims(np.array(bbox), 0)),
'keypoints':
pose
})

return pose_results, returned_outputs


def inference_bottom_up_pose_model(model, img_or_path):
Expand Down
5 changes: 2 additions & 3 deletions mmpose/datasets/datasets/top_down/topdown_coco_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,16 +294,15 @@ def evaluate(self, outputs, res_folder, metric='mAP', **kwargs):
heatmap width: W

Args:
outputs (list(preds, boxes, image_path, output_heatmap))
outputs (list(preds, boxes, image_path, heatmap))
:preds (np.ndarray[1,K,3]): The first two dimensions are
coordinates, score is the third dimension of the array.
:boxes (np.ndarray[1,6]): [center[0], center[1], scale[0]
, scale[1],area, score]
:image_path (list[str]): For example, [ '/', 'v','a', 'l',
'2', '0', '1', '7', '/', '0', '0', '0', '0', '0',
'0', '3', '9', '7', '1', '3', '3', '.', 'j', 'p', 'g']
:output_heatmap (np.ndarray[N, K, H, W]): model outpus.

:heatmap (np.ndarray[N, K, H, W]): model output heatmap.
res_folder (str): Path of directory to save the results.
metric (str | list[str]): Metric to be performed. Defaults: 'mAP'.

Expand Down
4 changes: 2 additions & 2 deletions mmpose/datasets/datasets/top_down/topdown_mpii_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ def evaluate(self, outputs, res_folder, metric='PCKh', **kwargs):
heatmap width: W

Args:
outputs(list(preds, boxes, image_path, output_heatmap)):
outputs(list(preds, boxes, image_path, heatmap)):

* preds(np.ndarray[1,K,3]): The first two dimensions are
coordinates, score is the third dimension of the array.
* boxes(np.ndarray[1,6]): [center[0], center[1], scale[0]
, scale[1],area, score]
* image_path(list[str]): For example, ['0', '0',
'0', '0', '0', '1', '1', '6', '3', '.', 'j', 'p', 'g']
* output_heatmap (np.ndarray[N, K, H, W]): model outputs.
* heatmap (np.ndarray[N, K, H, W]): model output heatmap.

res_folder(str): Path of directory to save the results.
metric (str | list[str]): Metrics to be performed.
Expand Down
5 changes: 2 additions & 3 deletions mmpose/datasets/datasets/top_down/topdown_mpii_trb_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,15 @@ def evaluate(self, outputs, res_folder, metric='PCKh', **kwargs):
heatmap width: W
Args:
outputs(list(preds, boxes, image_path, output_heatmap)):
outputs(list(preds, boxes, image_path, heatmap)):
* preds(np.ndarray[1,K,3]): The first two dimensions are
coordinates, score is the third dimension of the array.
* boxes(np.ndarray[1,6]): [center[0], center[1], scale[0]
, scale[1],area, score]
* image_path(list[str]): For example, ['0', '0',
'0', '0', '0', '1', '1', '6', '3', '.', 'j', 'p', 'g']
* output_heatmap (np.ndarray[N, K, H, W]): model outputs.
* heatmap (np.ndarray[N, K, H, W]): model output heatmap.
res_folder(str): Path of directory to save the results.
metric (str | list[str]): Metrics to be performed.
Defaults: 'PCKh'.
Expand Down
28 changes: 22 additions & 6 deletions mmpose/models/detectors/top_down.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def forward(self,
target_weight=None,
img_metas=None,
return_loss=True,
return_heatmap=False,
**kwargs):
"""Calls either forward_train or forward_test depending on whether
return_loss=True. Note this setting will change the expected inputs.
Expand Down Expand Up @@ -95,16 +96,19 @@ def forward(self,
- "bbox_score": score of bbox
return_loss (bool): Option to `return loss`. `return loss=True`
for training, `return loss=False` for validation & test.
return_heatmap (bool) : Option to return heatmap.

Returns:
dict|tuple: if `return loss` is true, then return losses.
Otherwise, return predicted poses, boxes and image paths.
Otherwise, return predicted poses, boxes, image paths
and heatmaps.
"""
if return_loss:
return self.forward_train(img, target, target_weight, img_metas,
**kwargs)
else:
return self.forward_test(img, img_metas, **kwargs)
return self.forward_test(
img, img_metas, return_heatmap=return_heatmap, **kwargs)

def forward_train(self, img, target, target_weight, img_metas, **kwargs):
"""Defines the computation performed at every call when training."""
Expand Down Expand Up @@ -175,16 +179,25 @@ def forward_train(self, img, target, target_weight, img_metas, **kwargs):

return losses

def forward_test(self, img, img_metas, **kwargs):
def forward_test(self, img, img_metas, return_heatmap=False, **kwargs):
"""Defines the computation performed at every call when testing."""
assert img.size(0) == 1
assert len(img_metas) == 1

img_metas = img_metas[0]

flip_pairs = img_metas['flip_pairs']
# compute output
# compute backbone features
output = self.backbone(img)

# process head
all_preds, all_boxes, image_path, heatmap = self.process_head(
output, img, img_metas, return_heatmap=return_heatmap)

return all_preds, all_boxes, image_path, heatmap

def process_head(self, output, img, img_metas, return_heatmap=False):
"""Process heatmap and keypoints from backbone features."""
flip_pairs = img_metas['flip_pairs']

if self.with_keypoint:
output = self.keypoint_head(output)

Expand Down Expand Up @@ -239,6 +252,9 @@ def forward_test(self, img, img_metas, **kwargs):
all_boxes[0, 5] = score
image_path.extend(img_metas['image_file'])

if not return_heatmap:
output_heatmap = None

return all_preds, all_boxes, image_path, output_heatmap

def show_result(self,
Expand Down
Loading