annotations.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. from os.path import join, isfile
  2. import numpy as np
  3. from collections import defaultdict, OrderedDict
  4. import abc
  5. import warnings
  6. class _MetaInfo(object):
  7. def __init__(self, **kwargs):
  8. for name, value in kwargs.items():
  9. setattr(self, name, value)
  10. self.structure = []
  11. class BaseAnnotations(abc.ABC):
  12. @property
  13. @abc.abstractmethod
  14. def meta(self):
  15. pass
  16. def _path(self, file):
  17. return join(self.root, file)
  18. def _open(self, file):
  19. return open(self._path(file))
  20. def read_content(self, file, attr):
  21. content = None
  22. fpath = self._path(file)
  23. if isfile(fpath):
  24. with self._open(file) as f:
  25. content = [line.strip() for line in f if line.strip()]
  26. else:
  27. warnings.warn("File \"{}\" was not found!".format(fpath))
  28. setattr(self, attr, content)
  29. def __init__(self, root):
  30. super(BaseAnnotations, self).__init__()
  31. self.root = root
  32. for fname, attr in self.meta.structure:
  33. self.read_content(fname, attr)
  34. self.labels = np.array([int(l) for l in self.labels], dtype=np.int32)
  35. self._load_uuids()
  36. self._load_parts()
  37. self._load_split()
  38. def _load_uuids(self):
  39. assert self._images is not None, "Images were not loaded!"
  40. uuid_fnames = [i.split() for i in self._images]
  41. self.uuids, self.images = map(np.array, zip(*uuid_fnames))
  42. self.uuid_to_idx = {uuid: i for i, uuid in enumerate(self.uuids)}
  43. def _load_parts(self):
  44. assert self._part_locs is not None, "Part locations were not loaded!"
  45. # this part is quite slow... TODO: some runtime improvements?
  46. uuid_to_parts = defaultdict(list)
  47. for content in [i.split() for i in self._part_locs]:
  48. uuid_to_parts[content[0]].append([float(i) for i in content[1:]])
  49. self.part_locs = np.stack([uuid_to_parts[uuid] for uuid in self.uuids]).astype(int)
  50. if hasattr(self, "_part_names") and self._part_names is not None:
  51. self._load_part_names()
  52. def _load_part_names(self):
  53. self.part_names = OrderedDict()
  54. self.part_name_list = []
  55. for line in self._part_names:
  56. part_idx, _, name = line.partition(" ")
  57. self.part_names[int(part_idx)] = name
  58. self.part_name_list.append(name)
  59. def _load_split(self):
  60. assert self._split is not None, "Train-test split was not loaded!"
  61. uuid_to_split = {uuid: int(split) for uuid, split in [i.split() for i in self._split]}
  62. self.train_split = np.array([uuid_to_split[uuid] for uuid in self.uuids], dtype=bool)
  63. self.test_split = np.logical_not(self.train_split)
  64. def image_path(self, image):
  65. return join(self.root, self.meta.images_folder, image)
  66. def image(self, uuid):
  67. fname = self.images[self.uuid_to_idx[uuid]]
  68. return self.image_path(fname)
  69. def label(self, uuid):
  70. return self.labels[self.uuid_to_idx[uuid]].copy()
  71. def parts(self, uuid):
  72. return self.part_locs[self.uuid_to_idx[uuid]].copy()
  73. def _uuids(self, split):
  74. return self.uuids[split]
  75. @property
  76. def train_uuids(self):
  77. return self._uuids(self.train_split)
  78. @property
  79. def test_uuids(self):
  80. return self._uuids(self.test_split)
  81. class NAB_Annotations(BaseAnnotations):
  82. @property
  83. def meta(self):
  84. info = _MetaInfo(
  85. images_folder="images",
  86. images_file="images.txt",
  87. labels_file="labels.txt",
  88. hierarchy_file="hierarchy.txt",
  89. split_file="train_test_split.txt",
  90. parts_file=join("parts", "part_locs.txt"),
  91. part_names_file=join("parts", "parts.txt"),
  92. )
  93. info.structure = [
  94. [info.images_file, "_images"],
  95. [info.labels_file, "labels"],
  96. [info.hierarchy_file, "hierarchy"],
  97. [info.split_file, "_split"],
  98. [info.parts_file, "_part_locs"],
  99. [info.part_names_file, "_part_names"],
  100. ]
  101. return info
  102. class CUB_Annotations(BaseAnnotations):
  103. @property
  104. def meta(self):
  105. info = _MetaInfo(
  106. images_folder="images",
  107. images_file="images.txt",
  108. labels_file="labels.txt",
  109. split_file="tr_ID.txt",
  110. bounding_boxes="bounding_boxes.txt",
  111. bounding_box_dtype=np.dtype([(v, np.int32) for v in "xywh"]),
  112. parts_file=join("parts", "part_locs.txt"),
  113. part_names_file=join("parts", "parts.txt"),
  114. )
  115. info.structure = [
  116. [info.images_file, "_images"],
  117. [info.labels_file, "labels"],
  118. [info.split_file, "_split"],
  119. [info.parts_file, "_part_locs"],
  120. [info.part_names_file, "_part_names"],
  121. [info.bounding_boxes, "_bounding_boxes"],
  122. ]
  123. return info
  124. def __init__(self, *args, **kwargs):
  125. super(CUB_Annotations, self).__init__(*args, **kwargs)
  126. # set labels from [1..200] to [0..199]
  127. self.labels -= 1
  128. def _load_split(self):
  129. assert self._split is not None, "Train-test split was not loaded!"
  130. uuid_to_split = {uuid: int(split) for uuid, split in zip(self.uuids, self._split)}
  131. self.train_split = np.array([uuid_to_split[uuid] for uuid in self.uuids], dtype=bool)
  132. self.test_split = np.logical_not(self.train_split)
  133. def _load_parts(self):
  134. super(CUB_Annotations, self)._load_parts()
  135. # set part idxs from 1-idxs to 0-idxs
  136. self.part_locs[..., 0] -= 1
  137. self._load_bounding_boxes()
  138. def _load_bounding_boxes(self):
  139. assert self._bounding_boxes is not None, "Bouding boxes were not loaded!"
  140. uuid_to_bbox = {}
  141. for content in [i.split() for i in self._bounding_boxes]:
  142. uuid, bbox = content[0], content[1:]
  143. uuid_to_bbox[uuid] = [float(i) for i in bbox]
  144. self.bounding_boxes = np.array(
  145. [tuple(uuid_to_bbox[uuid]) for uuid in self.uuids],
  146. dtype=self.meta.bounding_box_dtype)
  147. def bounding_box(self, uuid):
  148. return self.bounding_boxes[self.uuid_to_idx[uuid]].copy()