|
|
|
|
@ -1,6 +1,4 @@
|
|
|
|
|
import json
|
|
|
|
|
import numpy as np
|
|
|
|
|
import os
|
|
|
|
|
import os.path as osp
|
|
|
|
|
|
|
|
|
|
from unittest import TestCase
|
|
|
|
|
@ -19,94 +17,13 @@ from datumaro.plugins.coco_format.converter import (
|
|
|
|
|
CocoLabelsConverter,
|
|
|
|
|
)
|
|
|
|
|
from datumaro.plugins.coco_format.importer import CocoImporter
|
|
|
|
|
from datumaro.util.image import save_image, Image
|
|
|
|
|
from datumaro.util.image import Image
|
|
|
|
|
from datumaro.util.test_utils import TestDir, compare_datasets
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CocoImporterTest(TestCase):
|
|
|
|
|
@staticmethod
|
|
|
|
|
def generate_annotation():
|
|
|
|
|
annotation = {
|
|
|
|
|
'licenses': [],
|
|
|
|
|
'info': {},
|
|
|
|
|
'categories': [],
|
|
|
|
|
'images': [],
|
|
|
|
|
'annotations': [],
|
|
|
|
|
}
|
|
|
|
|
annotation['licenses'].append({
|
|
|
|
|
'name': '',
|
|
|
|
|
'id': 0,
|
|
|
|
|
'url': '',
|
|
|
|
|
})
|
|
|
|
|
annotation['info'] = {
|
|
|
|
|
'contributor': '',
|
|
|
|
|
'date_created': '',
|
|
|
|
|
'description': '',
|
|
|
|
|
'url': '',
|
|
|
|
|
'version': '',
|
|
|
|
|
'year': '',
|
|
|
|
|
}
|
|
|
|
|
annotation['licenses'].append({
|
|
|
|
|
'name': '',
|
|
|
|
|
'id': 0,
|
|
|
|
|
'url': '',
|
|
|
|
|
})
|
|
|
|
|
annotation['categories'].append({
|
|
|
|
|
'id': 1,
|
|
|
|
|
'name': 'TEST',
|
|
|
|
|
'supercategory': '',
|
|
|
|
|
})
|
|
|
|
|
annotation['images'].append({
|
|
|
|
|
"id": 1,
|
|
|
|
|
"width": 5,
|
|
|
|
|
"height": 10,
|
|
|
|
|
"file_name": '000000000001.jpg',
|
|
|
|
|
"license": 0,
|
|
|
|
|
"flickr_url": '',
|
|
|
|
|
"coco_url": '',
|
|
|
|
|
"date_captured": 0,
|
|
|
|
|
})
|
|
|
|
|
annotation['annotations'].append({
|
|
|
|
|
"id": 1,
|
|
|
|
|
"image_id": 1,
|
|
|
|
|
"category_id": 1,
|
|
|
|
|
"segmentation": [[0, 0, 1, 0, 1, 2, 0, 2]],
|
|
|
|
|
"area": 2,
|
|
|
|
|
"bbox": [0, 0, 1, 2],
|
|
|
|
|
"iscrowd": 0,
|
|
|
|
|
})
|
|
|
|
|
annotation['annotations'].append({
|
|
|
|
|
"id": 2,
|
|
|
|
|
"image_id": 1,
|
|
|
|
|
"category_id": 1,
|
|
|
|
|
"segmentation": {
|
|
|
|
|
"counts": [
|
|
|
|
|
0, 10,
|
|
|
|
|
5, 5,
|
|
|
|
|
5, 5,
|
|
|
|
|
0, 10,
|
|
|
|
|
10, 0],
|
|
|
|
|
"size": [10, 5]},
|
|
|
|
|
"area": 30,
|
|
|
|
|
"bbox": [0, 0, 10, 4],
|
|
|
|
|
"iscrowd": 1,
|
|
|
|
|
})
|
|
|
|
|
return annotation
|
|
|
|
|
|
|
|
|
|
def COCO_dataset_generate(self, path):
|
|
|
|
|
img_dir = osp.join(path, 'images', 'val')
|
|
|
|
|
ann_dir = osp.join(path, 'annotations')
|
|
|
|
|
os.makedirs(img_dir)
|
|
|
|
|
os.makedirs(ann_dir)
|
|
|
|
|
|
|
|
|
|
image = np.ones((10, 5, 3))
|
|
|
|
|
save_image(osp.join(img_dir, '000000000001.jpg'), image)
|
|
|
|
|
|
|
|
|
|
annotation = self.generate_annotation()
|
|
|
|
|
|
|
|
|
|
with open(osp.join(ann_dir, 'instances_val.json'), 'w') as outfile:
|
|
|
|
|
json.dump(annotation, outfile)
|
|
|
|
|
DUMMY_DATASET_DIR = osp.join(osp.dirname(__file__), 'assets', 'coco_dataset')
|
|
|
|
|
|
|
|
|
|
class CocoImporterTest(TestCase):
|
|
|
|
|
def test_can_import(self):
|
|
|
|
|
class DstExtractor(Extractor):
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
@ -129,18 +46,13 @@ class CocoImporterTest(TestCase):
|
|
|
|
|
label_cat.add('TEST')
|
|
|
|
|
return { AnnotationType.label: label_cat }
|
|
|
|
|
|
|
|
|
|
with TestDir() as test_dir:
|
|
|
|
|
self.COCO_dataset_generate(test_dir)
|
|
|
|
|
|
|
|
|
|
dataset = Project.import_from(test_dir, 'coco').make_dataset()
|
|
|
|
|
dataset = Project.import_from(DUMMY_DATASET_DIR, 'coco') \
|
|
|
|
|
.make_dataset()
|
|
|
|
|
|
|
|
|
|
compare_datasets(self, DstExtractor(), dataset)
|
|
|
|
|
compare_datasets(self, DstExtractor(), dataset)
|
|
|
|
|
|
|
|
|
|
def test_can_detect(self):
|
|
|
|
|
with TestDir() as test_dir:
|
|
|
|
|
self.COCO_dataset_generate(test_dir)
|
|
|
|
|
|
|
|
|
|
self.assertTrue(CocoImporter.detect(test_dir))
|
|
|
|
|
self.assertTrue(CocoImporter.detect(DUMMY_DATASET_DIR))
|
|
|
|
|
|
|
|
|
|
class CocoConverterTest(TestCase):
|
|
|
|
|
def _test_save_and_load(self, source_dataset, converter, test_dir,
|
|
|
|
|
@ -282,7 +194,7 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
label_categories.add(str(i))
|
|
|
|
|
categories = { AnnotationType.label: label_categories }
|
|
|
|
|
|
|
|
|
|
class TestExtractor(Extractor):
|
|
|
|
|
class SrcExtractor(Extractor):
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
return iter([
|
|
|
|
|
DatasetItem(id=1, image=np.zeros((6, 10, 3)),
|
|
|
|
|
@ -298,30 +210,35 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
def categories(self):
|
|
|
|
|
return categories
|
|
|
|
|
|
|
|
|
|
class TargetExtractor(TestExtractor):
|
|
|
|
|
class DstExtractor(Extractor):
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
items = list(super().__iter__())
|
|
|
|
|
items[0]._annotations = [
|
|
|
|
|
Mask(np.array([
|
|
|
|
|
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
|
|
|
|
|
[0, 0, 1, 1, 0, 1, 1, 1, 0, 0],
|
|
|
|
|
[0, 0, 0, 1, 0, 1, 1, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
|
|
|
# only internal fragment (without the border),
|
|
|
|
|
# but not everywhere...
|
|
|
|
|
),
|
|
|
|
|
label=3, id=4, group=4,
|
|
|
|
|
attributes={ 'is_crowd': False }),
|
|
|
|
|
]
|
|
|
|
|
return iter(items)
|
|
|
|
|
return iter([
|
|
|
|
|
DatasetItem(id=1, image=np.zeros((6, 10, 3)),
|
|
|
|
|
annotations=[
|
|
|
|
|
Mask(np.array([
|
|
|
|
|
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
|
|
|
|
|
[0, 0, 1, 1, 0, 1, 1, 1, 0, 0],
|
|
|
|
|
[0, 0, 0, 1, 0, 1, 1, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
|
|
|
# only internal fragment (without the border),
|
|
|
|
|
# but not everywhere...
|
|
|
|
|
),
|
|
|
|
|
label=3, id=4, group=4,
|
|
|
|
|
attributes={ 'is_crowd': False }),
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
def categories(self):
|
|
|
|
|
return categories
|
|
|
|
|
|
|
|
|
|
with TestDir() as test_dir:
|
|
|
|
|
self._test_save_and_load(TestExtractor(),
|
|
|
|
|
self._test_save_and_load(SrcExtractor(),
|
|
|
|
|
CocoInstancesConverter(), test_dir,
|
|
|
|
|
importer_args={'merge_instance_polygons': True},
|
|
|
|
|
target_dataset=TargetExtractor())
|
|
|
|
|
target_dataset=DstExtractor())
|
|
|
|
|
|
|
|
|
|
def test_can_crop_covered_segments(self):
|
|
|
|
|
label_categories = LabelCategories()
|
|
|
|
|
@ -435,7 +352,7 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
for i in range(10):
|
|
|
|
|
label_categories.add(str(i))
|
|
|
|
|
|
|
|
|
|
class SrcTestExtractor(Extractor):
|
|
|
|
|
class SrcExtractor(Extractor):
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
return iter([
|
|
|
|
|
DatasetItem(id=1, image=np.zeros((5, 10, 3)),
|
|
|
|
|
@ -455,7 +372,7 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
def categories(self):
|
|
|
|
|
return { AnnotationType.label: label_categories }
|
|
|
|
|
|
|
|
|
|
class DstTestExtractor(Extractor):
|
|
|
|
|
class DstExtractor(Extractor):
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
return iter([
|
|
|
|
|
DatasetItem(id=1, image=np.zeros((5, 10, 3)),
|
|
|
|
|
@ -476,9 +393,9 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
return { AnnotationType.label: label_categories }
|
|
|
|
|
|
|
|
|
|
with TestDir() as test_dir:
|
|
|
|
|
self._test_save_and_load(SrcTestExtractor(),
|
|
|
|
|
self._test_save_and_load(SrcExtractor(),
|
|
|
|
|
CocoInstancesConverter(segmentation_mode='polygons'), test_dir,
|
|
|
|
|
target_dataset=DstTestExtractor())
|
|
|
|
|
target_dataset=DstExtractor())
|
|
|
|
|
|
|
|
|
|
def test_can_save_and_load_images(self):
|
|
|
|
|
class TestExtractor(Extractor):
|
|
|
|
|
@ -506,16 +423,8 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
annotations=[
|
|
|
|
|
Label(4, id=1, group=1),
|
|
|
|
|
Label(9, id=2, group=2),
|
|
|
|
|
]),
|
|
|
|
|
DatasetItem(id=2, subset='train',
|
|
|
|
|
annotations=[
|
|
|
|
|
Label(4, id=4, group=4),
|
|
|
|
|
]),
|
|
|
|
|
|
|
|
|
|
DatasetItem(id=3, subset='val',
|
|
|
|
|
annotations=[
|
|
|
|
|
Label(2, id=1, group=1),
|
|
|
|
|
]),
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
def categories(self):
|
|
|
|
|
@ -555,21 +464,16 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
# Full instance annotations: bbox + keypoints
|
|
|
|
|
Points([1, 2, 3, 4, 2, 3], group=2, id=2),
|
|
|
|
|
Bbox(1, 2, 2, 2, group=2, id=2),
|
|
|
|
|
]),
|
|
|
|
|
DatasetItem(id=2, subset='train', image=np.zeros((5, 4, 3)),
|
|
|
|
|
annotations=[
|
|
|
|
|
|
|
|
|
|
# Solitary keypoints
|
|
|
|
|
Points([1, 2, 0, 2, 4, 1], label=5, id=3),
|
|
|
|
|
|
|
|
|
|
# Some other solitary annotations (bug #1387)
|
|
|
|
|
Polygon([0, 0, 4, 0, 4, 4], label=3, id=4),
|
|
|
|
|
]),
|
|
|
|
|
|
|
|
|
|
DatasetItem(id=3, subset='val',
|
|
|
|
|
annotations=[
|
|
|
|
|
# Solitary keypoints with no label
|
|
|
|
|
Points([0, 0, 1, 2, 3, 4], [0, 1, 2], id=3),
|
|
|
|
|
]),
|
|
|
|
|
Points([0, 0, 1, 2, 3, 4], [0, 1, 2], id=5),
|
|
|
|
|
])
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
def categories(self):
|
|
|
|
|
@ -593,24 +497,19 @@ class CocoConverterTest(TestCase):
|
|
|
|
|
Polygon([1, 2, 3, 2, 3, 4, 1, 4],
|
|
|
|
|
group=2, id=2,
|
|
|
|
|
attributes={'is_crowd': False}),
|
|
|
|
|
]),
|
|
|
|
|
DatasetItem(id=2, subset='train',
|
|
|
|
|
annotations=[
|
|
|
|
|
|
|
|
|
|
Points([1, 2, 0, 2, 4, 1],
|
|
|
|
|
label=5, group=3, id=3,
|
|
|
|
|
attributes={'is_crowd': False}),
|
|
|
|
|
Polygon([0, 1, 4, 1, 4, 2, 0, 2],
|
|
|
|
|
label=5, group=3, id=3,
|
|
|
|
|
attributes={'is_crowd': False}),
|
|
|
|
|
]),
|
|
|
|
|
|
|
|
|
|
DatasetItem(id=3, subset='val',
|
|
|
|
|
annotations=[
|
|
|
|
|
Points([0, 0, 1, 2, 3, 4], [0, 1, 2],
|
|
|
|
|
group=3, id=3,
|
|
|
|
|
group=5, id=5,
|
|
|
|
|
attributes={'is_crowd': False}),
|
|
|
|
|
Polygon([1, 2, 3, 2, 3, 4, 1, 4],
|
|
|
|
|
group=3, id=3,
|
|
|
|
|
group=5, id=5,
|
|
|
|
|
attributes={'is_crowd': False}),
|
|
|
|
|
]),
|
|
|
|
|
])
|
|
|
|
|
|