diff --git a/CHANGELOG.md b/CHANGELOG.md index afd20a1e658f..1270dfc691f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Manual review pipeline: issues/comments/workspace () -- Added basic projects implementation () -- Added documentation on how to mount cloud starage(AWS S3 bucket, Azure container, Google Drive) as FUSE () -- Added ability to work with share files without copying inside () +- Basic projects implementation () +- Documentation on how to mount cloud starage(AWS S3 bucket, Azure container, Google Drive) as FUSE () +- Ability to work with share files without copying inside () - Tooltips in label selectors () - Page redirect after login using `next` query parameter () +- [ImageNet](http://www.image-net.org) format support () +- [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) format support () ### Changed diff --git a/README.md b/README.md index 9721f67c311f..e0921ba525c0 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,13 @@ annotation team. Try it online [cvat.org](https://cvat.org). ## Supported annotation formats -Format selection is possible after clicking on the Upload annotation -and Dump annotation buttons. -[Datumaro](https://github.com/openvinotoolkit/datumaro) dataset -framework allows additional dataset transformations -via its command line tool and Python library. +Format selection is possible after clicking on the Upload annotation and Dump +annotation buttons. [Datumaro](https://github.com/openvinotoolkit/datumaro) +dataset framework allows additional dataset transformations via its command +line tool and Python library. + +For more information about supported formats look at the +[documentation](cvat/apps/dataset_manager/formats/README.md#formats). | Annotation format | Import | Export | | ----------------------------------------------------------------------------- | ------ | ------ | @@ -58,6 +60,8 @@ via its command line tool and Python library. | [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X | | [MOT](https://motchallenge.net/) | X | X | | [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X | +| [ImageNet](http://www.image-net.org) | X | X | +| [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) | X | X | ## Deep learning models for automatic labeling diff --git a/cvat/apps/dataset_manager/formats/README.md b/cvat/apps/dataset_manager/formats/README.md index b47287013eaf..38ff06762172 100644 --- a/cvat/apps/dataset_manager/formats/README.md +++ b/cvat/apps/dataset_manager/formats/README.md @@ -19,6 +19,7 @@ - [YOLO](#yolo) - [TF detection API](#tfrecord) - [ImageNet](#imagenet) + - [CamVid](#camvid) ## How to add a new annotation format support @@ -835,3 +836,41 @@ taskname.zip/ Uploaded file: a zip archive of the structure above - supported annotations: Labels + +### [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) + +#### CamVid Dumper + +Downloaded file: a zip archive of the following structure: + +```bash +taskname.zip/ +├── labelmap.txt # optional, required for non-CamVid labels +└── / + ├── image1.png + └── image2.png +└── annot/ + ├── image1.png + └── image2.png +└── .txt + +# labelmap.txt +# color (RGB) label +0 0 0 Void +64 128 64 Animal +192 0 128 Archway +0 128 192 Bicyclist +0 128 64 Bridge +``` + +Mask is a `png` image with 1 or 3 channels where each pixel +has own color which corresponds to a label. +`(0, 0, 0)` is used for background by default. + +- supported annotations: Rectangles, Polygons + +#### CamVid Loader + +Uploaded file: a zip archive of the structure above + +- supported annotations: Polygons diff --git a/cvat/apps/dataset_manager/formats/camvid.py b/cvat/apps/dataset_manager/formats/camvid.py new file mode 100644 index 000000000000..bcd00b7a7bf1 --- /dev/null +++ b/cvat/apps/dataset_manager/formats/camvid.py @@ -0,0 +1,42 @@ +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from tempfile import TemporaryDirectory + +from datumaro.components.project import Dataset +from pyunpack import Archive + +from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, + import_dm_annotations) +from cvat.apps.dataset_manager.util import make_zip_archive + +from .registry import dm_env, exporter, importer +from .utils import make_colormap + + +@exporter(name='CamVid', ext='ZIP', version='1.0') +def _export(dst_file, task_data, save_images=False): + extractor = CvatTaskDataExtractor(task_data, include_images=save_images) + envt = dm_env.transforms + extractor = extractor.transform(envt.get('polygons_to_masks')) + extractor = extractor.transform(envt.get('boxes_to_masks')) + extractor = extractor.transform(envt.get('merge_instance_segments')) + extractor = Dataset.from_extractors(extractor) # apply lazy transforms + label_map = make_colormap(task_data) + with TemporaryDirectory() as temp_dir: + dm_env.converters.get('camvid').convert(extractor, + save_dir=temp_dir, save_images=save_images, apply_colormap=True, + label_map={label: label_map[label][0] for label in label_map}) + + make_zip_archive(temp_dir, dst_file) + +@importer(name='CamVid', ext='ZIP', version='1.0') +def _import(src_file, task_data): + with TemporaryDirectory() as tmp_dir: + Archive(src_file.name).extractall(tmp_dir) + + dataset = dm_env.make_importer('camvid')(tmp_dir).make_dataset() + masks_to_polygons = dm_env.transforms.get('masks_to_polygons') + dataset = dataset.transform(masks_to_polygons) + import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/registry.py b/cvat/apps/dataset_manager/formats/registry.py index c175a42b7728..507b497992b8 100644 --- a/cvat/apps/dataset_manager/formats/registry.py +++ b/cvat/apps/dataset_manager/formats/registry.py @@ -91,4 +91,5 @@ def make_exporter(name): import cvat.apps.dataset_manager.formats.pascal_voc import cvat.apps.dataset_manager.formats.tfrecord import cvat.apps.dataset_manager.formats.yolo -import cvat.apps.dataset_manager.formats.imagenet \ No newline at end of file +import cvat.apps.dataset_manager.formats.imagenet +import cvat.apps.dataset_manager.formats.camvid \ No newline at end of file diff --git a/cvat/apps/dataset_manager/tests/test_formats.py b/cvat/apps/dataset_manager/tests/test_formats.py index d41e253c5821..a01e60b78ae7 100644 --- a/cvat/apps/dataset_manager/tests/test_formats.py +++ b/cvat/apps/dataset_manager/tests/test_formats.py @@ -270,6 +270,7 @@ def test_export_formats_query(self): 'TFRecord 1.0', 'YOLO 1.1', 'ImageNet 1.0', + 'CamVid 1.0', }) def test_import_formats_query(self): @@ -287,6 +288,7 @@ def test_import_formats_query(self): 'TFRecord 1.0', 'YOLO 1.1', 'ImageNet 1.0', + 'CamVid 1.0', }) def test_exports(self): @@ -323,6 +325,7 @@ def test_empty_images_are_exported(self): ('TFRecord 1.0', 'tf_detection_api'), ('YOLO 1.1', 'yolo'), ('ImageNet 1.0', 'imagenet_txt'), + ('CamVid 1.0', 'camvid'), ]: with self.subTest(format=format_name): if not dm.formats.registry.EXPORT_FORMATS[format_name].ENABLED: diff --git a/cvat/apps/engine/tests/test_rest_api.py b/cvat/apps/engine/tests/test_rest_api.py index ff938adfc25f..32b1ddbdace0 100644 --- a/cvat/apps/engine/tests/test_rest_api.py +++ b/cvat/apps/engine/tests/test_rest_api.py @@ -3751,6 +3751,10 @@ def _get_initial_annotation(annotation_format): elif annotation_format == "ImageNet 1.0": annotations["tags"] = tags_wo_attrs + elif annotation_format == "CamVid 1.0": + annotations["shapes"] = rectangle_shapes_wo_attrs \ + + polygon_shapes_wo_attrs + else: raise Exception("Unknown format {}".format(annotation_format)) @@ -3847,7 +3851,7 @@ def _get_initial_annotation(annotation_format): self.assertEqual(response.status_code, HTTP_201_CREATED) # 7. check annotation - if import_format in {"Segmentation mask 1.1", "MOTS PNG 1.0"}: + if import_format in {"Segmentation mask 1.1", "MOTS PNG 1.0", "CamVid 1.0"}: continue # can't really predict the result to check response = self._get_api_v1_tasks_id_annotations(task["id"], annotator) self.assertEqual(response.status_code, HTTP_200_OK)