|
@@ -1,49 +1,35 @@
|
|
-import numpy as np
|
|
|
|
import abc
|
|
import abc
|
|
-import warnings
|
|
|
|
import logging
|
|
import logging
|
|
-from os.path import join, isfile, isdir
|
|
|
|
-from collections import defaultdict, OrderedDict
|
|
|
|
-
|
|
|
|
-from cvdatasets.utils import read_info_file, feature_file_name
|
|
|
|
-from cvdatasets.dataset import Dataset
|
|
|
|
-
|
|
|
|
-# def _parse_index(idx, offset):
|
|
|
|
-# if idx.isdigit():
|
|
|
|
-# idx = str(int(idx) - offset)
|
|
|
|
-# return idx
|
|
|
|
-
|
|
|
|
-class BBoxMixin(abc.ABC):
|
|
|
|
- def _load_bounding_boxes(self):
|
|
|
|
- assert self._bounding_boxes is not None, "Bouding boxes were not loaded!"
|
|
|
|
-
|
|
|
|
- uuid_to_bbox = {}
|
|
|
|
- for content in [i.split() for i in self._bounding_boxes]:
|
|
|
|
- uuid, bbox = content[0], content[1:]
|
|
|
|
- uuid_to_bbox[uuid] = [float(i) for i in bbox]
|
|
|
|
-
|
|
|
|
- self.bounding_boxes = np.array(
|
|
|
|
- [tuple(uuid_to_bbox[uuid]) for uuid in self.uuids],
|
|
|
|
- dtype=self.meta.bounding_box_dtype)
|
|
|
|
|
|
+import numpy as np
|
|
|
|
|
|
- def bounding_box(self, uuid):
|
|
|
|
- return self.bounding_boxes[self.uuid_to_idx[uuid]].copy()
|
|
|
|
|
|
+from collections import OrderedDict
|
|
|
|
+from collections import defaultdict
|
|
|
|
+from os.path import isdir
|
|
|
|
+from os.path import isfile
|
|
|
|
+from os.path import join
|
|
|
|
|
|
|
|
+from cvdatasets.dataset import Dataset
|
|
|
|
+from cvdatasets.utils import feature_file_name
|
|
|
|
+from cvdatasets.utils import read_info_file
|
|
|
|
+from cvdatasets.utils import pretty_print_dict
|
|
|
|
+from cvdatasets.utils.decorators import only_with_info
|
|
|
|
|
|
class BaseAnnotations(abc.ABC):
|
|
class BaseAnnotations(abc.ABC):
|
|
|
|
|
|
FEATURE_PHONY = dict(train=["train"], test=["test", "val"])
|
|
FEATURE_PHONY = dict(train=["train"], test=["test", "val"])
|
|
|
|
|
|
- def __init__(self, root_or_infofile, parts=None, feature_model=None):
|
|
|
|
- super(BaseAnnotations, self).__init__()
|
|
|
|
- self.part_type = parts
|
|
|
|
|
|
+ def __init__(self, *, root_or_infofile, feature_model=None, load_strict=True, **kwargs):
|
|
|
|
+ super(BaseAnnotations, self).__init__(**kwargs)
|
|
self.feature_model = feature_model
|
|
self.feature_model = feature_model
|
|
|
|
+ self.load_strict = load_strict
|
|
|
|
|
|
if isdir(root_or_infofile):
|
|
if isdir(root_or_infofile):
|
|
self.info = None
|
|
self.info = None
|
|
self.root = root_or_infofile
|
|
self.root = root_or_infofile
|
|
|
|
+
|
|
elif isfile(root_or_infofile):
|
|
elif isfile(root_or_infofile):
|
|
- self.root = self.root_from_infofile(root_or_infofile, parts)
|
|
|
|
|
|
+ self.root = self.root_from_infofile(root_or_infofile)
|
|
|
|
+
|
|
else:
|
|
else:
|
|
raise ValueError("Root folder or info file does not exist: \"{}\"".format(
|
|
raise ValueError("Root folder or info file does not exist: \"{}\"".format(
|
|
root_or_infofile
|
|
root_or_infofile
|
|
@@ -52,25 +38,20 @@ class BaseAnnotations(abc.ABC):
|
|
for fname, attr in self.meta.structure:
|
|
for fname, attr in self.meta.structure:
|
|
self.read_content(fname, attr)
|
|
self.read_content(fname, attr)
|
|
|
|
|
|
- self._load_uuids()
|
|
|
|
- self._load_labels()
|
|
|
|
- self._load_parts()
|
|
|
|
- self._load_split()
|
|
|
|
|
|
+ self.load()
|
|
|
|
+
|
|
|
|
|
|
@property
|
|
@property
|
|
|
|
+ @only_with_info
|
|
def data_root(self):
|
|
def data_root(self):
|
|
- if self.info is None: return None
|
|
|
|
return join(self.info.BASE_DIR, self.info.DATA_DIR)
|
|
return join(self.info.BASE_DIR, self.info.DATA_DIR)
|
|
|
|
|
|
@property
|
|
@property
|
|
|
|
+ @only_with_info
|
|
def dataset_info(self):
|
|
def dataset_info(self):
|
|
- if self.info is None: return None
|
|
|
|
- if self.part_type is None:
|
|
|
|
- return self.info.DATASETS[self.__class__.name]
|
|
|
|
- else:
|
|
|
|
- return self.info.PARTS[self.part_type]
|
|
|
|
|
|
+ return self.info.DATASETS[self.__class__.name]
|
|
|
|
|
|
- def root_from_infofile(self, info_file, parts=None):
|
|
|
|
|
|
+ def root_from_infofile(self, info_file):
|
|
self.info = read_info_file(info_file)
|
|
self.info = read_info_file(info_file)
|
|
|
|
|
|
dataset_info = self.dataset_info
|
|
dataset_info = self.dataset_info
|
|
@@ -85,26 +66,24 @@ class BaseAnnotations(abc.ABC):
|
|
else:
|
|
else:
|
|
uuids = self.uuids
|
|
uuids = self.uuids
|
|
|
|
|
|
- kwargs = self.check_parts_and_features(subset, **kwargs)
|
|
|
|
|
|
+ kwargs = self.check_dataset_kwargs(subset, **kwargs)
|
|
return dataset_cls(uuids=uuids, annotations=self, **kwargs)
|
|
return dataset_cls(uuids=uuids, annotations=self, **kwargs)
|
|
|
|
|
|
- def check_parts_and_features(self, subset, **kwargs):
|
|
|
|
|
|
+ def check_dataset_kwargs(self, subset, **kwargs):
|
|
dataset_info = self.dataset_info
|
|
dataset_info = self.dataset_info
|
|
if dataset_info is None:
|
|
if dataset_info is None:
|
|
return kwargs
|
|
return kwargs
|
|
- logging.debug("Dataset info: {}".format(dataset_info))
|
|
|
|
|
|
+
|
|
|
|
+ logging.debug("Dataset info: {}".format(pretty_print_dict(dataset_info)))
|
|
|
|
|
|
# TODO: pass all scales
|
|
# TODO: pass all scales
|
|
- new_opts = {}
|
|
|
|
|
|
+ new_kwargs = {}
|
|
|
|
|
|
if "scales" in dataset_info:
|
|
if "scales" in dataset_info:
|
|
- new_opts["ratio"] = dataset_info.scales[0]
|
|
|
|
|
|
+ new_kwargs["ratio"] = dataset_info.scales[0]
|
|
|
|
|
|
if "is_uniform" in dataset_info:
|
|
if "is_uniform" in dataset_info:
|
|
- new_opts["uniform_parts"] = dataset_info.is_uniform
|
|
|
|
-
|
|
|
|
- if self.part_type is not None:
|
|
|
|
- new_opts["part_rescale_size"] = dataset_info.rescale_size
|
|
|
|
|
|
+ new_kwargs["uniform_parts"] = dataset_info.is_uniform
|
|
|
|
|
|
if None not in [subset, self.feature_model]:
|
|
if None not in [subset, self.feature_model]:
|
|
tried = []
|
|
tried = []
|
|
@@ -120,15 +99,11 @@ class BaseAnnotations(abc.ABC):
|
|
join(self.root, "features"), subset, tried))
|
|
join(self.root, "features"), subset, tried))
|
|
|
|
|
|
logging.info("Using features file from \"{}\"".format(feature_path))
|
|
logging.info("Using features file from \"{}\"".format(feature_path))
|
|
- new_opts["features"] = feature_path
|
|
|
|
- new_opts.update(kwargs)
|
|
|
|
|
|
+ new_kwargs["features"] = feature_path
|
|
|
|
+ new_kwargs.update(kwargs)
|
|
|
|
|
|
- logging.debug("Final kwargs: {}".format(new_opts))
|
|
|
|
- return new_opts
|
|
|
|
-
|
|
|
|
- @property
|
|
|
|
- def has_parts(self):
|
|
|
|
- return hasattr(self, "_part_locs") and self._part_locs is not None
|
|
|
|
|
|
+ logging.debug("Final kwargs: {}".format(pretty_print_dict(new_kwargs)))
|
|
|
|
+ return new_kwargs
|
|
|
|
|
|
@property
|
|
@property
|
|
@abc.abstractmethod
|
|
@abc.abstractmethod
|
|
@@ -148,11 +123,21 @@ class BaseAnnotations(abc.ABC):
|
|
with self._open(file) as f:
|
|
with self._open(file) as f:
|
|
content = [line.strip() for line in f if line.strip()]
|
|
content = [line.strip() for line in f if line.strip()]
|
|
else:
|
|
else:
|
|
- warnings.warn("File \"{}\" was not found!".format(fpath))
|
|
|
|
|
|
+ msg = "File \"{}\" was not found!".format(fpath)
|
|
|
|
+ if self.load_strict:
|
|
|
|
+ raise AssertionError(msg)
|
|
|
|
+ else:
|
|
|
|
+ logging.warning(msg)
|
|
|
|
|
|
setattr(self, attr, content)
|
|
setattr(self, attr, content)
|
|
|
|
|
|
|
|
|
|
|
|
+ def load(self):
|
|
|
|
+ logging.debug("Loading uuids, labels and training-test split")
|
|
|
|
+ self._load_uuids()
|
|
|
|
+ self._load_labels()
|
|
|
|
+ self._load_split()
|
|
|
|
+
|
|
def _load_labels(self):
|
|
def _load_labels(self):
|
|
self.labels = np.array([int(l) for l in self.labels], dtype=np.int32)
|
|
self.labels = np.array([int(l) for l in self.labels], dtype=np.int32)
|
|
|
|
|
|
@@ -162,31 +147,6 @@ class BaseAnnotations(abc.ABC):
|
|
self.uuids, self.images = map(np.array, zip(*uuid_fnames))
|
|
self.uuids, self.images = map(np.array, zip(*uuid_fnames))
|
|
self.uuid_to_idx = {uuid: i for i, uuid in enumerate(self.uuids)}
|
|
self.uuid_to_idx = {uuid: i for i, uuid in enumerate(self.uuids)}
|
|
|
|
|
|
- def _load_parts(self):
|
|
|
|
- assert self.has_parts, "Part locations were not loaded!"
|
|
|
|
- # this part is quite slow... TODO: some runtime improvements?
|
|
|
|
- uuid_to_parts = defaultdict(list)
|
|
|
|
- for content in [i.split() for i in self._part_locs]:
|
|
|
|
- uuid = content[0]
|
|
|
|
- # assert uuid in self.uuids, \
|
|
|
|
- # "Could not find UUID \"\" from part annotations in image annotations!".format(uuid)
|
|
|
|
- uuid_to_parts[uuid].append([float(c) for c in content[1:]])
|
|
|
|
-
|
|
|
|
- uuid_to_parts = dict(uuid_to_parts)
|
|
|
|
- self.part_locs = np.stack([
|
|
|
|
- uuid_to_parts[uuid] for uuid in self.uuids]).astype(int)
|
|
|
|
-
|
|
|
|
- if hasattr(self, "_part_names") and self._part_names is not None:
|
|
|
|
- self._load_part_names()
|
|
|
|
-
|
|
|
|
- def _load_part_names(self):
|
|
|
|
- self.part_names = OrderedDict()
|
|
|
|
- self.part_name_list = []
|
|
|
|
- for line in self._part_names:
|
|
|
|
- part_idx, _, name = line.partition(" ")
|
|
|
|
- self.part_names[int(part_idx)] = name
|
|
|
|
- self.part_name_list.append(name)
|
|
|
|
-
|
|
|
|
def _load_split(self):
|
|
def _load_split(self):
|
|
assert self._split is not None, "Train-test split was not loaded!"
|
|
assert self._split is not None, "Train-test split was not loaded!"
|
|
uuid_to_split = {uuid: int(split) for uuid, split in zip(self.uuids, self._split)}
|
|
uuid_to_split = {uuid: int(split) for uuid, split in zip(self.uuids, self._split)}
|
|
@@ -203,10 +163,6 @@ class BaseAnnotations(abc.ABC):
|
|
def label(self, uuid):
|
|
def label(self, uuid):
|
|
return self.labels[self.uuid_to_idx[uuid]].copy()
|
|
return self.labels[self.uuid_to_idx[uuid]].copy()
|
|
|
|
|
|
- def parts(self, uuid):
|
|
|
|
- return self.part_locs[self.uuid_to_idx[uuid]].copy()
|
|
|
|
-
|
|
|
|
-
|
|
|
|
def _uuids(self, split):
|
|
def _uuids(self, split):
|
|
return self.uuids[split]
|
|
return self.uuids[split]
|
|
|
|
|
|
@@ -218,3 +174,5 @@ class BaseAnnotations(abc.ABC):
|
|
def test_uuids(self):
|
|
def test_uuids(self):
|
|
return self._uuids(self.test_split)
|
|
return self._uuids(self.test_split)
|
|
|
|
|
|
|
|
+from .bbox_mixin import BBoxMixin
|
|
|
|
+from .parts_mixin import PartsMixin
|