Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    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)
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
    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
        }
Exemplo n.º 10
0
    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)
Exemplo n.º 11
0
    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])
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    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)
Exemplo n.º 16
0
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}
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
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
Exemplo n.º 19
0
    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
Exemplo n.º 20
0
    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)
Exemplo n.º 21
0
    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)
Exemplo n.º 22
0
    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
Exemplo n.º 23
0
    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,
            })
Exemplo n.º 24
0
    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
Exemplo n.º 25
0
    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)
Exemplo n.º 26
0
    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)))