def test_project_labels_maps_secondary_categories(self): source = Dataset.from_iterable([], categories={ AnnotationType.label: LabelCategories.from_iterable([ 'a', 'b', # no parents ('c', 'a'), ('d', 'b') # have parents ]), AnnotationType.points: PointsCategories.from_iterable([ (0, ['a']), (1, ['b']), (2, ['c']) ]), AnnotationType.mask: MaskCategories.generate(4) }) expected = Dataset.from_iterable([], categories={ AnnotationType.label: LabelCategories.from_iterable([ ('c', 'a'), # must keep parent 'a', 'd' # must drop parent because it was removed ]), AnnotationType.points: PointsCategories.from_iterable([ (0, ['c']), (1, ['a']) ]), AnnotationType.mask: MaskCategories(colormap={ i: v for i, v in { { 2: 0, 0: 1, 3: 2 }.get(k): v for k, v in mask_tools.generate_colormap(4).items() }.items() if i is not None }), }) actual = transforms.ProjectLabels(source, dst_labels=['c', 'a', 'd']) compare_datasets(self, expected, actual)
def test_remap_labels_ignore_missing_labels_in_secondary_categories(self): source_dataset = Dataset.from_iterable([ DatasetItem(id=1, annotations=[ Label(0), ]) ], categories={ AnnotationType.label: LabelCategories.from_iterable(['a', 'b', 'c']), AnnotationType.points: PointsCategories.from_iterable([]), # all missing AnnotationType.mask: MaskCategories.generate(2) # no c color }) target_dataset = Dataset.from_iterable([ DatasetItem(id=1, annotations=[ Label(0), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['d', 'e', 'f']), AnnotationType.points: PointsCategories.from_iterable([]), AnnotationType.mask: MaskCategories.generate(2) }) actual = transforms.RemapLabels(source_dataset, mapping={ 'a': 'd', 'b': 'e', 'c': 'f' }, default='delete') compare_datasets(self, target_dataset, actual)
def test_remap_labels(self): src_dataset = Dataset.from_iterable([ DatasetItem(id=1, annotations=[ # Should be remapped Label(1), Bbox(1, 2, 3, 4, label=2), Mask(image=np.array([1]), label=3), # Should be deleted Polygon([1, 1, 2, 2, 3, 4], label=4), # Should be kept PolyLine([1, 3, 4, 2, 5, 6]), Bbox(4, 3, 2, 1, label=5), ]) ], categories={ AnnotationType.label: LabelCategories.from_iterable( f'label{i}' for i in range(6)), AnnotationType.mask: MaskCategories( colormap=mask_tools.generate_colormap(6)), AnnotationType.points: PointsCategories.from_iterable( [(i, [str(i)]) for i in range(6)]) }) dst_dataset = Dataset.from_iterable([ DatasetItem(id=1, annotations=[ Label(1), Bbox(1, 2, 3, 4, label=0), Mask(image=np.array([1]), label=1), PolyLine([1, 3, 4, 2, 5, 6], label=None), Bbox(4, 3, 2, 1, label=2), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable( ['label0', 'label9', 'label5']), AnnotationType.mask: MaskCategories(colormap={ i: v for i, v in enumerate({ k: v for k, v in mask_tools.generate_colormap(6).items() if k in { 0, 1, 5 } }.values()) }), AnnotationType.points: PointsCategories.from_iterable( [(0, ['0']), (1, ['1']), (2, ['5'])]) }) actual = transforms.RemapLabels(src_dataset, mapping={ 'label1': 'label9', # rename & join with new label9 (from label3) 'label2': 'label0', # rename & join with existing label0 'label3': 'label9', # rename & join with new label9 (from label1) 'label4': '', # delete the label and associated annotations # 'label5' - unchanged }, default='keep') compare_datasets(self, dst_dataset, actual)
def test_can_merge_categories(self): source0 = Dataset.from_iterable([ DatasetItem(1, annotations=[ Label(0), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['a', 'b']), AnnotationType.points: PointsCategories.from_iterable([ (0, ['l0', 'l1']), (1, ['l2', 'l3']), ]), AnnotationType.mask: MaskCategories({ 0: (0, 1, 2), 1: (1, 2, 3), }), }) source1 = Dataset.from_iterable([ DatasetItem(1, annotations=[ Label(0), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['c', 'b']), AnnotationType.points: PointsCategories.from_iterable([ (0, []), (1, ['l2', 'l3']), ]), AnnotationType.mask: MaskCategories({ 0: (0, 2, 4), 1: (1, 2, 3), }), }) expected = Dataset.from_iterable([ DatasetItem(1, annotations=[ Label(0), Label(2), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['a', 'b', 'c']), AnnotationType.points: PointsCategories.from_iterable([ (0, ['l0', 'l1']), (1, ['l2', 'l3']), (2, []), ]), AnnotationType.mask: MaskCategories({ 0: (0, 1, 2), 1: (1, 2, 3), 2: (0, 2, 4), }), }) merger = IntersectMerge() merged = merger([source0, source1]) compare_datasets(self, expected, merged, ignored_attrs={'score'})
def test_can_import_v2_0_panoptic_with_keeping_category_ids(self): labels = [f'class-{i}' for i in range(101)] labels[1] = ('animal--bird', 'animal') labels[10] = ('construction--barrier--separator', 'construction') labels[100] = ('object--vehicle--bicycle', 'object') label_cat = LabelCategories.from_iterable(labels) mask_cat = MaskCategories({ 1: (165, 42, 42), 10: (128, 128, 128), 100: (119, 11, 32) }) expected_dataset = Dataset.from_iterable([ DatasetItem(id='0', subset='val', annotations=[ Mask(image=np.array([[1, 1, 1, 0, 0]] * 5), id=1, group=1, label=1, attributes={'is_crowd': True}), Mask(image=np.array([[0, 0, 0, 1, 1]] * 5), id=2, group=2, label=10, attributes={'is_crowd': False}), Polygon(points=[0, 0, 1, 0, 2, 0, 2, 4, 0, 4], label=1), Polygon(points=[3, 0, 4, 0, 4, 1, 4, 4, 3, 4], label=10), ], image=np.ones((5, 5, 3))), DatasetItem(id='1', subset='val', annotations=[ Mask(image=np.array([[1, 1, 0, 0, 0]] * 5), id=1, group=1, label=100, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 1, 0, 0]] * 5), id=2, group=2, label=10, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 0, 1, 1]] * 5), id=3, group=3, label=100, attributes={'is_crowd': True}), Polygon(points=[2, 0, 2, 1, 2, 2, 2, 3, 2, 4], label=10), Polygon(points=[0, 0, 1, 0, 1, 4, 4, 0, 0, 0], label=100), Polygon(points=[3, 0, 4, 0, 4, 4, 3, 4, 3, 0], label=100), ], image=np.ones((5, 5, 3))), DatasetItem(id='2', subset='train', annotations=[ Mask(image=np.array([[1, 0, 0, 0, 0]] * 5), id=1, group=1, label=1, attributes={'is_crowd': False}), Mask(image=np.array([[0, 1, 0, 0, 0]] * 5), id=2, group=2, label=10, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 1, 0, 0]] * 5), id=3, group=3, label=1, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 0, 1, 0]] * 5), id=4, group=4, label=10, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 0, 0, 1]] * 5), id=5, group=5, label=1, attributes={'is_crowd': False}), Polygon(points=[0, 0, 0, 1, 0, 2, 0, 3, 0, 4], label=1), Polygon(points=[2, 0, 2, 1, 2, 2, 2, 3, 2, 4], label=1), Polygon(points=[4, 0, 4, 1, 4, 2, 4, 3, 4, 4], label=1), Polygon(points=[1, 0, 1, 1, 1, 2, 1, 3, 1, 4], label=10), Polygon(points=[3, 0, 3, 1, 3, 2, 3, 3, 3, 4], label=10), ], image=np.ones((5, 5, 3))), ], categories={ AnnotationType.label: label_cat, AnnotationType.mask: mask_cat }) imported_dataset = Dataset.import_from(DUMMY_DATASET_V2_0, 'mapillary_vistas_panoptic', keep_original_category_ids=True) compare_datasets(self, expected_dataset, imported_dataset, require_images=True)
def test_can_import_with_meta_file(self): label_cat = LabelCategories.from_iterable(['animal--bird', 'construction--barrier--curb', 'human--person']) mask_cat = MaskCategories({ 0: (10, 50, 90), 1: (20, 30, 80), 2: (30, 70, 40) }) expected_dataset = Dataset.from_iterable([ DatasetItem(id='1', subset='train', annotations=[ Mask(image=np.array([[1, 1, 0, 0, 0]] * 5), label=0, id=0), Mask(image=np.array([[0, 0, 0, 0, 1]] * 5), label=0, id=1), Mask(image=np.array([[0, 0, 1, 1, 0]] * 5), label=1, id=0), ], image=np.ones((5, 5, 3))), DatasetItem(id='2', subset='train', annotations=[ Mask(image=np.array([[1, 1, 0, 1, 1]] * 5), label=1), Mask(image=np.array([[0, 0, 1, 0, 0]] * 5), label=2), ], image=np.ones((5, 5, 3))), ], categories={ AnnotationType.label: label_cat, AnnotationType.mask: mask_cat }) imported_dataset = Dataset.import_from(DUMMY_DATASET_WITH_META_FILE, 'mapillary_vistas') compare_datasets(self, expected_dataset, imported_dataset, require_images=True)
def make_voc_categories(label_map=None): if label_map is None: label_map = make_voc_label_map() categories = {} label_categories = LabelCategories() label_categories.attributes.update(['difficult', 'truncated', 'occluded']) for label, desc in label_map.items(): label_categories.add(label, attributes=desc[2]) for part in OrderedDict( (k, None) for k in chain(*(desc[1] for desc in label_map.values()))): label_categories.add(part) categories[AnnotationType.label] = label_categories has_colors = any(v[0] is not None for v in label_map.values()) if not has_colors: # generate new colors colormap = generate_colormap(len(label_map)) else: # only copy defined colors label_id = lambda label: label_categories.find(label)[0] colormap = { label_id(name): desc[0] for name, desc in label_map.items() if desc[0] is not None } mask_categories = MaskCategories(colormap) mask_categories.inverse_colormap # pylint: disable=pointless-statement categories[AnnotationType.mask] = mask_categories return categories
def make_cityscapes_categories(label_map=None): if label_map is None: label_map = CITYSCAPES_LABEL_MAP bg_label = find(label_map.items(), lambda x: x[1] == (0, 0, 0)) if bg_label is None: bg_label = 'background' if bg_label not in label_map: has_colors = any(v is not None for v in label_map.values()) color = (0, 0, 0) if has_colors else None label_map[bg_label] = color label_map.move_to_end(bg_label, last=False) categories = {} label_categories = LabelCategories() for label in label_map: label_categories.add(label) categories[AnnotationType.label] = label_categories has_colors = any(v is not None for v in label_map.values()) if not has_colors: # generate new colors colormap = generate_colormap(len(label_map)) else: # only copy defined colors label_id = lambda label: label_categories.find(label)[0] colormap = { label_id(name): (desc[0], desc[1], desc[2]) for name, desc in label_map.items() } mask_categories = MaskCategories(colormap) mask_categories.inverse_colormap # pylint: disable=pointless-statement categories[AnnotationType.mask] = mask_categories return categories
def _load_panoptic_categories(self, categories_info, keep_original_ids): label_cat = LabelCategories() label_map, color_map = {}, {} if keep_original_ids: for cat in sorted(categories_info, key=lambda cat: cat['id']): label_map[cat['id']] = cat['id'] color_map[cat['id']] = tuple(map(int, cat['color'])) while len(label_cat) < cat['id']: label_cat.add(name=f"class-{len(label_cat)}") label_cat.add(name=cat['name'], parent=cat.get('supercategory')) else: for idx, cat in enumerate(categories_info): label_map[cat['id']] = idx color_map[idx] = tuple(map(int, cat['color'])) label_cat.add(name=cat['name'], parent=cat.get('supercategory')) self._label_map = label_map mask_cat = MaskCategories(color_map) mask_cat.inverse_colormap # pylint: disable=pointless-statement return { AnnotationType.label:label_cat, AnnotationType.mask:mask_cat }
def __init__(self, extractor: IExtractor, mapping: Union[Dict[str, str], List[Tuple[str, str]]], default: Union[None, str, DefaultAction] = None): super().__init__(extractor) default = parse_str_enum_value(default, self.DefaultAction, self.DefaultAction.keep) self._default_action = default assert isinstance(mapping, (dict, list)) if isinstance(mapping, list): mapping = dict(mapping) self._categories = {} src_categories = self._extractor.categories() src_label_cat = src_categories.get(AnnotationType.label) if src_label_cat is not None: self._make_label_id_map(src_label_cat, mapping, default) src_mask_cat = src_categories.get(AnnotationType.mask) if src_mask_cat is not None: assert src_label_cat is not None dst_mask_cat = MaskCategories( attributes=deepcopy(src_mask_cat.attributes)) for old_id, old_color in src_mask_cat.colormap.items(): new_id = self._map_id(old_id) if new_id is not None and new_id not in dst_mask_cat: dst_mask_cat.colormap[new_id] = deepcopy(old_color) self._categories[AnnotationType.mask] = dst_mask_cat src_point_cat = src_categories.get(AnnotationType.points) if src_point_cat is not None: assert src_label_cat is not None dst_point_cat = PointsCategories( attributes=deepcopy(src_point_cat.attributes)) for old_id, old_cat in src_point_cat.items.items(): new_id = self._map_id(old_id) if new_id is not None and new_id not in dst_point_cat: dst_point_cat.items[new_id] = deepcopy(old_cat) self._categories[AnnotationType.points] = dst_point_cat assert len(self._categories) == len(src_categories)
def test_project_labels_generates_colors_for_added_labels(self): source = Dataset.from_iterable([], categories={ AnnotationType.label: LabelCategories.from_iterable(['a', 'b', 'c']), AnnotationType.mask: MaskCategories.generate(2) }) actual = transforms.ProjectLabels(source, dst_labels=['a', 'c', 'd']) self.assertEqual((0, 0, 0), actual.categories()[AnnotationType.mask][0]) self.assertNotIn(1, actual.categories()[AnnotationType.mask]) self.assertIn(2, actual.categories()[AnnotationType.mask])
def test_inplace_save_writes_only_updated_data_with_direct_changes(self): expected = Dataset.from_iterable([ DatasetItem(1, subset='a', image=np.ones((1, 2, 3)), annotations=[ # Bbox(0, 0, 0, 0, label=1) # won't find removed anns ]), DatasetItem(2, subset='b', image=np.ones((3, 2, 3)), annotations=[ Bbox(0, 0, 0, 0, label=4, id=1, group=1, attributes={ 'truncated': False, 'difficult': False, 'occluded': False, }) ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable( ['background', 'a', 'b', 'c', 'd']), AnnotationType.mask: MaskCategories( colormap=VOC.generate_colormap(5)), }) dataset = Dataset.from_iterable([ DatasetItem(1, subset='a', image=np.ones((1, 2, 3)), annotations=[Bbox(0, 0, 0, 0, label=1)]), DatasetItem(2, subset='b', annotations=[Bbox(0, 0, 0, 0, label=2)]), DatasetItem(3, subset='c', image=np.ones((2, 2, 3)), annotations=[ Bbox(0, 0, 0, 0, label=3), Mask(np.ones((2, 2)), label=1) ]), ], categories=['a', 'b', 'c', 'd']) with TestDir() as path: dataset.export(path, 'voc', save_images=True) os.unlink(osp.join(path, 'Annotations', '1.xml')) os.unlink(osp.join(path, 'Annotations', '2.xml')) os.unlink(osp.join(path, 'Annotations', '3.xml')) dataset.put(DatasetItem(2, subset='b', image=np.ones((3, 2, 3)), annotations=[Bbox(0, 0, 0, 0, label=3)])) dataset.remove(3, 'c') dataset.save(save_images=True) self.assertEqual({'2.xml'}, # '1.xml' won't be touched set(os.listdir(osp.join(path, 'Annotations')))) self.assertEqual({'1.jpg', '2.jpg'}, set(os.listdir(osp.join(path, 'JPEGImages')))) self.assertEqual({'a.txt', 'b.txt'}, set(os.listdir(osp.join(path, 'ImageSets', 'Main')))) compare_datasets(self, expected, Dataset.import_from(path, 'voc'), require_images=True)
def test_inplace_save_writes_only_updated_data(self): src_mask_cat = MaskCategories.generate(3, include_background=False) expected = Dataset.from_iterable( [ DatasetItem(1, subset='a', image=np.ones((2, 1, 3)), annotations=[Mask(np.ones((2, 1)), label=2)]), DatasetItem(2, subset='a', image=np.ones((3, 2, 3))), DatasetItem(2, subset='b'), ], categories=Camvid.make_camvid_categories( OrderedDict([ ('background', (0, 0, 0)), ('a', src_mask_cat.colormap[0]), ('b', src_mask_cat.colormap[1]), ]))) with TestDir() as path: dataset = Dataset.from_iterable( [ DatasetItem(1, subset='a', image=np.ones((2, 1, 3)), annotations=[Mask(np.ones((2, 1)), label=1)]), DatasetItem(2, subset='b'), DatasetItem(3, subset='c', image=np.ones((2, 2, 3)), annotations=[Mask(np.ones((2, 2)), label=0)]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['a', 'b']), AnnotationType.mask: src_mask_cat }) dataset.export(path, 'camvid', save_images=True) dataset.put(DatasetItem(2, subset='a', image=np.ones((3, 2, 3)))) dataset.remove(3, 'c') dataset.save(save_images=True) self.assertEqual( {'a', 'aannot', 'a.txt', 'b.txt', 'label_colors.txt'}, set(os.listdir(path))) self.assertEqual({'1.jpg', '2.jpg'}, set(os.listdir(osp.join(path, 'a')))) compare_datasets(self, expected, Dataset.import_from(path, 'camvid'), require_images=True)
def test_can_import_v2_0_instances(self): label_cat = LabelCategories.from_iterable(['animal--bird', 'construction--barrier--separator', 'object--vehicle--bicycle']) mask_cat = MaskCategories({ 0: (165, 42, 42), 1: (128, 128, 128), 2: (119, 11, 32) }) expected_dataset = Dataset.from_iterable([ DatasetItem(id='0', subset='val', annotations=[ Mask(image=np.array([[1, 1, 1, 0, 0]] * 5), id=0, label=0), Mask(image=np.array([[0, 0, 0, 1, 1]] * 5), id=0, label=1), Polygon(points=[0, 0, 1, 0, 2, 0, 2, 4, 0, 4], label=0), Polygon(points=[3, 0, 4, 0, 4, 1, 4, 4, 3, 4], label=1), ], image=np.ones((5, 5, 3))), DatasetItem(id='1', subset='val', annotations=[ Mask(image=np.array([[0, 0, 1, 0, 0]] * 5), id=0, label=1), Mask(image=np.array([[1, 1, 0, 0, 0]] * 5), id=0, label=2), Mask(image=np.array([[0, 0, 0, 1, 1]] * 5), id=1, label=2), Polygon(points=[2, 0, 2, 1, 2, 2, 2, 3, 2, 4], label=1), Polygon(points=[0, 0, 1, 0, 1, 4, 4, 0, 0, 0], label=2), Polygon(points=[3, 0, 4, 0, 4, 4, 3, 4, 3, 0], label=2), ], image=np.ones((5, 5, 3))), DatasetItem(id='2', subset='train', annotations=[ Mask(image=np.array([[1, 0, 0, 0, 0]] * 5), id=0, label=0), Mask(image=np.array([[0, 0, 1, 0, 0]] * 5), id=1, label=0), Mask(image=np.array([[0, 0, 0, 0, 1]] * 5), id=2, label=0), Mask(image=np.array([[0, 1, 0, 0, 0]] * 5), id=0, label=1), Mask(image=np.array([[0, 0, 0, 1, 0]] * 5), id=1, label=1), Polygon(points=[0, 0, 0, 1, 0, 2, 0, 3, 0, 4], label=0), Polygon(points=[2, 0, 2, 1, 2, 2, 2, 3, 2, 4], label=0), Polygon(points=[4, 0, 4, 1, 4, 2, 4, 3, 4, 4], label=0), Polygon(points=[1, 0, 1, 1, 1, 2, 1, 3, 1, 4], label=1), Polygon(points=[3, 0, 3, 1, 3, 2, 3, 3, 3, 4], label=1), ], image=np.ones((5, 5, 3))), ], categories={ AnnotationType.label: label_cat, AnnotationType.mask: mask_cat }) imported_dataset = Dataset.import_from(DUMMY_DATASET_V2_0, 'mapillary_vistas_instances') compare_datasets(self, expected_dataset, imported_dataset, require_images=True)
def test_can_import_v2_0_panoptic_wo_images(self): label_cat = LabelCategories.from_iterable([ ('animal--bird', 'animal'), ('construction--barrier--separator', 'construction'), ('object--vehicle--bicycle', 'object') ]) mask_cat = MaskCategories({ 0: (165, 42, 42), 1: (128, 128, 128), 2: (119, 11, 32) }) expected_dataset = Dataset.from_iterable([ DatasetItem(id='2', subset='dataset', annotations=[ Mask(image=np.array([[1, 0, 0, 0, 0]] * 5), id=1, group=1, label=0, attributes={'is_crowd': False}), Mask(image=np.array([[0, 1, 0, 0, 0]] * 5), id=2, group=2, label=1, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 1, 0, 0]] * 5), id=3, group=3, label=0, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 0, 1, 0]] * 5), id=4, group=4, label=1, attributes={'is_crowd': False}), Mask(image=np.array([[0, 0, 0, 0, 1]] * 5), id=5, group=5, label=0, attributes={'is_crowd': False}), Polygon(points=[0, 0, 0, 1, 0, 2, 0, 3, 0, 4], label=0), Polygon(points=[2, 0, 2, 1, 2, 2, 2, 3, 2, 4], label=0), Polygon(points=[4, 0, 4, 1, 4, 2, 4, 3, 4, 4], label=0), Polygon(points=[1, 0, 1, 1, 1, 2, 1, 3, 1, 4], label=1), Polygon(points=[3, 0, 3, 1, 3, 2, 3, 3, 3, 4], label=1), ]) ], categories={ AnnotationType.label: label_cat, AnnotationType.mask: mask_cat }) with TestDir() as test_dir: dataset_path = osp.join(test_dir, 'dataset') shutil.copytree(osp.join(DUMMY_DATASET_V2_0, 'train'), dataset_path) shutil.rmtree(osp.join(dataset_path, 'images')) imported_dataset = Dataset.import_from(dataset_path, 'mapillary_vistas_panoptic') compare_datasets(self, expected_dataset, imported_dataset, require_images=True)
def make_mapillary_instance_categories(label_map): label_cat = LabelCategories() for label, _ in label_map.items(): label_cat.add(label) has_colors = any([len(color) == 3 for color in label_map.values()]) if not has_colors: colormap = generate_colormap(len(label_map)) else: colormap = { label_cat.find(label)[0]: color for label, color in label_map.items() } mask_cat = MaskCategories(colormap) mask_cat.inverse_colormap # pylint: disable=pointless-statement return {AnnotationType.label: label_cat, AnnotationType.mask: mask_cat}
def test_can_import_with_custom_labelmap(self): expected_dataset = Dataset.from_iterable( [ DatasetItem( id='Stereo_Left/Omni_F/000000', image=np.ones((1, 5, 3)), annotations=[ Mask(np.array([[1, 1, 1, 0, 0]]), label=1), Mask(np.array([[0, 0, 0, 1, 1]]), label=4), ], ), DatasetItem( id='Stereo_Left/Omni_F/000001', image=np.ones((1, 5, 3)), annotations=[ Mask(np.array([[1, 1, 0, 0, 0]]), label=2), Mask(np.array([[0, 0, 1, 1, 0]]), label=3), Mask(np.array([[0, 0, 0, 0, 1]]), label=4), ], ) ], categories={ AnnotationType.label: LabelCategories.from_iterable( ['background', 'sky', 'building', 'person', 'road']), AnnotationType.mask: MaskCategories({ 0: (0, 0, 0), 1: (0, 0, 64), 2: (0, 128, 128), 3: (128, 0, 64), 4: (0, 192, 128) }) }) dataset = Dataset.import_from(DUMMY_DATASET_DIR_CUSTOM_LABELMAP, 'synthia') compare_datasets(self, expected_dataset, dataset, require_images=True)
def make_categories(label_map=None): if label_map is None: label_map = SYNTHIA_LABEL_MAP categories = {} label_categories = LabelCategories() for label in label_map: label_categories.add(label) categories[AnnotationType.label] = label_categories has_colors = any(v is not None for v in label_map.values()) if not has_colors: # generate new colors colormap = generate_colormap(len(label_map)) else: # only copy defined colors colormap = { label_id: (desc[0], desc[1], desc[2]) for label_id, desc in enumerate(label_map.values()) } mask_categories = MaskCategories(colormap) mask_categories.inverse_colormap # pylint: disable=pointless-statement categories[AnnotationType.mask] = mask_categories return categories
def _load_categories(parsed): categories = {} parsed_label_cat = parsed['categories'].get(AnnotationType.label.name) if parsed_label_cat: label_categories = LabelCategories( attributes=parsed_label_cat.get('attributes', [])) for item in parsed_label_cat['labels']: label_categories.add(item['name'], parent=item['parent'], attributes=item.get('attributes', [])) categories[AnnotationType.label] = label_categories parsed_mask_cat = parsed['categories'].get(AnnotationType.mask.name) if parsed_mask_cat: colormap = {} for item in parsed_mask_cat['colormap']: colormap[int(item['label_id'])] = \ (item['r'], item['g'], item['b']) mask_categories = MaskCategories(colormap=colormap) categories[AnnotationType.mask] = mask_categories parsed_points_cat = parsed['categories'].get( AnnotationType.points.name) if parsed_points_cat: point_categories = PointsCategories() for item in parsed_points_cat['items']: point_categories.add(int(item['label_id']), item['labels'], joints=item['joints']) categories[AnnotationType.points] = point_categories return categories
def test_inplace_save_writes_only_updated_data(self): src_mask_cat = MaskCategories.generate(2, include_background=False) expected = Dataset.from_iterable([ DatasetItem(1, subset='a', image=np.ones((2, 1, 3)), annotations=[ Mask(np.ones((2, 1)), label=2, id=1) ]), DatasetItem(2, subset='a', image=np.ones((3, 2, 3))), DatasetItem(2, subset='b', image=np.ones((2, 2, 3)), annotations=[ Mask(np.ones((2, 2)), label=1, id=1) ]), ], categories=Cityscapes.make_cityscapes_categories(OrderedDict([ ('a', src_mask_cat.colormap[0]), ('b', src_mask_cat.colormap[1]), ]))) with TestDir() as path: dataset = Dataset.from_iterable([ DatasetItem(1, subset='a', image=np.ones((2, 1, 3)), annotations=[ Mask(np.ones((2, 1)), label=1) ]), DatasetItem(2, subset='b', image=np.ones((2, 2, 3)), annotations=[ Mask(np.ones((2, 2)), label=0) ]), DatasetItem(3, subset='c', image=np.ones((2, 3, 3)), annotations=[ Mask(np.ones((2, 2)), label=0) ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['a', 'b']), AnnotationType.mask: src_mask_cat }) dataset.export(path, 'cityscapes', save_images=True) dataset.put(DatasetItem(2, subset='a', image=np.ones((3, 2, 3)))) dataset.remove(3, 'c') dataset.save(save_images=True) self.assertEqual({'a', 'b'}, set(os.listdir(osp.join(path, 'gtFine')))) self.assertEqual({ '1_gtFine_color.png', '1_gtFine_instanceIds.png', '1_gtFine_labelIds.png' }, set(os.listdir(osp.join(path, 'gtFine', 'a')))) self.assertEqual({ '2_gtFine_color.png', '2_gtFine_instanceIds.png', '2_gtFine_labelIds.png' }, set(os.listdir(osp.join(path, 'gtFine', 'b')))) self.assertEqual({'a', 'b'}, set(os.listdir(osp.join(path, 'imgsFine', 'leftImg8bit')))) self.assertEqual({'1_leftImg8bit.png', '2_leftImg8bit.png'}, set(os.listdir(osp.join(path, 'imgsFine', 'leftImg8bit', 'a')))) self.assertEqual({'2_leftImg8bit.png'}, set(os.listdir(osp.join(path, 'imgsFine', 'leftImg8bit', 'b')))) compare_datasets(self, expected, Dataset.import_from(path, 'cityscapes'), require_images=True, ignored_attrs=IGNORE_ALL)
def test_can_run_self_merge(self): dataset1 = Dataset.from_iterable([ DatasetItem(id=100, subset='train', image=np.ones((10, 6, 3)), annotations=[ Bbox(1, 2, 3, 3, label=0), ]), ], categories=['a', 'b']) dataset2 = Dataset.from_iterable([ DatasetItem(id=100, subset='train', image=np.ones((10, 6, 3)), annotations=[ Bbox(1, 2, 3, 4, label=1), Bbox(5, 6, 2, 3, label=2), ]), ], categories=['a', 'b', 'c']) expected = Dataset.from_iterable( [ DatasetItem(id=100, subset='train', image=np.ones((10, 6, 3)), annotations=[ Bbox(1, 2, 3, 4, label=2, id=1, group=1, attributes={ 'score': 0.5, 'occluded': False, 'difficult': False, 'truncated': False }), Bbox(5, 6, 2, 3, label=3, id=2, group=2, attributes={ 'score': 0.5, 'occluded': False, 'difficult': False, 'truncated': False }), Bbox(1, 2, 3, 3, label=1, id=1, group=1, attributes={ 'score': 0.5, 'is_crowd': False }), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable(['background', 'a', 'b', 'c']), AnnotationType.mask: MaskCategories(VOC.generate_colormap(4)) }) with TestDir() as test_dir: dataset1_url = osp.join(test_dir, 'dataset1') dataset2_url = osp.join(test_dir, 'dataset2') dataset1.export(dataset1_url, 'coco', save_images=True) dataset2.export(dataset2_url, 'voc', save_images=True) proj_dir = osp.join(test_dir, 'proj') with Project.init(proj_dir) as project: project.import_source('source', dataset2_url, 'voc') result_dir = osp.join(test_dir, 'result') run(self, 'merge', '-o', result_dir, '-p', proj_dir, dataset1_url + ':coco') compare_datasets(self, expected, Dataset.load(result_dir), require_images=True)
def _load_segmentation_items(self): items = {} image_dir = osp.join(self._path, IcdarPath.IMAGES_DIR) if osp.isdir(image_dir): images = { osp.splitext(osp.relpath(p, image_dir))[0].replace('\\', '/'): p for p in find_images(image_dir, recursive=True) } else: images = {} for path in glob.iglob( osp.join(self._path, '**', '*.txt'), recursive=True): item_id = osp.splitext(osp.relpath(path, self._path))[0] item_id = item_id.replace('\\', '/') if item_id.endswith('_GT'): item_id = item_id[:-3] if item_id not in items: items[item_id] = DatasetItem(item_id, subset=self._subset, image=images.get(item_id)) annotations = items[item_id].annotations colors = [(255, 255, 255)] chars = [''] centers = [0] groups = [0] group = 1 number_in_group = 0 with open(path, encoding='utf-8') as f: for line in f: line = line.strip() if line == '': if number_in_group == 1: groups[len(groups) - 1] = 0 else: group += 1 number_in_group = 0 continue objects = line.split() if objects[0][0] == '#': objects[0] = objects[0][1:] objects[9] = '\" \"' objects.pop() if len(objects) != 10: raise Exception("Line %s contains the wrong number " "of arguments, e.g. '241 73 144 1 4 0 3 1 4 \"h\"" % line) centers.append(objects[3] + ' ' + objects[4]) groups.append(group) colors.append(tuple(int(o) for o in objects[:3])) char = objects[9] if char[0] == '\"' and char[-1] == '\"': char = char[1:-1] chars.append(char) number_in_group += 1 if number_in_group == 1: groups[len(groups) - 1] = 0 mask_categories = MaskCategories( {i: colors[i] for i in range(len(colors))}) inverse_cls_colormap = mask_categories.inverse_colormap gt_path = osp.join(self._path, item_id + '_GT' + IcdarPath.GT_EXT) if osp.isfile(gt_path): # load mask through cache mask = lazy_mask(gt_path, inverse_cls_colormap) mask = mask() classes = np.unique(mask) for label_id in classes: if label_id == 0: continue i = int(label_id) annotations.append(Mask(group=groups[i], image=self._lazy_extract_mask(mask, label_id), attributes={ 'index': i - 1, 'color': ' '.join(str(p) for p in colors[i]), 'text': chars[i], 'center': centers[i] } )) return items
def test_dataset(self): label_categories = LabelCategories(attributes={'a', 'b', 'score'}) for i in range(5): label_categories.add('cat' + str(i), attributes={'x', 'y'}) mask_categories = MaskCategories( generate_colormap(len(label_categories.items))) points_categories = PointsCategories() for index, _ in enumerate(label_categories.items): points_categories.add(index, ['cat1', 'cat2'], joints=[[0, 1]]) return Dataset.from_iterable( [ DatasetItem(id=100, subset='train', image=np.ones((10, 6, 3)), annotations=[ Caption('hello', id=1), Caption('world', id=2, group=5), Label(2, id=3, attributes={ 'x': 1, 'y': '2', }), Bbox(1, 2, 3, 4, label=4, id=4, z_order=1, attributes={ 'score': 1.0, }), Bbox(5, 6, 7, 8, id=5, group=5, attributes={ 'a': 1.5, 'b': 'text', }), Points([1, 2, 2, 0, 1, 1], label=0, id=5, z_order=4, attributes={ 'x': 1, 'y': '2', }), Mask(label=3, id=5, z_order=2, image=np.ones((2, 3)), attributes={ 'x': 1, 'y': '2', }), ]), DatasetItem(id=21, subset='train', annotations=[ Caption('test'), Label(2), Bbox(1, 2, 3, 4, label=5, id=42, group=42) ]), DatasetItem( id=2, subset='val', annotations=[ PolyLine([1, 2, 3, 4, 5, 6, 7, 8], id=11, z_order=1), Polygon([1, 2, 3, 4, 5, 6, 7, 8], id=12, z_order=4), ]), DatasetItem(id=1, subset='test', annotations=[ Cuboid3d([1.0, 2.0, 3.0], [2.0, 2.0, 4.0], [1.0, 3.0, 4.0], id=6, label=0, attributes={'occluded': True}, group=6) ]), DatasetItem( id=42, subset='test', attributes={ 'a1': 5, 'a2': '42' }), DatasetItem(id=42), DatasetItem(id=43, image=Image(path='1/b/c.qq', size=(2, 4))), ], categories={ AnnotationType.label: label_categories, AnnotationType.mask: mask_categories, AnnotationType.points: points_categories, })
def __init__(self, extractor: IExtractor, dst_labels: Union[Iterable[str], LabelCategories]): super().__init__(extractor) self._categories = {} src_categories = self._extractor.categories() src_label_cat = src_categories.get(AnnotationType.label) if isinstance(dst_labels, LabelCategories): dst_label_cat = deepcopy(dst_labels) else: dst_labels = list(dst_labels) if src_label_cat: dst_label_cat = LabelCategories( attributes=deepcopy(src_label_cat.attributes)) for dst_label in dst_labels: assert isinstance(dst_label, str) src_label = src_label_cat.find(dst_label)[1] if src_label is not None: dst_label_cat.add(dst_label, src_label.parent, deepcopy(src_label.attributes)) else: dst_label_cat.add(dst_label) else: dst_label_cat = LabelCategories.from_iterable(dst_labels) for label in dst_label_cat: if label.parent not in dst_label_cat: label.parent = '' self._categories[AnnotationType.label] = dst_label_cat self._make_label_id_map(src_label_cat, dst_label_cat) src_mask_cat = src_categories.get(AnnotationType.mask) if src_mask_cat is not None: assert src_label_cat is not None dst_mask_cat = MaskCategories( attributes=deepcopy(src_mask_cat.attributes)) for old_id, old_color in src_mask_cat.colormap.items(): new_id = self._map_id(old_id) if new_id is not None and new_id not in dst_mask_cat: dst_mask_cat.colormap[new_id] = deepcopy(old_color) # Generate new colors for new labels, keep old untouched existing_colors = set(dst_mask_cat.colormap.values()) color_bank = iter( mask_tools.generate_colormap( len(dst_label_cat), include_background=False).values()) for new_id, new_label in enumerate(dst_label_cat): if new_label.name in src_label_cat: continue if new_id in dst_mask_cat: continue color = next(color_bank) while color in existing_colors: color = next(color_bank) dst_mask_cat.colormap[new_id] = color self._categories[AnnotationType.mask] = dst_mask_cat src_point_cat = src_categories.get(AnnotationType.points) if src_point_cat is not None: assert src_label_cat is not None dst_point_cat = PointsCategories( attributes=deepcopy(src_point_cat.attributes)) for old_id, old_cat in src_point_cat.items.items(): new_id = self._map_id(old_id) if new_id is not None and new_id not in dst_point_cat: dst_point_cat.items[new_id] = deepcopy(old_cat) self._categories[AnnotationType.points] = dst_point_cat
def test_inplace_save_writes_only_updated_data_with_transforms(self): expected = Dataset.from_iterable([ DatasetItem(3, subset='test', image=np.ones((2, 3, 3)), annotations=[ Bbox(0, 1, 0, 0, label=4, id=1, group=1, attributes={ 'truncated': False, 'difficult': False, 'occluded': False, }) ]), DatasetItem(4, subset='train', image=np.ones((2, 4, 3)), annotations=[ Bbox(1, 0, 0, 0, label=4, id=1, group=1, attributes={ 'truncated': False, 'difficult': False, 'occluded': False, }), Mask(np.ones((2, 2)), label=2, group=1), ]), ], categories={ AnnotationType.label: LabelCategories.from_iterable( ['background', 'a', 'b', 'c', 'd']), AnnotationType.mask: MaskCategories( colormap=VOC.generate_colormap(5)), }) dataset = Dataset.from_iterable([ DatasetItem(1, subset='a', image=np.ones((2, 1, 3)), annotations=[ Bbox(0, 0, 0, 1, label=1) ]), DatasetItem(2, subset='b', image=np.ones((2, 2, 3)), annotations=[ Bbox(0, 0, 1, 0, label=2), Mask(np.ones((2, 2)), label=1), ]), DatasetItem(3, subset='b', image=np.ones((2, 3, 3)), annotations=[ Bbox(0, 1, 0, 0, label=3) ]), DatasetItem(4, subset='c', image=np.ones((2, 4, 3)), annotations=[ Bbox(1, 0, 0, 0, label=3), Mask(np.ones((2, 2)), label=1) ]), ], categories=['a', 'b', 'c', 'd']) with TestDir() as path: dataset.export(path, 'voc', save_images=True) dataset.filter('/item[id >= 3]') dataset.transform('random_split', splits=(('train', 0.5), ('test', 0.5)), seed=42) dataset.save(save_images=True) self.assertEqual({'3.xml', '4.xml'}, set(os.listdir(osp.join(path, 'Annotations')))) self.assertEqual({'3.jpg', '4.jpg'}, set(os.listdir(osp.join(path, 'JPEGImages')))) self.assertEqual({'4.png'}, set(os.listdir(osp.join(path, 'SegmentationClass')))) self.assertEqual({'4.png'}, set(os.listdir(osp.join(path, 'SegmentationObject')))) self.assertEqual({'train.txt', 'test.txt'}, set(os.listdir(osp.join(path, 'ImageSets', 'Main')))) self.assertEqual({'train.txt'}, set(os.listdir(osp.join(path, 'ImageSets', 'Segmentation')))) compare_datasets(self, expected, Dataset.import_from(path, 'voc'), require_images=True)
def test_can_compare_projects(self): # just a smoke test label_categories1 = LabelCategories.from_iterable(['x', 'a', 'b', 'y']) mask_categories1 = MaskCategories.generate(len(label_categories1)) point_categories1 = PointsCategories() for index, _ in enumerate(label_categories1.items): point_categories1.add(index, ['cat1', 'cat2'], joints=[[0, 1]]) dataset1 = Dataset.from_iterable([ DatasetItem(id=100, subset='train', image=np.ones((10, 6, 3)), annotations=[ Caption('hello', id=1), Caption('world', id=2, group=5), Label(2, id=3, attributes={ 'x': 1, 'y': '2', }), Bbox(1, 2, 3, 4, label=0, id=4, z_order=1, attributes={ 'score': 1.0, }), Bbox(5, 6, 7, 8, id=5, group=5), Points([1, 2, 2, 0, 1, 1], label=0, id=5, z_order=4), Mask(label=3, id=5, z_order=2, image=np.ones((2, 3))), ]), DatasetItem(id=21, subset='train', annotations=[ Caption('test'), Label(2), Bbox(1, 2, 3, 4, label=2, id=42, group=42) ]), DatasetItem(id=2, subset='val', annotations=[ PolyLine([1, 2, 3, 4, 5, 6, 7, 8], id=11, z_order=1), Polygon([1, 2, 3, 4, 5, 6, 7, 8], id=12, z_order=4), ]), DatasetItem(id=42, subset='test', attributes={'a1': 5, 'a2': '42'}), DatasetItem(id=42), DatasetItem(id=43, image=Image(path='1/b/c.qq', size=(2, 4))), ], categories={ AnnotationType.label: label_categories1, AnnotationType.mask: mask_categories1, AnnotationType.points: point_categories1, }) label_categories2 = LabelCategories.from_iterable(['a', 'b', 'x', 'y']) mask_categories2 = MaskCategories.generate(len(label_categories2)) point_categories2 = PointsCategories() for index, _ in enumerate(label_categories2.items): point_categories2.add(index, ['cat1', 'cat2'], joints=[[0, 1]]) dataset2 = Dataset.from_iterable([ DatasetItem(id=100, subset='train', image=np.ones((10, 6, 3)), annotations=[ Caption('hello', id=1), Caption('world', id=2, group=5), Label(2, id=3, attributes={ 'x': 1, 'y': '2', }), Bbox(1, 2, 3, 4, label=1, id=4, z_order=1, attributes={ 'score': 1.0, }), Bbox(5, 6, 7, 8, id=5, group=5), Points([1, 2, 2, 0, 1, 1], label=0, id=5, z_order=4), Mask(label=3, id=5, z_order=2, image=np.ones((2, 3))), ]), DatasetItem(id=21, subset='train', annotations=[ Caption('test'), Label(2), Bbox(1, 2, 3, 4, label=3, id=42, group=42) ]), DatasetItem(id=2, subset='val', annotations=[ PolyLine([1, 2, 3, 4, 5, 6, 7, 8], id=11, z_order=1), Polygon([1, 2, 3, 4, 5, 6, 7, 8], id=12, z_order=4), ]), DatasetItem(id=42, subset='test', attributes={'a1': 5, 'a2': '42'}), DatasetItem(id=42), DatasetItem(id=43, image=Image(path='1/b/c.qq', size=(2, 4))), ], categories={ AnnotationType.label: label_categories2, AnnotationType.mask: mask_categories2, AnnotationType.points: point_categories2, }) with TestDir() as test_dir: with DiffVisualizer(save_dir=test_dir, comparator=DistanceComparator(iou_threshold=0.8), ) as visualizer: visualizer.save(dataset1, dataset2) self.assertNotEqual(0, os.listdir(osp.join(test_dir)))