Initial snapshot: hand motion pipeline (Dyn-HaMR + dex-retargeting + SPIDER)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from .collect_env import collect_env
|
||||
from .logger import get_root_logger
|
||||
from .setup_env import setup_multi_processes
|
||||
from .timer import StopWatch
|
||||
|
||||
__all__ = [
|
||||
'get_root_logger', 'collect_env', 'StopWatch', 'setup_multi_processes'
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from mmcv.utils import collect_env as collect_basic_env
|
||||
from mmcv.utils import get_git_hash
|
||||
|
||||
import mmpose
|
||||
|
||||
|
||||
def collect_env():
|
||||
env_info = collect_basic_env()
|
||||
env_info['MMPose'] = (mmpose.__version__ + '+' + get_git_hash(digits=7))
|
||||
return env_info
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
for name, val in collect_env().items():
|
||||
print(f'{name}: {val}')
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import functools
|
||||
|
||||
|
||||
class OutputHook:
|
||||
|
||||
def __init__(self, module, outputs=None, as_tensor=False):
|
||||
self.outputs = outputs
|
||||
self.as_tensor = as_tensor
|
||||
self.layer_outputs = {}
|
||||
self.register(module)
|
||||
|
||||
def register(self, module):
|
||||
|
||||
def hook_wrapper(name):
|
||||
|
||||
def hook(model, input, output):
|
||||
if self.as_tensor:
|
||||
self.layer_outputs[name] = output
|
||||
else:
|
||||
if isinstance(output, list):
|
||||
self.layer_outputs[name] = [
|
||||
out.detach().cpu().numpy() for out in output
|
||||
]
|
||||
else:
|
||||
self.layer_outputs[name] = output.detach().cpu().numpy(
|
||||
)
|
||||
|
||||
return hook
|
||||
|
||||
self.handles = []
|
||||
if isinstance(self.outputs, (list, tuple)):
|
||||
for name in self.outputs:
|
||||
try:
|
||||
layer = rgetattr(module, name)
|
||||
h = layer.register_forward_hook(hook_wrapper(name))
|
||||
except ModuleNotFoundError as module_not_found:
|
||||
raise ModuleNotFoundError(
|
||||
f'Module {name} not found') from module_not_found
|
||||
self.handles.append(h)
|
||||
|
||||
def remove(self):
|
||||
for h in self.handles:
|
||||
h.remove()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.remove()
|
||||
|
||||
|
||||
# using wonder's beautiful simplification:
|
||||
# https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects
|
||||
def rgetattr(obj, attr, *args):
|
||||
|
||||
def _getattr(obj, attr):
|
||||
return getattr(obj, attr, *args)
|
||||
|
||||
return functools.reduce(_getattr, [obj] + attr.split('.'))
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import logging
|
||||
|
||||
from mmcv.utils import get_logger
|
||||
|
||||
|
||||
def get_root_logger(log_file=None, log_level=logging.INFO):
|
||||
"""Use `get_logger` method in mmcv to get the root logger.
|
||||
|
||||
The logger will be initialized if it has not been initialized. By default a
|
||||
StreamHandler will be added. If `log_file` is specified, a FileHandler will
|
||||
also be added. The name of the root logger is the top-level package name,
|
||||
e.g., "mmpose".
|
||||
|
||||
Args:
|
||||
log_file (str | None): The log filename. If specified, a FileHandler
|
||||
will be added to the root logger.
|
||||
log_level (int): The root logger level. Note that only the process of
|
||||
rank 0 is affected, while other processes will set the level to
|
||||
"Error" and be silent most of the time.
|
||||
|
||||
Returns:
|
||||
logging.Logger: The root logger.
|
||||
"""
|
||||
return get_logger(__name__.split('.')[0], log_file, log_level)
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import os
|
||||
import platform
|
||||
import warnings
|
||||
|
||||
import cv2
|
||||
import torch.multiprocessing as mp
|
||||
|
||||
|
||||
def setup_multi_processes(cfg):
|
||||
"""Setup multi-processing environment variables."""
|
||||
# set multi-process start method as `fork` to speed up the training
|
||||
if platform.system() != 'Windows':
|
||||
mp_start_method = cfg.get('mp_start_method', 'fork')
|
||||
current_method = mp.get_start_method(allow_none=True)
|
||||
if current_method is not None and current_method != mp_start_method:
|
||||
warnings.warn(
|
||||
f'Multi-processing start method `{mp_start_method}` is '
|
||||
f'different from the previous setting `{current_method}`.'
|
||||
f'It will be force set to `{mp_start_method}`. You can change '
|
||||
f'this behavior by changing `mp_start_method` in your config.')
|
||||
mp.set_start_method(mp_start_method, force=True)
|
||||
|
||||
# disable opencv multithreading to avoid system being overloaded
|
||||
opencv_num_threads = cfg.get('opencv_num_threads', 0)
|
||||
cv2.setNumThreads(opencv_num_threads)
|
||||
|
||||
# setup OMP threads
|
||||
# This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa
|
||||
if 'OMP_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1:
|
||||
omp_num_threads = 1
|
||||
warnings.warn(
|
||||
f'Setting OMP_NUM_THREADS environment variable for each process '
|
||||
f'to be {omp_num_threads} in default, to avoid your system being '
|
||||
f'overloaded, please further tune the variable for optimal '
|
||||
f'performance in your application as needed.')
|
||||
os.environ['OMP_NUM_THREADS'] = str(omp_num_threads)
|
||||
|
||||
# setup MKL threads
|
||||
if 'MKL_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1:
|
||||
mkl_num_threads = 1
|
||||
warnings.warn(
|
||||
f'Setting MKL_NUM_THREADS environment variable for each process '
|
||||
f'to be {mkl_num_threads} in default, to avoid your system being '
|
||||
f'overloaded, please further tune the variable for optimal '
|
||||
f'performance in your application as needed.')
|
||||
os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads)
|
||||
@@ -0,0 +1,117 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from functools import partial
|
||||
|
||||
import numpy as np
|
||||
from mmcv import Timer
|
||||
|
||||
|
||||
class RunningAverage():
|
||||
r"""A helper class to calculate running average in a sliding window.
|
||||
|
||||
Args:
|
||||
window (int): The size of the sliding window.
|
||||
"""
|
||||
|
||||
def __init__(self, window: int = 1):
|
||||
self.window = window
|
||||
self._data = []
|
||||
|
||||
def update(self, value):
|
||||
"""Update a new data sample."""
|
||||
self._data.append(value)
|
||||
self._data = self._data[-self.window:]
|
||||
|
||||
def average(self):
|
||||
"""Get the average value of current window."""
|
||||
return np.mean(self._data)
|
||||
|
||||
|
||||
class StopWatch:
|
||||
r"""A helper class to measure FPS and detailed time consuming of each phase
|
||||
in a video processing loop or similar scenarios.
|
||||
|
||||
Args:
|
||||
window (int): The sliding window size to calculate the running average
|
||||
of the time consuming.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.utils import StopWatch
|
||||
>>> import time
|
||||
>>> stop_watch = StopWatch(window=10)
|
||||
>>> with stop_watch.timeit('total'):
|
||||
>>> time.sleep(0.1)
|
||||
>>> # 'timeit' support nested use
|
||||
>>> with stop_watch.timeit('phase1'):
|
||||
>>> time.sleep(0.1)
|
||||
>>> with stop_watch.timeit('phase2'):
|
||||
>>> time.sleep(0.2)
|
||||
>>> time.sleep(0.2)
|
||||
>>> report = stop_watch.report()
|
||||
"""
|
||||
|
||||
def __init__(self, window=1):
|
||||
self.window = window
|
||||
self._record = defaultdict(partial(RunningAverage, window=self.window))
|
||||
self._timer_stack = []
|
||||
|
||||
@contextmanager
|
||||
def timeit(self, timer_name='_FPS_'):
|
||||
"""Timing a code snippet with an assigned name.
|
||||
|
||||
Args:
|
||||
timer_name (str): The unique name of the interested code snippet to
|
||||
handle multiple timers and generate reports. Note that '_FPS_'
|
||||
is a special key that the measurement will be in `fps` instead
|
||||
of `millisecond`. Also see `report` and `report_strings`.
|
||||
Default: '_FPS_'.
|
||||
Note:
|
||||
This function should always be used in a `with` statement, as shown
|
||||
in the example.
|
||||
"""
|
||||
self._timer_stack.append((timer_name, Timer()))
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
timer_name, timer = self._timer_stack.pop()
|
||||
self._record[timer_name].update(timer.since_start())
|
||||
|
||||
def report(self, key=None):
|
||||
"""Report timing information.
|
||||
|
||||
Returns:
|
||||
dict: The key is the timer name and the value is the \
|
||||
corresponding average time consuming.
|
||||
"""
|
||||
result = {
|
||||
name: r.average() * 1000.
|
||||
for name, r in self._record.items()
|
||||
}
|
||||
|
||||
if '_FPS_' in result:
|
||||
result['_FPS_'] = 1000. / result.pop('_FPS_')
|
||||
|
||||
if key is None:
|
||||
return result
|
||||
return result[key]
|
||||
|
||||
def report_strings(self):
|
||||
"""Report timing information in texture strings.
|
||||
|
||||
Returns:
|
||||
list(str): Each element is the information string of a timed \
|
||||
event, in format of '{timer_name}: {time_in_ms}'. \
|
||||
Specially, if timer_name is '_FPS_', the result will \
|
||||
be converted to fps.
|
||||
"""
|
||||
result = self.report()
|
||||
strings = []
|
||||
if '_FPS_' in result:
|
||||
strings.append(f'FPS: {result["_FPS_"]:>5.1f}')
|
||||
strings += [f'{name}: {val:>3.0f}' for name, val in result.items()]
|
||||
return strings
|
||||
|
||||
def reset(self):
|
||||
self._record = defaultdict(list)
|
||||
self._active_timer_stack = []
|
||||
Reference in New Issue
Block a user