From ef6ded49612d6085ff8d19d2b935ed09c5bf6247 Mon Sep 17 00:00:00 2001 From: VangengLab <145450894+VangengLab@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:02:42 +0800 Subject: [PATCH] Add files via upload --- .../util/__pycache__/addict.cpython-310.pyc | Bin 0 -> 5403 bytes .../util/__pycache__/box_ops.cpython-310.pyc | Bin 0 -> 3880 bytes .../util/__pycache__/config.cpython-310.pyc | Bin 0 -> 12888 bytes .../__pycache__/keypoint_ops.cpython-310.pyc | Bin 0 -> 996 bytes .../util/__pycache__/misc.cpython-310.pyc | Bin 0 -> 20810 bytes src/utils/dependencies/XPose/util/addict.py | 159 ++++ src/utils/dependencies/XPose/util/box_ops.py | 139 ++++ src/utils/dependencies/XPose/util/config.py | 428 +++++++++++ .../dependencies/XPose/util/keypoint_ops.py | 29 + src/utils/dependencies/XPose/util/misc.py | 701 ++++++++++++++++++ 10 files changed, 1456 insertions(+) create mode 100644 src/utils/dependencies/XPose/util/__pycache__/addict.cpython-310.pyc create mode 100644 src/utils/dependencies/XPose/util/__pycache__/box_ops.cpython-310.pyc create mode 100644 src/utils/dependencies/XPose/util/__pycache__/config.cpython-310.pyc create mode 100644 src/utils/dependencies/XPose/util/__pycache__/keypoint_ops.cpython-310.pyc create mode 100644 src/utils/dependencies/XPose/util/__pycache__/misc.cpython-310.pyc create mode 100644 src/utils/dependencies/XPose/util/addict.py create mode 100644 src/utils/dependencies/XPose/util/box_ops.py create mode 100644 src/utils/dependencies/XPose/util/config.py create mode 100644 src/utils/dependencies/XPose/util/keypoint_ops.py create mode 100644 src/utils/dependencies/XPose/util/misc.py diff --git a/src/utils/dependencies/XPose/util/__pycache__/addict.cpython-310.pyc b/src/utils/dependencies/XPose/util/__pycache__/addict.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70cd334abdb5704db2a57e8306a35a6f44fa42e4 GIT binary patch literal 5403 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%0Aa%@essTObiNc$Coy{^m8IoIX4 zNj_mIc>qw0U?S0#UGo*_J=!7nf;n%tr}6@QVlJL$!Dn*m;>?hqdx6CbZqB@5z9!wz zQ2W@k)8{kRr5C{71|qA(ZL;}>25Xnj{!nqz-WQ>eem6K1+yz*=Z5j34PLlNFCymA- zEU*Pw3W2nIOYlLXqF(x0M-biug5yp!=yLZ!xIyTCJlx)NM|g%ipgYeApnHCjzw;B3 z3}hI)zWedsru*!<+xG)`8pHyqoycIg>7I6k6z-n;UAHU3hq*J=)qVu!&ldT+Ie)=p z4ymce~0*B>9E%RrzHaeFfdQBfm=Hx2XFjb#>~#M%~w``v$tqBoDss znzs)xRp!mylwOX>d$*UiS=HFmb4Oj*WdqDBPI?7QeuvpF0FxPT{1;GCz(yfka28zX zbs$l|RKSW!P?3YjRlv9c7S_$G{}S#aUFuXQU&eE7rYLW2^&dj02<%M+bxn5}!kPkm z8*^85b|tXi{67SCsvQ9gj@Ypte_I7Q;C&&`jjN%X14#iM1yraoHzrmQ=0!*svIS?s zLiTGxZYcPkhqtw0p;=(hg4#ZN@xKT4)385Co~ZW;7#`&jj!0IKxckK2+kC8Me>_O8 zNfJ)byqgQ$=o|Ht+=N|G=5+kNP&U3PFy*5gyyw9UAy;_P{@jx&_G+xk=H67(O{Pc+C{C+XxuLq#j*Pr^!^ot9_a!0%5Kjr89ig&I0sKccw_h2Q z`W1&}at@_;SS2;5loqIVL`gGosGdYr0mvt1UYfE=g*#IK^<51q-I^@n9LrN}RpaHU zK9g7}BuZGrDAsUN=e0GE#J^yT3eHtUBB-QxT0S&!21@#sv%jH!ggJ$qC{Vwwrh)Fh zr0QDD^ksAsa3qPIA0Nk;X+ZtGng(*zS1|vsm9iH{3v+zb@tY*NAmiBwst%$8ibyAK{dU68{GwIr@o$*JdZ~$&&$=KM{)BF z^(}J}klD8AGeYf^*Ns%hNh;{_tW23PhXIPxVz1Ee3D3(+Xq2lv%XewI$k9zR=<|cD z9E-%mXH82i-19QuIN#|-e9#r|Q1HhtI+N9mW#$-8$#I-p&I70BY&iFp*)k9VA2R>` E5C7(jaR2}S literal 0 HcmV?d00001 diff --git a/src/utils/dependencies/XPose/util/__pycache__/config.cpython-310.pyc b/src/utils/dependencies/XPose/util/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5dc297becf58782a489e8255fd0907b567c750a4 GIT binary patch literal 12888 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 literal 0 HcmV?d00001 diff --git a/src/utils/dependencies/XPose/util/addict.py b/src/utils/dependencies/XPose/util/addict.py new file mode 100644 index 0000000..55e02d1 --- /dev/null +++ b/src/utils/dependencies/XPose/util/addict.py @@ -0,0 +1,159 @@ +import copy + + +class Dict(dict): + + def __init__(__self, *args, **kwargs): + object.__setattr__(__self, '__parent', kwargs.pop('__parent', None)) + object.__setattr__(__self, '__key', kwargs.pop('__key', None)) + object.__setattr__(__self, '__frozen', False) + for arg in args: + if not arg: + continue + elif isinstance(arg, dict): + for key, val in arg.items(): + __self[key] = __self._hook(val) + elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)): + __self[arg[0]] = __self._hook(arg[1]) + else: + for key, val in iter(arg): + __self[key] = __self._hook(val) + + for key, val in kwargs.items(): + __self[key] = __self._hook(val) + + def __setattr__(self, name, value): + if hasattr(self.__class__, name): + raise AttributeError("'Dict' object attribute " + "'{0}' is read-only".format(name)) + else: + self[name] = value + + def __setitem__(self, name, value): + isFrozen = (hasattr(self, '__frozen') and + object.__getattribute__(self, '__frozen')) + if isFrozen and name not in super(Dict, self).keys(): + raise KeyError(name) + super(Dict, self).__setitem__(name, value) + try: + p = object.__getattribute__(self, '__parent') + key = object.__getattribute__(self, '__key') + except AttributeError: + p = None + key = None + if p is not None: + p[key] = self + object.__delattr__(self, '__parent') + object.__delattr__(self, '__key') + + def __add__(self, other): + if not self.keys(): + return other + else: + self_type = type(self).__name__ + other_type = type(other).__name__ + msg = "unsupported operand type(s) for +: '{}' and '{}'" + raise TypeError(msg.format(self_type, other_type)) + + @classmethod + def _hook(cls, item): + if isinstance(item, dict): + return cls(item) + elif isinstance(item, (list, tuple)): + return type(item)(cls._hook(elem) for elem in item) + return item + + def __getattr__(self, item): + return self.__getitem__(item) + + def __missing__(self, name): + if object.__getattribute__(self, '__frozen'): + raise KeyError(name) + return self.__class__(__parent=self, __key=name) + + def __delattr__(self, name): + del self[name] + + def to_dict(self): + base = {} + for key, value in self.items(): + if isinstance(value, type(self)): + base[key] = value.to_dict() + elif isinstance(value, (list, tuple)): + base[key] = type(value)( + item.to_dict() if isinstance(item, type(self)) else + item for item in value) + else: + base[key] = value + return base + + def copy(self): + return copy.copy(self) + + def deepcopy(self): + return copy.deepcopy(self) + + def __deepcopy__(self, memo): + other = self.__class__() + memo[id(self)] = other + for key, value in self.items(): + other[copy.deepcopy(key, memo)] = copy.deepcopy(value, memo) + return other + + def update(self, *args, **kwargs): + other = {} + if args: + if len(args) > 1: + raise TypeError() + other.update(args[0]) + other.update(kwargs) + for k, v in other.items(): + if ((k not in self) or + (not isinstance(self[k], dict)) or + (not isinstance(v, dict))): + self[k] = v + else: + self[k].update(v) + + def __getnewargs__(self): + return tuple(self.items()) + + def __getstate__(self): + return self + + def __setstate__(self, state): + self.update(state) + + def __or__(self, other): + if not isinstance(other, (Dict, dict)): + return NotImplemented + new = Dict(self) + new.update(other) + return new + + def __ror__(self, other): + if not isinstance(other, (Dict, dict)): + return NotImplemented + new = Dict(other) + new.update(self) + return new + + def __ior__(self, other): + self.update(other) + return self + + def setdefault(self, key, default=None): + if key in self: + return self[key] + else: + self[key] = default + return default + + def freeze(self, shouldFreeze=True): + object.__setattr__(self, '__frozen', shouldFreeze) + for key, val in self.items(): + if isinstance(val, Dict): + val.freeze(shouldFreeze) + + def unfreeze(self): + self.freeze(False) diff --git a/src/utils/dependencies/XPose/util/box_ops.py b/src/utils/dependencies/XPose/util/box_ops.py new file mode 100644 index 0000000..fff6624 --- /dev/null +++ b/src/utils/dependencies/XPose/util/box_ops.py @@ -0,0 +1,139 @@ +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved +""" +Utilities for bounding box manipulation and GIoU. +""" +import torch, os +from torchvision.ops.boxes import box_area + + +def box_cxcywh_to_xyxy(x): + x_c, y_c, w, h = x.unbind(-1) + b = [(x_c - 0.5 * w), (y_c - 0.5 * h), + (x_c + 0.5 * w), (y_c + 0.5 * h)] + return torch.stack(b, dim=-1) + + +def box_xyxy_to_cxcywh(x): + x0, y0, x1, y1 = x.unbind(-1) + b = [(x0 + x1) / 2, (y0 + y1) / 2, + (x1 - x0), (y1 - y0)] + return torch.stack(b, dim=-1) + + +# modified from torchvision to also return the union +def box_iou(boxes1, boxes2): + area1 = box_area(boxes1) + area2 = box_area(boxes2) + + # import ipdb; ipdb.set_trace() + lt = torch.max(boxes1[:, None, :2], boxes2[:, :2]) # [N,M,2] + rb = torch.min(boxes1[:, None, 2:], boxes2[:, 2:]) # [N,M,2] + + wh = (rb - lt).clamp(min=0) # [N,M,2] + inter = wh[:, :, 0] * wh[:, :, 1] # [N,M] + + union = area1[:, None] + area2 - inter + + iou = inter / (union + 1e-6) + return iou, union + + +def generalized_box_iou(boxes1, boxes2): + """ + Generalized IoU from https://giou.stanford.edu/ + + The boxes should be in [x0, y0, x1, y1] format + + Returns a [N, M] pairwise matrix, where N = len(boxes1) + and M = len(boxes2) + """ + # degenerate boxes gives inf / nan results + # so do an early check + assert (boxes1[:, 2:] >= boxes1[:, :2]).all() + assert (boxes2[:, 2:] >= boxes2[:, :2]).all() + # except: + # import ipdb; ipdb.set_trace() + iou, union = box_iou(boxes1, boxes2) + + lt = torch.min(boxes1[:, None, :2], boxes2[:, :2]) + rb = torch.max(boxes1[:, None, 2:], boxes2[:, 2:]) + + wh = (rb - lt).clamp(min=0) # [N,M,2] + area = wh[:, :, 0] * wh[:, :, 1] + + return iou - (area - union) / (area + 1e-6) + + + +# modified from torchvision to also return the union +def box_iou_pairwise(boxes1, boxes2): + area1 = box_area(boxes1) + area2 = box_area(boxes2) + + lt = torch.max(boxes1[:, :2], boxes2[:, :2]) # [N,2] + rb = torch.min(boxes1[:, 2:], boxes2[:, 2:]) # [N,2] + + wh = (rb - lt).clamp(min=0) # [N,2] + inter = wh[:, 0] * wh[:, 1] # [N] + + union = area1 + area2 - inter + + iou = inter / union + return iou, union + + +def generalized_box_iou_pairwise(boxes1, boxes2): + """ + Generalized IoU from https://giou.stanford.edu/ + + Input: + - boxes1, boxes2: N,4 + Output: + - giou: N, 4 + """ + # degenerate boxes gives inf / nan results + # so do an early check + assert (boxes1[:, 2:] >= boxes1[:, :2]).all() + assert (boxes2[:, 2:] >= boxes2[:, :2]).all() + assert boxes1.shape == boxes2.shape + iou, union = box_iou_pairwise(boxes1, boxes2) # N, 4 + + lt = torch.min(boxes1[:, :2], boxes2[:, :2]) + rb = torch.max(boxes1[:, 2:], boxes2[:, 2:]) + + wh = (rb - lt).clamp(min=0) # [N,2] + area = wh[:, 0] * wh[:, 1] + + return iou - (area - union) / area + +def masks_to_boxes(masks): + """Compute the bounding boxes around the provided masks + + The masks should be in format [N, H, W] where N is the number of masks, (H, W) are the spatial dimensions. + + Returns a [N, 4] tensors, with the boxes in xyxy format + """ + if masks.numel() == 0: + return torch.zeros((0, 4), device=masks.device) + + h, w = masks.shape[-2:] + + y = torch.arange(0, h, dtype=torch.float) + x = torch.arange(0, w, dtype=torch.float) + y, x = torch.meshgrid(y, x) + + x_mask = (masks * x.unsqueeze(0)) + x_max = x_mask.flatten(1).max(-1)[0] + x_min = x_mask.masked_fill(~(masks.bool()), 1e8).flatten(1).min(-1)[0] + + y_mask = (masks * y.unsqueeze(0)) + y_max = y_mask.flatten(1).max(-1)[0] + y_min = y_mask.masked_fill(~(masks.bool()), 1e8).flatten(1).min(-1)[0] + + return torch.stack([x_min, y_min, x_max, y_max], 1) + +if __name__ == '__main__': + x = torch.rand(5, 4) + y = torch.rand(3, 4) + iou, union = box_iou(x, y) + import ipdb; ipdb.set_trace() diff --git a/src/utils/dependencies/XPose/util/config.py b/src/utils/dependencies/XPose/util/config.py new file mode 100644 index 0000000..2255307 --- /dev/null +++ b/src/utils/dependencies/XPose/util/config.py @@ -0,0 +1,428 @@ +# ========================================================== +# Modified from mmcv +# ========================================================== +import sys +import os.path as osp +import ast +import tempfile +import shutil +from importlib import import_module +from argparse import Action + +from .addict import Dict + +BASE_KEY = '_base_' +DELETE_KEY = '_delete_' +RESERVED_KEYS = ['filename', 'text', 'pretty_text', 'get', 'dump', 'merge_from_dict'] + + +def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): + if not osp.isfile(filename): + raise FileNotFoundError(msg_tmpl.format(filename)) + +class ConfigDict(Dict): + + def __missing__(self, name): + raise KeyError(name) + + def __getattr__(self, name): + try: + value = super(ConfigDict, self).__getattr__(name) + except KeyError: + ex = AttributeError(f"'{self.__class__.__name__}' object has no " + f"attribute '{name}'") + except Exception as e: + ex = e + else: + return value + raise ex + + +class Config(object): + """ + config files. + only support .py file as config now. + + ref: mmcv.utils.config + + Example: + >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) + >>> cfg.a + 1 + >>> cfg.b + {'b1': [0, 1]} + >>> cfg.b.b1 + [0, 1] + >>> cfg = Config.fromfile('tests/data/config/a.py') + >>> cfg.filename + "/home/kchen/projects/mmcv/tests/data/config/a.py" + >>> cfg.item4 + 'test' + >>> cfg + "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " + "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" + """ + @staticmethod + def _validate_py_syntax(filename): + with open(filename) as f: + content = f.read() + try: + ast.parse(content) + except SyntaxError: + raise SyntaxError('There are syntax errors in config ' + f'file {filename}') + + @staticmethod + def _file2dict(filename): + filename = osp.abspath(osp.expanduser(filename)) + check_file_exist(filename) + if filename.lower().endswith('.py'): + with tempfile.TemporaryDirectory() as temp_config_dir: + temp_config_file = tempfile.NamedTemporaryFile( + dir=temp_config_dir, suffix='.py') + temp_config_name = osp.basename(temp_config_file.name) + # close temp file before copy + temp_config_file.close() + shutil.copyfile(filename, + osp.join(temp_config_dir, temp_config_name)) + temp_module_name = osp.splitext(temp_config_name)[0] + sys.path.insert(0, temp_config_dir) + Config._validate_py_syntax(filename) + mod = import_module(temp_module_name) + sys.path.pop(0) + cfg_dict = { + name: value + for name, value in mod.__dict__.items() + if not name.startswith('__') + } + # delete imported module + del sys.modules[temp_module_name] + + + elif filename.lower().endswith(('.yml', '.yaml', '.json')): + from .slio import slload + cfg_dict = slload(filename) + else: + raise IOError('Only py/yml/yaml/json type are supported now!') + + cfg_text = filename + '\n' + with open(filename, 'r') as f: + cfg_text += f.read() + + # parse the base file + if BASE_KEY in cfg_dict: + cfg_dir = osp.dirname(filename) + base_filename = cfg_dict.pop(BASE_KEY) + base_filename = base_filename if isinstance( + base_filename, list) else [base_filename] + + cfg_dict_list = list() + cfg_text_list = list() + for f in base_filename: + _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) + cfg_dict_list.append(_cfg_dict) + cfg_text_list.append(_cfg_text) + + base_cfg_dict = dict() + for c in cfg_dict_list: + if len(base_cfg_dict.keys() & c.keys()) > 0: + raise KeyError('Duplicate key is not allowed among bases') + # TODO Allow the duplicate key while warnning user + base_cfg_dict.update(c) + + base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) + cfg_dict = base_cfg_dict + + # merge cfg_text + cfg_text_list.append(cfg_text) + cfg_text = '\n'.join(cfg_text_list) + + return cfg_dict, cfg_text + + @staticmethod + def _merge_a_into_b(a, b): + """merge dict `a` into dict `b` (non-inplace). + values in `a` will overwrite `b`. + copy first to avoid inplace modification + + Args: + a ([type]): [description] + b ([type]): [description] + + Returns: + [dict]: [description] + """ + # import ipdb; ipdb.set_trace() + if not isinstance(a, dict): + return a + + b = b.copy() + for k, v in a.items(): + if isinstance(v, dict) and k in b and not v.pop(DELETE_KEY, False): + + if not isinstance(b[k], dict) and not isinstance(b[k], list): + # if : + # import ipdb; ipdb.set_trace() + raise TypeError( + f'{k}={v} in child config cannot inherit from base ' + f'because {k} is a dict in the child config but is of ' + f'type {type(b[k])} in base config. You may set ' + f'`{DELETE_KEY}=True` to ignore the base config') + b[k] = Config._merge_a_into_b(v, b[k]) + elif isinstance(b, list): + try: + _ = int(k) + except: + raise TypeError( + f'b is a list, ' + f'index {k} should be an int when input but {type(k)}' + ) + b[int(k)] = Config._merge_a_into_b(v, b[int(k)]) + else: + b[k] = v + + return b + + @staticmethod + def fromfile(filename): + cfg_dict, cfg_text = Config._file2dict(filename) + return Config(cfg_dict, cfg_text=cfg_text, filename=filename) + + + def __init__(self, cfg_dict=None, cfg_text=None, filename=None): + if cfg_dict is None: + cfg_dict = dict() + elif not isinstance(cfg_dict, dict): + raise TypeError('cfg_dict must be a dict, but ' + f'got {type(cfg_dict)}') + for key in cfg_dict: + if key in RESERVED_KEYS: + raise KeyError(f'{key} is reserved for config file') + + super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) + super(Config, self).__setattr__('_filename', filename) + if cfg_text: + text = cfg_text + elif filename: + with open(filename, 'r') as f: + text = f.read() + else: + text = '' + super(Config, self).__setattr__('_text', text) + + + @property + def filename(self): + return self._filename + + @property + def text(self): + return self._text + + @property + def pretty_text(self): + + indent = 4 + + def _indent(s_, num_spaces): + s = s_.split('\n') + if len(s) == 1: + return s_ + first = s.pop(0) + s = [(num_spaces * ' ') + line for line in s] + s = '\n'.join(s) + s = first + '\n' + s + return s + + def _format_basic_types(k, v, use_mapping=False): + if isinstance(v, str): + v_str = f"'{v}'" + else: + v_str = str(v) + + if use_mapping: + k_str = f"'{k}'" if isinstance(k, str) else str(k) + attr_str = f'{k_str}: {v_str}' + else: + attr_str = f'{str(k)}={v_str}' + attr_str = _indent(attr_str, indent) + + return attr_str + + def _format_list(k, v, use_mapping=False): + # check if all items in the list are dict + if all(isinstance(_, dict) for _ in v): + v_str = '[\n' + v_str += '\n'.join( + f'dict({_indent(_format_dict(v_), indent)}),' + for v_ in v).rstrip(',') + if use_mapping: + k_str = f"'{k}'" if isinstance(k, str) else str(k) + attr_str = f'{k_str}: {v_str}' + else: + attr_str = f'{str(k)}={v_str}' + attr_str = _indent(attr_str, indent) + ']' + else: + attr_str = _format_basic_types(k, v, use_mapping) + return attr_str + + def _contain_invalid_identifier(dict_str): + contain_invalid_identifier = False + for key_name in dict_str: + contain_invalid_identifier |= \ + (not str(key_name).isidentifier()) + return contain_invalid_identifier + + def _format_dict(input_dict, outest_level=False): + r = '' + s = [] + + use_mapping = _contain_invalid_identifier(input_dict) + if use_mapping: + r += '{' + for idx, (k, v) in enumerate(input_dict.items()): + is_last = idx >= len(input_dict) - 1 + end = '' if outest_level or is_last else ',' + if isinstance(v, dict): + v_str = '\n' + _format_dict(v) + if use_mapping: + k_str = f"'{k}'" if isinstance(k, str) else str(k) + attr_str = f'{k_str}: dict({v_str}' + else: + attr_str = f'{str(k)}=dict({v_str}' + attr_str = _indent(attr_str, indent) + ')' + end + elif isinstance(v, list): + attr_str = _format_list(k, v, use_mapping) + end + else: + attr_str = _format_basic_types(k, v, use_mapping) + end + + s.append(attr_str) + r += '\n'.join(s) + if use_mapping: + r += '}' + return r + + cfg_dict = self._cfg_dict.to_dict() + text = _format_dict(cfg_dict, outest_level=True) + return text + + + def __repr__(self): + return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' + + def __len__(self): + return len(self._cfg_dict) + + def __getattr__(self, name): + # # debug + # print('+'*15) + # print('name=%s' % name) + # print("addr:", id(self)) + # # print('type(self):', type(self)) + # print(self.__dict__) + # print('+'*15) + # if self.__dict__ == {}: + # raise ValueError + + return getattr(self._cfg_dict, name) + + def __getitem__(self, name): + return self._cfg_dict.__getitem__(name) + + def __setattr__(self, name, value): + if isinstance(value, dict): + value = ConfigDict(value) + self._cfg_dict.__setattr__(name, value) + + def __setitem__(self, name, value): + if isinstance(value, dict): + value = ConfigDict(value) + self._cfg_dict.__setitem__(name, value) + + def __iter__(self): + return iter(self._cfg_dict) + + def dump(self, file=None): + # import ipdb; ipdb.set_trace() + if file is None: + return self.pretty_text + else: + with open(file, 'w') as f: + f.write(self.pretty_text) + + def merge_from_dict(self, options): + """Merge list into cfg_dict + + Merge the dict parsed by MultipleKVAction into this cfg. + + Examples: + >>> options = {'model.backbone.depth': 50, + ... 'model.backbone.with_cp':True} + >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) + >>> cfg.merge_from_dict(options) + >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') + >>> assert cfg_dict == dict( + ... model=dict(backbone=dict(depth=50, with_cp=True))) + + Args: + options (dict): dict of configs to merge from. + """ + option_cfg_dict = {} + for full_key, v in options.items(): + d = option_cfg_dict + key_list = full_key.split('.') + for subkey in key_list[:-1]: + d.setdefault(subkey, ConfigDict()) + d = d[subkey] + subkey = key_list[-1] + d[subkey] = v + + cfg_dict = super(Config, self).__getattribute__('_cfg_dict') + super(Config, self).__setattr__( + '_cfg_dict', Config._merge_a_into_b(option_cfg_dict, cfg_dict)) + + # for multiprocess + def __setstate__(self, state): + self.__init__(state) + + + def copy(self): + return Config(self._cfg_dict.copy()) + + def deepcopy(self): + return Config(self._cfg_dict.deepcopy()) + + +class DictAction(Action): + """ + argparse action to split an argument into KEY=VALUE form + on the first = and append to a dictionary. List options should + be passed as comma separated values, i.e KEY=V1,V2,V3 + """ + + @staticmethod + def _parse_int_float_bool(val): + try: + return int(val) + except ValueError: + pass + try: + return float(val) + except ValueError: + pass + if val.lower() in ['true', 'false']: + return True if val.lower() == 'true' else False + if val.lower() in ['none', 'null']: + return None + return val + + def __call__(self, parser, namespace, values, option_string=None): + options = {} + for kv in values: + key, val = kv.split('=', maxsplit=1) + val = [self._parse_int_float_bool(v) for v in val.split(',')] + if len(val) == 1: + val = val[0] + options[key] = val + setattr(namespace, self.dest, options) + diff --git a/src/utils/dependencies/XPose/util/keypoint_ops.py b/src/utils/dependencies/XPose/util/keypoint_ops.py new file mode 100644 index 0000000..036d813 --- /dev/null +++ b/src/utils/dependencies/XPose/util/keypoint_ops.py @@ -0,0 +1,29 @@ +import torch, os + +def keypoint_xyxyzz_to_xyzxyz(keypoints: torch.Tensor): + """_summary_ + + Args: + keypoints (torch.Tensor): ..., 51 + """ + res = torch.zeros_like(keypoints) + num_points = keypoints.shape[-1] // 3 + Z = keypoints[..., :2*num_points] + V = keypoints[..., 2*num_points:] + res[...,0::3] = Z[..., 0::2] + res[...,1::3] = Z[..., 1::2] + res[...,2::3] = V[...] + return res + +def keypoint_xyzxyz_to_xyxyzz(keypoints: torch.Tensor): + """_summary_ + + Args: + keypoints (torch.Tensor): ..., 51 + """ + res = torch.zeros_like(keypoints) + num_points = keypoints.shape[-1] // 3 + res[...,0:2*num_points:2] = keypoints[..., 0::3] + res[...,1:2*num_points:2] = keypoints[..., 1::3] + res[...,2*num_points:] = keypoints[..., 2::3] + return res \ No newline at end of file diff --git a/src/utils/dependencies/XPose/util/misc.py b/src/utils/dependencies/XPose/util/misc.py new file mode 100644 index 0000000..0fa90f3 --- /dev/null +++ b/src/utils/dependencies/XPose/util/misc.py @@ -0,0 +1,701 @@ +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved +""" +Misc functions, including distributed helpers. + +Mostly copy-paste from torchvision references. +""" +import functools +import io +import os +import random +import subprocess +import time +from collections import OrderedDict, defaultdict, deque +import datetime +import pickle +from typing import Optional, List + +import json, time +import numpy as np +import torch +import torch.distributed as dist +from torch import Tensor + +import colorsys + +# needed due to empty tensor bug in pytorch and torchvision 0.5 +import torchvision +__torchvision_need_compat_flag = float(torchvision.__version__.split('.')[1]) < 7 +if __torchvision_need_compat_flag: + from torchvision.ops import _new_empty_tensor + from torchvision.ops.misc import _output_size + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + if d.shape[0] == 0: + return 0 + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + if os.environ.get("SHILONG_AMP", None) == '1': + eps = 1e-4 + else: + eps = 1e-6 + return self.total / (self.count + eps) + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) + +@functools.lru_cache() +def _get_global_gloo_group(): + """ + Return a process group based on gloo backend, containing all the ranks + The result is cached. + """ + + if dist.get_backend() == "nccl": + return dist.new_group(backend="gloo") + + return dist.group.WORLD + +def all_gather_cpu(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + + world_size = get_world_size() + if world_size == 1: + return [data] + + cpu_group = _get_global_gloo_group() + + buffer = io.BytesIO() + torch.save(data, buffer) + data_view = buffer.getbuffer() + device = "cuda" if cpu_group is None else "cpu" + tensor = torch.ByteTensor(data_view).to(device) + + # obtain Tensor size of each rank + local_size = torch.tensor([tensor.numel()], device=device, dtype=torch.long) + size_list = [torch.tensor([0], device=device, dtype=torch.long) for _ in range(world_size)] + if cpu_group is None: + dist.all_gather(size_list, local_size) + else: + print("gathering on cpu") + dist.all_gather(size_list, local_size, group=cpu_group) + size_list = [int(size.item()) for size in size_list] + max_size = max(size_list) + assert isinstance(local_size.item(), int) + local_size = int(local_size.item()) + + # receiving Tensor from all ranks + # we pad the tensor because torch all_gather does not support + # gathering tensors of different shapes + tensor_list = [] + for _ in size_list: + tensor_list.append(torch.empty((max_size,), dtype=torch.uint8, device=device)) + if local_size != max_size: + padding = torch.empty(size=(max_size - local_size,), dtype=torch.uint8, device=device) + tensor = torch.cat((tensor, padding), dim=0) + if cpu_group is None: + dist.all_gather(tensor_list, tensor) + else: + dist.all_gather(tensor_list, tensor, group=cpu_group) + + data_list = [] + for size, tensor in zip(size_list, tensor_list): + tensor = torch.split(tensor, [size, max_size - size], dim=0)[0] + buffer = io.BytesIO(tensor.cpu().numpy()) + obj = torch.load(buffer) + data_list.append(obj) + + return data_list + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + + if os.getenv("CPU_REDUCE") == "1": + return all_gather_cpu(data) + + + + world_size = get_world_size() + if world_size == 1: + return [data] + + # serialized to a Tensor + buffer = pickle.dumps(data) + storage = torch.ByteStorage.from_buffer(buffer) + tensor = torch.ByteTensor(storage).to("cuda") + + # obtain Tensor size of each rank + local_size = torch.tensor([tensor.numel()], device="cuda") + size_list = [torch.tensor([0], device="cuda") for _ in range(world_size)] + dist.all_gather(size_list, local_size) + size_list = [int(size.item()) for size in size_list] + max_size = max(size_list) + + # receiving Tensor from all ranks + # we pad the tensor because torch all_gather does not support + # gathering tensors of different shapes + tensor_list = [] + for _ in size_list: + tensor_list.append(torch.empty((max_size,), dtype=torch.uint8, device="cuda")) + if local_size != max_size: + padding = torch.empty(size=(max_size - local_size,), dtype=torch.uint8, device="cuda") + tensor = torch.cat((tensor, padding), dim=0) + dist.all_gather(tensor_list, tensor) + + data_list = [] + for size, tensor in zip(size_list, tensor_list): + buffer = tensor.cpu().numpy().tobytes()[:size] + data_list.append(pickle.loads(buffer)) + + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict + + +class MetricLogger(object): + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + # print(name, str(meter)) + # import ipdb;ipdb.set_trace() + if meter.count > 0: + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None, logger=None): + if logger is None: + print_func = print + else: + print_func = logger.info + + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + if torch.cuda.is_available(): + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}', + 'max mem: {memory:.0f}' + ]) + else: + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + MB = 1024.0 * 1024.0 + for obj in iterable: + data_time.update(time.time() - end) + yield obj + # import ipdb; ipdb.set_trace() + iter_time.update(time.time() - end) + if i % print_freq == 0 or i == len(iterable) - 1: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + if torch.cuda.is_available(): + print_func(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time), + memory=torch.cuda.max_memory_allocated() / MB)) + else: + print_func(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print_func('{} Total time: {} ({:.4f} s / it)'.format( + header, total_time_str, total_time / len(iterable))) + + +def get_sha(): + cwd = os.path.dirname(os.path.abspath(__file__)) + + def _run(command): + return subprocess.check_output(command, cwd=cwd).decode('ascii').strip() + sha = 'N/A' + diff = "clean" + branch = 'N/A' + try: + sha = _run(['git', 'rev-parse', 'HEAD']) + subprocess.check_output(['git', 'diff'], cwd=cwd) + diff = _run(['git', 'diff-index', 'HEAD']) + diff = "has uncommited changes" if diff else "clean" + branch = _run(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) + except Exception: + pass + message = f"sha: {sha}, status: {diff}, branch: {branch}" + return message + + +def collate_fn(batch): + # import ipdb; ipdb.set_trace() + batch = list(zip(*batch)) + batch[0] = nested_tensor_from_tensor_list(batch[0]) + return tuple(batch) + + +def _max_by_axis(the_list): + # type: (List[List[int]]) -> List[int] + maxes = the_list[0] + for sublist in the_list[1:]: + for index, item in enumerate(sublist): + maxes[index] = max(maxes[index], item) + return maxes + + +class NestedTensor(object): + def __init__(self, tensors, mask: Optional[Tensor]): + self.tensors = tensors + self.mask = mask + if mask == 'auto': + self.mask = torch.zeros_like(tensors).to(tensors.device) + if self.mask.dim() == 3: + self.mask = self.mask.sum(0).to(bool) + elif self.mask.dim() == 4: + self.mask = self.mask.sum(1).to(bool) + else: + raise ValueError("tensors dim must be 3 or 4 but {}({})".format(self.tensors.dim(), self.tensors.shape)) + + def imgsize(self): + res = [] + for i in range(self.tensors.shape[0]): + mask = self.mask[i] + maxH = (~mask).sum(0).max() + maxW = (~mask).sum(1).max() + res.append(torch.Tensor([maxH, maxW])) + return res + + def to(self, device): + # type: (Device) -> NestedTensor # noqa + cast_tensor = self.tensors.to(device) + mask = self.mask + if mask is not None: + assert mask is not None + cast_mask = mask.to(device) + else: + cast_mask = None + return NestedTensor(cast_tensor, cast_mask) + + def to_img_list_single(self, tensor, mask): + assert tensor.dim() == 3, "dim of tensor should be 3 but {}".format(tensor.dim()) + maxH = (~mask).sum(0).max() + maxW = (~mask).sum(1).max() + img = tensor[:, :maxH, :maxW] + return img + + def to_img_list(self): + """remove the padding and convert to img list + + Returns: + [type]: [description] + """ + if self.tensors.dim() == 3: + return self.to_img_list_single(self.tensors, self.mask) + else: + res = [] + for i in range(self.tensors.shape[0]): + tensor_i = self.tensors[i] + mask_i = self.mask[i] + res.append(self.to_img_list_single(tensor_i, mask_i)) + return res + + @property + def device(self): + return self.tensors.device + + def decompose(self): + return self.tensors, self.mask + + def __repr__(self): + return str(self.tensors) + + @property + def shape(self): + return { + 'tensors.shape': self.tensors.shape, + 'mask.shape': self.mask.shape + } + + +def nested_tensor_from_tensor_list(tensor_list: List[Tensor]): + # TODO make this more general + if tensor_list[0].ndim == 3: + if torchvision._is_tracing(): + # nested_tensor_from_tensor_list() does not export well to ONNX + # call _onnx_nested_tensor_from_tensor_list() instead + return _onnx_nested_tensor_from_tensor_list(tensor_list) + + # TODO make it support different-sized images + max_size = _max_by_axis([list(img.shape) for img in tensor_list]) + # min_size = tuple(min(s) for s in zip(*[img.shape for img in tensor_list])) + batch_shape = [len(tensor_list)] + max_size + b, c, h, w = batch_shape + dtype = tensor_list[0].dtype + device = tensor_list[0].device + tensor = torch.zeros(batch_shape, dtype=dtype, device=device) + mask = torch.ones((b, h, w), dtype=torch.bool, device=device) + for img, pad_img, m in zip(tensor_list, tensor, mask): + pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img) + m[: img.shape[1], :img.shape[2]] = False + else: + raise ValueError('not supported') + return NestedTensor(tensor, mask) + + +# _onnx_nested_tensor_from_tensor_list() is an implementation of +# nested_tensor_from_tensor_list() that is supported by ONNX tracing. +@torch.jit.unused +def _onnx_nested_tensor_from_tensor_list(tensor_list: List[Tensor]) -> NestedTensor: + max_size = [] + for i in range(tensor_list[0].dim()): + max_size_i = torch.max(torch.stack([img.shape[i] for img in tensor_list]).to(torch.float32)).to(torch.int64) + max_size.append(max_size_i) + max_size = tuple(max_size) + + # work around for + # pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img) + # m[: img.shape[1], :img.shape[2]] = False + # which is not yet supported in onnx + padded_imgs = [] + padded_masks = [] + for img in tensor_list: + padding = [(s1 - s2) for s1, s2 in zip(max_size, tuple(img.shape))] + padded_img = torch.nn.functional.pad(img, (0, padding[2], 0, padding[1], 0, padding[0])) + padded_imgs.append(padded_img) + + m = torch.zeros_like(img[0], dtype=torch.int, device=img.device) + padded_mask = torch.nn.functional.pad(m, (0, padding[2], 0, padding[1]), "constant", 1) + padded_masks.append(padded_mask.to(torch.bool)) + + tensor = torch.stack(padded_imgs) + mask = torch.stack(padded_masks) + + return NestedTensor(tensor, mask=mask) + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + +def init_distributed_mode(args): + if 'WORLD_SIZE' in os.environ and os.environ['WORLD_SIZE'] != '': # 'RANK' in os.environ and + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = args.local_rank = int(os.environ['LOCAL_RANK']) + + # launch by torch.distributed.launch + # Single node + # python -m torch.distributed.launch --nproc_per_node=8 main.py --world-size 1 --rank 0 ... + # Multi nodes + # python -m torch.distributed.launch --nproc_per_node=8 main.py --world-size 2 --rank 0 --dist-url 'tcp://IP_OF_NODE0:FREEPORT' ... + # python -m torch.distributed.launch --nproc_per_node=8 main.py --world-size 2 --rank 1 --dist-url 'tcp://IP_OF_NODE0:FREEPORT' ... + # args.rank = int(os.environ.get('OMPI_COMM_WORLD_RANK')) + # local_world_size = int(os.environ['GPU_PER_NODE_COUNT']) + # args.world_size = args.world_size * local_world_size + # args.gpu = args.local_rank = int(os.environ['LOCAL_RANK']) + # args.rank = args.rank * local_world_size + args.local_rank + print('world size: {}, rank: {}, local rank: {}'.format(args.world_size, args.rank, args.local_rank)) + print(json.dumps(dict(os.environ), indent=2)) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.local_rank = int(os.environ['SLURM_LOCALID']) + args.world_size = int(os.environ['SLURM_NPROCS']) + + if os.environ.get('HAND_DEFINE_DIST_URL', 0) == '1': + pass + else: + import util.hostlist as uh + nodenames = uh.parse_nodelist(os.environ['SLURM_JOB_NODELIST']) + gpu_ids = [int(node[3:]) for node in nodenames] + fixid = int(os.environ.get('FIX_DISTRIBUTED_PORT_NUMBER', 0)) + # fixid += random.randint(0, 300) + port = str(3137 + int(min(gpu_ids)) + fixid) + args.dist_url = "tcp://{ip}:{port}".format(ip=uh.nodename_to_ip(nodenames[0]), port=port) + + print('world size: {}, world rank: {}, local rank: {}, device_count: {}'.format(args.world_size, args.rank, args.local_rank, torch.cuda.device_count())) + + + else: + print('Not using distributed mode') + args.distributed = False + args.world_size = 1 + args.rank = 0 + args.local_rank = 0 + return + + print("world_size:{} rank:{} local_rank:{}".format(args.world_size, args.rank, args.local_rank)) + args.distributed = True + torch.cuda.set_device(args.local_rank) + args.dist_backend = 'nccl' + print('| distributed init (rank {}): {}'.format(args.rank, args.dist_url), flush=True) + + torch.distributed.init_process_group( + backend=args.dist_backend, + world_size=args.world_size, + rank=args.rank, + init_method=args.dist_url, + ) + + print("Before torch.distributed.barrier()") + torch.distributed.barrier() + print("End torch.distributed.barrier()") + setup_for_distributed(args.rank == 0) + + +@torch.no_grad() +def accuracy(output, target, topk=(1,)): + """Computes the precision@k for the specified values of k""" + if target.numel() == 0: + return [torch.zeros([], device=output.device)] + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target.view(1, -1).expand_as(pred)) + + res = [] + for k in topk: + correct_k = correct[:k].view(-1).float().sum(0) + res.append(correct_k.mul_(100.0 / batch_size)) + return res + +@torch.no_grad() +def accuracy_onehot(pred, gt): + """_summary_ + + Args: + pred (_type_): n, c + gt (_type_): n, c + """ + tp = ((pred - gt).abs().sum(-1) < 1e-4).float().sum() + acc = tp / gt.shape[0] * 100 + return acc + + + + + +def interpolate(input, size=None, scale_factor=None, mode="nearest", align_corners=None): + # type: (Tensor, Optional[List[int]], Optional[float], str, Optional[bool]) -> Tensor + """ + Equivalent to nn.functional.interpolate, but with support for empty batch sizes. + This will eventually be supported natively by PyTorch, and this + class can go away. + """ + if __torchvision_need_compat_flag < 0.7: + if input.numel() > 0: + return torch.nn.functional.interpolate( + input, size, scale_factor, mode, align_corners + ) + + output_shape = _output_size(2, input, size, scale_factor) + output_shape = list(input.shape[:-2]) + list(output_shape) + return _new_empty_tensor(input, output_shape) + else: + return torchvision.ops.misc.interpolate(input, size, scale_factor, mode, align_corners) + + + +class color_sys(): + def __init__(self, num_colors) -> None: + self.num_colors = num_colors + colors=[] + for i in np.arange(0., 360., 360. / num_colors): + hue = i/360. + lightness = (50 + np.random.rand() * 10)/100. + saturation = (90 + np.random.rand() * 10)/100. + colors.append(tuple([int(j*255) for j in colorsys.hls_to_rgb(hue, lightness, saturation)])) + self.colors = colors + + def __call__(self, idx): + return self.colors[idx] + +def inverse_sigmoid(x, eps=1e-3): + x = x.clamp(min=0, max=1) + x1 = x.clamp(min=eps) + x2 = (1 - x).clamp(min=eps) + return torch.log(x1/x2) + +def clean_state_dict(state_dict): + new_state_dict = OrderedDict() + for k, v in state_dict.items(): + if k[:7] == 'module.': + k = k[7:] # remove `module.` + new_state_dict[k] = v + return new_state_dict \ No newline at end of file