diff --git a/datumaro/plugins/coco_format/converter.py b/datumaro/plugins/coco_format/converter.py index 71cb37dbc0..44d604ab09 100644 --- a/datumaro/plugins/coco_format/converter.py +++ b/datumaro/plugins/coco_format/converter.py @@ -16,7 +16,7 @@ import datumaro.util.mask_tools as mask_tools from datumaro.components.converter import Converter from datumaro.components.extractor import (_COORDINATE_ROUNDING_DIGITS, - DEFAULT_SUBSET_NAME, AnnotationType, Points) + AnnotationType, Points) from datumaro.util import cast, find, str_to_bool from .format import CocoPath, CocoTask @@ -144,7 +144,9 @@ def save_annotations(self, item): log.warning("Item '%s', ann #%s: failed to convert " "attribute 'score': %e" % (item.id, ann_idx, e)) if self._context._allow_attributes: - elem['attributes'] = self._convert_attributes(ann) + attrs = self._convert_attributes(ann) + if attrs: + elem['attributes'] = attrs self.annotations.append(elem) @@ -317,7 +319,9 @@ def convert_instance(self, instance, item): log.warning("Item '%s': failed to convert attribute " "'score': %e" % (item.id, e)) if self._context._allow_attributes: - elem['attributes'] = self._convert_attributes(ann) + attrs = self._convert_attributes(ann) + if attrs: + elem['attributes'] = attrs return elem @@ -434,7 +438,9 @@ def save_annotations(self, item): log.warning("Item '%s': failed to convert attribute " "'score': %e" % (item.id, e)) if self._context._allow_attributes: - elem['attributes'] = self._convert_attributes(ann) + attrs = self._convert_attributes(ann) + if attrs: + elem['attributes'] = attrs self.annotations.append(elem) diff --git a/tests/assets/coco_dataset/coco_captions/annotations/captions_train.json b/tests/assets/coco_dataset/coco_captions/annotations/captions_train.json new file mode 100644 index 0000000000..e360262e0c --- /dev/null +++ b/tests/assets/coco_dataset/coco_captions/annotations/captions_train.json @@ -0,0 +1,54 @@ +{ + "licenses": [{ + "name": "", + "id": 0, + "url": "" + }], + "info": { + "contributor": "", + "date_created": "", + "description": "", + "url": "", + "version": "", + "year": "" + }, + "categories": [], + "images": [{ + "id": 1, + "width": 0, + "height": 0, + "file_name": "1.jpg", + "license": 0, + "flickr_url": "", + "coco_url": "", + "date_captured": 0 + }, { + "id": 2, + "width": 0, + "height": 0, + "file_name": "2.jpg", + "license": 0, + "flickr_url": "", + "coco_url": "", + "date_captured": 0 + }], + "annotations": [{ + "id": 1, + "image_id": 1, + "category_id": 0, + "caption": "hello", + "attributes": {} + }, { + "id": 2, + "image_id": 1, + "category_id": 0, + "caption": "world", + "attributes": {} + }, { + "id": 3, + "image_id": 2, + "category_id": 0, + "caption": "test", + "attributes": {} + }] +} \ No newline at end of file diff --git a/tests/assets/coco_dataset/coco_captions/annotations/captions_val.json b/tests/assets/coco_dataset/coco_captions/annotations/captions_val.json new file mode 100644 index 0000000000..47d071a57d --- /dev/null +++ b/tests/assets/coco_dataset/coco_captions/annotations/captions_val.json @@ -0,0 +1,33 @@ +{ + "licenses": [{ + "name": "", + "id": 0, + "url": "" + }], + "info": { + "contributor": "", + "date_created": "", + "description": "", + "url": "", + "version": "", + "year": "" + }, + "categories": [], + "images": [{ + "id": 1, + "width": 0, + "height": 0, + "file_name": "3.jpg", + "license": 0, + "flickr_url": "", + "coco_url": "", + "date_captured": 0 + }], + "annotations": [{ + "id": 1, + "image_id": 1, + "category_id": 0, + "caption": "word", + "attributes": {} + }] +} \ No newline at end of file diff --git a/tests/assets/coco_dataset/coco_image_info/annotations/image_info_default.json b/tests/assets/coco_dataset/coco_image_info/annotations/image_info_default.json new file mode 100644 index 0000000000..f2fc85a73f --- /dev/null +++ b/tests/assets/coco_dataset/coco_image_info/annotations/image_info_default.json @@ -0,0 +1,27 @@ +{ + "licenses": [{ + "name": "", + "id": 0, + "url": "" + }], + "info": { + "contributor": "", + "date_created": "", + "description": "", + "url": "", + "version": "", + "year": "" + }, + "categories": [], + "images": [{ + "id": 1, + "width": 15, + "height": 10, + "file_name": "1.jpg", + "license": 0, + "flickr_url": "", + "coco_url": "", + "date_captured": 0 + }], + "annotations": [] +} \ No newline at end of file diff --git a/tests/assets/coco_dataset/annotations/instances_val.json b/tests/assets/coco_dataset/coco_instances/annotations/instances_val.json similarity index 92% rename from tests/assets/coco_dataset/annotations/instances_val.json rename to tests/assets/coco_dataset/coco_instances/annotations/instances_val.json index b5d9bd8697..74de288d8e 100644 --- a/tests/assets/coco_dataset/annotations/instances_val.json +++ b/tests/assets/coco_dataset/coco_instances/annotations/instances_val.json @@ -41,7 +41,10 @@ "segmentation": [[0, 0, 1, 0, 1, 2, 0, 2]], "area": 2, "bbox": [0, 0, 1, 2], - "iscrowd": 0 + "iscrowd": 0, + "attributes": { + "x": 1, "y": "hello" + } }, { "id": 2, diff --git a/tests/assets/coco_dataset/images/val/000000000001.jpg b/tests/assets/coco_dataset/coco_instances/images/val/000000000001.jpg similarity index 100% rename from tests/assets/coco_dataset/images/val/000000000001.jpg rename to tests/assets/coco_dataset/coco_instances/images/val/000000000001.jpg diff --git a/tests/assets/coco_dataset/coco_labels/annotations/labels_train.json b/tests/assets/coco_dataset/coco_labels/annotations/labels_train.json new file mode 100644 index 0000000000..1f790645c3 --- /dev/null +++ b/tests/assets/coco_dataset/coco_labels/annotations/labels_train.json @@ -0,0 +1,44 @@ +{ + "licenses": [{ + "name": "", + "id": 0, + "url": "" + }], + "info": { + "contributor": "", + "date_created": "", + "description": "", + "url": "", + "version": "", + "year": "" + }, + "categories": [{ + "id": 1, + "name": "a", + "supercategory": "" + }, { + "id": 2, + "name": "b", + "supercategory": "" + }], + "images": [{ + "id": 1, + "width": 0, + "height": 0, + "file_name": "1.jpg", + "license": 0, + "flickr_url": "", + "coco_url": "", + "date_captured": 0 + }], + "annotations": [{ + "id": 1, + "image_id": 1, + "category_id": 2 + }, { + "id": 2, + "image_id": 1, + "category_id": 1, + "attributes": {} + }] +} \ No newline at end of file diff --git a/tests/assets/coco_dataset/coco_person_keypoints/annotations/person_keypoints_train.json b/tests/assets/coco_dataset/coco_person_keypoints/annotations/person_keypoints_train.json new file mode 100644 index 0000000000..e5c2238d17 --- /dev/null +++ b/tests/assets/coco_dataset/coco_person_keypoints/annotations/person_keypoints_train.json @@ -0,0 +1,87 @@ +{ + "licenses": [{ + "name": "", + "id": 0, + "url": "" + }], + "info": { + "contributor": "", + "date_created": "", + "description": "", + "url": "", + "version": "", + "year": "" + }, + "categories": [{ + "id": 1, + "name": "a", + "supercategory": "", + "keypoints": [], + "skeleton": [ + [0, 1], + [1, 2] + ] + }, { + "id": 2, + "name": "b", + "supercategory": "", + "keypoints": [], + "skeleton": [ + [0, 1], + [1, 2] + ] + }], + "images": [{ + "id": 1, + "width": 5, + "height": 5, + "file_name": "1.jpg", + "license": 0, + "flickr_url": "", + "coco_url": "", + "date_captured": 0 + }], + "annotations": [{ + "id": 3, + "image_id": 1, + "category_id": 1, + "segmentation": [], + "area": 4.0, + "bbox": [0.0, 1.0, 4.0, 1.0], + "iscrowd": 0, + "keypoints": [1, 2, 2, 0, 2, 2, 4, 1, 2], + "num_keypoints": 3 + }, { + "id": 5, + "image_id": 1, + "category_id": 0, + "segmentation": [], + "area": 4.0, + "bbox": [1.0, 2.0, 2.0, 2.0], + "iscrowd": 0, + "keypoints": [0, 0, 0, 1, 2, 1, 3, 4, 2], + "num_keypoints": 2 + }, { + "id": 1, + "image_id": 1, + "category_id": 2, + "segmentation": [ + [0.0, 0.0, 4.0, 0.0, 4.0, 4.0] + ], + "area": 6.0, + "bbox": [0.0, 0.0, 4.0, 4.0], + "iscrowd": 0, + "keypoints": [0, 0, 0, 0, 2, 1, 4, 1, 2], + "num_keypoints": 2 + }, { + "id": 2, + "image_id": 1, + "category_id": 0, + "segmentation": [], + "area": 4.0, + "bbox": [1.0, 2.0, 2.0, 2.0], + "iscrowd": 0, + "keypoints": [1, 2, 2, 3, 4, 2, 2, 3, 2], + "num_keypoints": 3 + }] +} \ No newline at end of file diff --git a/tests/test_coco_format.py b/tests/test_coco_format.py index f97d9255f8..9fcd26d30d 100644 --- a/tests/test_coco_format.py +++ b/tests/test_coco_format.py @@ -26,13 +26,14 @@ DUMMY_DATASET_DIR = osp.join(osp.dirname(__file__), 'assets', 'coco_dataset') class CocoImporterTest(TestCase): - def test_can_import(self): + def test_can_import_instances(self): expected_dataset = Dataset.from_iterable([ DatasetItem(id='000000000001', image=np.ones((10, 5, 3)), subset='val', attributes={'id': 1}, annotations=[ Polygon([0, 0, 1, 0, 1, 2, 0, 2], label=0, - id=1, group=1, attributes={'is_crowd': False}), + id=1, group=1, attributes={'is_crowd': False, + 'x': 1, 'y': 'hello'}), Mask(np.array( [[1, 0, 0, 1, 0]] * 5 + [[1, 1, 1, 1, 0]] * 5 @@ -42,12 +43,107 @@ def test_can_import(self): ), ], categories=['TEST',]) - dataset = Dataset.import_from(DUMMY_DATASET_DIR, 'coco') + dataset = Dataset.import_from( + osp.join(DUMMY_DATASET_DIR, 'coco_instances'), 'coco') + + compare_datasets(self, expected_dataset, dataset) + + def test_can_import_captions(self): + expected_dataset = Dataset.from_iterable([ + DatasetItem(id=1, subset='train', + annotations=[ + Caption('hello', id=1, group=1), + Caption('world', id=2, group=2), + ], attributes={'id': 1}), + DatasetItem(id=2, subset='train', + annotations=[ + Caption('test', id=3, group=3), + ], attributes={'id': 2}), + + DatasetItem(id=3, subset='val', + annotations=[ + Caption('word', id=1, group=1), + ], attributes={'id': 1}), + ]) + + dataset = Dataset.import_from( + osp.join(DUMMY_DATASET_DIR, 'coco_captions'), 'coco') + + compare_datasets(self, expected_dataset, dataset) + + def test_can_import_labels(self): + expected_dataset = Dataset.from_iterable([ + DatasetItem(id=1, subset='train', + annotations=[ + Label(1, id=1, group=1), + Label(0, id=2, group=2), + ], attributes={'id': 1}), + ], categories=['a', 'b']) + + dataset = Dataset.import_from( + osp.join(DUMMY_DATASET_DIR, 'coco_labels'), 'coco') + + compare_datasets(self, expected_dataset, dataset) + + def test_can_import_points(self): + expected_dataset = Dataset.from_iterable([ + DatasetItem(id=1, subset='train', + image=Image(path='1.jpg', size=(5, 5)), + annotations=[ + Points([0, 0, 0, 2, 4, 1], [0, 1, 2], + label=1, group=1, id=1, + attributes={'is_crowd': False}), + Polygon([0, 0, 4, 0, 4, 4], + label=1, group=1, id=1, + attributes={'is_crowd': False}), + + Points([1, 2, 3, 4, 2, 3], + group=2, id=2, + attributes={'is_crowd': False}), + Bbox(1, 2, 2, 2, + group=2, id=2, + attributes={'is_crowd': False}), + + Points([1, 2, 0, 2, 4, 1], + label=0, group=3, id=3, + attributes={'is_crowd': False}), + Bbox(0, 1, 4, 1, + label=0, group=3, id=3, + attributes={'is_crowd': False}), + + Points([0, 0, 1, 2, 3, 4], [0, 1, 2], + group=5, id=5, + attributes={'is_crowd': False}), + Bbox(1, 2, 2, 2, + group=5, id=5, + attributes={'is_crowd': False}), + ], attributes={'id': 1}), + ], categories={ + AnnotationType.label: LabelCategories.from_iterable(['a', 'b']), + AnnotationType.points: PointsCategories.from_iterable( + (i, None, [[0, 1], [1, 2]]) for i in range(2) + ), + }) + + dataset = Dataset.import_from( + osp.join(DUMMY_DATASET_DIR, 'coco_person_keypoints'), 'coco') + + compare_datasets(self, expected_dataset, dataset) + + def test_can_import_image_info(self): + expected_dataset = Dataset.from_iterable([ + DatasetItem(id=1, image=Image(path='1.jpg', size=(10, 15)), + attributes={'id': 1}), + ]) + + dataset = Dataset.import_from( + osp.join(DUMMY_DATASET_DIR, 'coco_image_info'), 'coco') compare_datasets(self, expected_dataset, dataset) def test_can_detect(self): - self.assertTrue(CocoImporter.detect(DUMMY_DATASET_DIR)) + self.assertTrue(CocoImporter.detect( + osp.join(DUMMY_DATASET_DIR, 'coco_instances'))) class CocoConverterTest(TestCase): def _test_save_and_load(self, source_dataset, converter, test_dir,