|
|
| import copy
|
| import logging
|
| import types
|
| from collections import UserDict
|
| from typing import List
|
|
|
| from detectron2.utils.logger import log_first_n
|
|
|
| __all__ = ["DatasetCatalog", "MetadataCatalog", "Metadata"]
|
|
|
|
|
| class _DatasetCatalog(UserDict):
|
| """
|
| A global dictionary that stores information about the datasets and how to obtain them.
|
|
|
| It contains a mapping from strings
|
| (which are names that identify a dataset, e.g. "coco_2014_train")
|
| to a function which parses the dataset and returns the samples in the
|
| format of `list[dict]`.
|
|
|
| The returned dicts should be in Detectron2 Dataset format (See DATASETS.md for details)
|
| if used with the data loader functionalities in `data/build.py,data/detection_transform.py`.
|
|
|
| The purpose of having this catalog is to make it easy to choose
|
| different datasets, by just using the strings in the config.
|
| """
|
|
|
| def register(self, name, func):
|
| """
|
| Args:
|
| name (str): the name that identifies a dataset, e.g. "coco_2014_train".
|
| func (callable): a callable which takes no arguments and returns a list of dicts.
|
| It must return the same results if called multiple times.
|
| """
|
| assert callable(func), "You must register a function with `DatasetCatalog.register`!"
|
| assert name not in self, "Dataset '{}' is already registered!".format(name)
|
| self[name] = func
|
|
|
| def get(self, name):
|
| """
|
| Call the registered function and return its results.
|
|
|
| Args:
|
| name (str): the name that identifies a dataset, e.g. "coco_2014_train".
|
|
|
| Returns:
|
| list[dict]: dataset annotations.
|
| """
|
| try:
|
| f = self[name]
|
| except KeyError as e:
|
| raise KeyError(
|
| "Dataset '{}' is not registered! Available datasets are: {}".format(
|
| name, ", ".join(list(self.keys()))
|
| )
|
| ) from e
|
| return f()
|
|
|
| def list(self) -> List[str]:
|
| """
|
| List all registered datasets.
|
|
|
| Returns:
|
| list[str]
|
| """
|
| return list(self.keys())
|
|
|
| def remove(self, name):
|
| """
|
| Alias of ``pop``.
|
| """
|
| self.pop(name)
|
|
|
| def __str__(self):
|
| return "DatasetCatalog(registered datasets: {})".format(", ".join(self.keys()))
|
|
|
| __repr__ = __str__
|
|
|
|
|
| DatasetCatalog = _DatasetCatalog()
|
| DatasetCatalog.__doc__ = (
|
| _DatasetCatalog.__doc__
|
| + """
|
| .. automethod:: detectron2.data.catalog.DatasetCatalog.register
|
| .. automethod:: detectron2.data.catalog.DatasetCatalog.get
|
| """
|
| )
|
|
|
|
|
| class Metadata(types.SimpleNamespace):
|
| """
|
| A class that supports simple attribute setter/getter.
|
| It is intended for storing metadata of a dataset and make it accessible globally.
|
|
|
| Examples:
|
| ::
|
| # somewhere when you load the data:
|
| MetadataCatalog.get("mydataset").thing_classes = ["person", "dog"]
|
|
|
| # somewhere when you print statistics or visualize:
|
| classes = MetadataCatalog.get("mydataset").thing_classes
|
| """
|
|
|
|
|
|
|
| name: str = "N/A"
|
|
|
| _RENAMED = {
|
| "class_names": "thing_classes",
|
| "dataset_id_to_contiguous_id": "thing_dataset_id_to_contiguous_id",
|
| "stuff_class_names": "stuff_classes",
|
| }
|
|
|
| def __getattr__(self, key):
|
| if key in self._RENAMED:
|
| log_first_n(
|
| logging.WARNING,
|
| "Metadata '{}' was renamed to '{}'!".format(key, self._RENAMED[key]),
|
| n=10,
|
| )
|
| return getattr(self, self._RENAMED[key])
|
|
|
|
|
| if len(self.__dict__) > 1:
|
| raise AttributeError(
|
| "Attribute '{}' does not exist in the metadata of dataset '{}'. Available "
|
| "keys are {}.".format(key, self.name, str(self.__dict__.keys()))
|
| )
|
| else:
|
| raise AttributeError(
|
| f"Attribute '{key}' does not exist in the metadata of dataset '{self.name}': "
|
| "metadata is empty."
|
| )
|
|
|
| def __setattr__(self, key, val):
|
| if key in self._RENAMED:
|
| log_first_n(
|
| logging.WARNING,
|
| "Metadata '{}' was renamed to '{}'!".format(key, self._RENAMED[key]),
|
| n=10,
|
| )
|
| setattr(self, self._RENAMED[key], val)
|
|
|
|
|
| try:
|
| oldval = getattr(self, key)
|
| assert oldval == val, (
|
| "Attribute '{}' in the metadata of '{}' cannot be set "
|
| "to a different value!\n{} != {}".format(key, self.name, oldval, val)
|
| )
|
| except AttributeError:
|
| super().__setattr__(key, val)
|
|
|
| def as_dict(self):
|
| """
|
| Returns all the metadata as a dict.
|
| Note that modifications to the returned dict will not reflect on the Metadata object.
|
| """
|
| return copy.copy(self.__dict__)
|
|
|
| def set(self, **kwargs):
|
| """
|
| Set multiple metadata with kwargs.
|
| """
|
| for k, v in kwargs.items():
|
| setattr(self, k, v)
|
| return self
|
|
|
| def get(self, key, default=None):
|
| """
|
| Access an attribute and return its value if exists.
|
| Otherwise return default.
|
| """
|
| try:
|
| return getattr(self, key)
|
| except AttributeError:
|
| return default
|
|
|
|
|
| class _MetadataCatalog(UserDict):
|
| """
|
| MetadataCatalog is a global dictionary that provides access to
|
| :class:`Metadata` of a given dataset.
|
|
|
| The metadata associated with a certain name is a singleton: once created, the
|
| metadata will stay alive and will be returned by future calls to ``get(name)``.
|
|
|
| It's like global variables, so don't abuse it.
|
| It's meant for storing knowledge that's constant and shared across the execution
|
| of the program, e.g.: the class names in COCO.
|
| """
|
|
|
| def get(self, name):
|
| """
|
| Args:
|
| name (str): name of a dataset (e.g. coco_2014_train).
|
|
|
| Returns:
|
| Metadata: The :class:`Metadata` instance associated with this name,
|
| or create an empty one if none is available.
|
| """
|
| assert len(name)
|
| r = super().get(name, None)
|
| if r is None:
|
| r = self[name] = Metadata(name=name)
|
| return r
|
|
|
| def list(self):
|
| """
|
| List all registered metadata.
|
|
|
| Returns:
|
| list[str]: keys (names of datasets) of all registered metadata
|
| """
|
| return list(self.keys())
|
|
|
| def remove(self, name):
|
| """
|
| Alias of ``pop``.
|
| """
|
| self.pop(name)
|
|
|
| def __str__(self):
|
| return "MetadataCatalog(registered metadata: {})".format(", ".join(self.keys()))
|
|
|
| __repr__ = __str__
|
|
|
|
|
| MetadataCatalog = _MetadataCatalog()
|
| MetadataCatalog.__doc__ = (
|
| _MetadataCatalog.__doc__
|
| + """
|
| .. automethod:: detectron2.data.catalog.MetadataCatalog.get
|
| """
|
| )
|
|
|