diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b1930ef4c7..e456da47db21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Backup/Restore guide](cvat/apps/documentation/backup_guide.md) () - Label deletion from tasks and projects () - [ICDAR](https://rrc.cvc.uab.es/?ch=2) format support () +- [Market-1501](https://www.aitribune.com/dataset/2018051063) format support () ### Changed diff --git a/README.md b/README.md index 157ea1540d52..5ef55cdacdc0 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ For more information about supported formats look at the | [WIDER Face](http://shuoyang1213.me/WIDERFACE/) | X | X | | [VGGFace2](https://github.com/ox-vgg/vgg_face2) | X | X | | [ICDAR13/15](https://rrc.cvc.uab.es/?ch=2) | X | X | +| [Market-1501](https://www.aitribune.com/dataset/2018051063) | X | X | ## Deep learning serverless functions for automatic labeling diff --git a/cvat/apps/dataset_manager/formats/README.md b/cvat/apps/dataset_manager/formats/README.md index fc2b612c5bf3..56276da7f4fd 100644 --- a/cvat/apps/dataset_manager/formats/README.md +++ b/cvat/apps/dataset_manager/formats/README.md @@ -23,6 +23,7 @@ - [WIDER Face](#widerface) - [VGGFace2](#vggface2) - [ICDAR13/15](#icdar) + - [Market-1501](#market1501) ## How to add a new annotation format support @@ -954,7 +955,6 @@ taskname.zip/ | ├── word1.png | └── word2.png └── gt.txt - # text localization task taskname.zip/ └── text_localization/ @@ -964,7 +964,6 @@ taskname.zip/ | └── img_2.png ├── gt_img_1.txt └── gt_img_1.txt - #text segmentation task taskname.zip/ └── text_localization/ @@ -1009,3 +1008,39 @@ Uploaded file: a zip archive of the structure above - supported annotations: Rectangles and Polygons with label `icdar` and attributes `index`, `text`, `color`, `center` + +### [Market-1501](https://www.aitribune.com/dataset/2018051063) + +#### Market-1501 Dumper + +Downloaded file: a zip archive of the following structure: + +```bash +taskname.zip/ +├── bounding_box_/ +│ └── image_name_1.jpg +└── query + ├── image_name_2.jpg + └── image_name_3.jpg +# if we keep only annotation: +taskname.zip/ +└── images_.txt +# images_.txt +query/image_name_1.jpg +bounding_box_/image_name_2.jpg +bounding_box_/image_name_3.jpg +# image_name = 0001_c1s1_000015_00.jpg +0001 - person id +c1 - camera id (there are totally 6 cameras) +s1 - sequence +000015 - frame number in sequence +00 - means that this bounding box is the first one among the several +``` + +- supported annotations: Label `market-1501` with atrributes (`query`, `person_id`, `camera_id`) + +#### Market-1501 Loader + +Uploaded file: a zip archive of the structure above + +- supported annotations: Label `market-1501` with atrributes (`query`, `person_id`, `camera_id`) diff --git a/cvat/apps/dataset_manager/formats/market1501.py b/cvat/apps/dataset_manager/formats/market1501.py new file mode 100644 index 000000000000..1b6e24f43f1a --- /dev/null +++ b/cvat/apps/dataset_manager/formats/market1501.py @@ -0,0 +1,77 @@ +# Copyright (C) 2021 Intel Corporation +# +# SPDX-License-Identifier: MIT + +import zipfile +from tempfile import TemporaryDirectory + +from datumaro.components.dataset import Dataset +from datumaro.components.extractor import (AnnotationType, Label, + LabelCategories, Transform) + +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 + +class AttrToLabelAttr(Transform): + def __init__(self, extractor, label): + super().__init__(extractor) + + assert isinstance(label, str) + self._categories = {} + label_cat = self._extractor.categories().get(AnnotationType.label) + if not label_cat: + label_cat = LabelCategories() + self._label = label_cat.add(label) + self._categories[AnnotationType.label] = label_cat + + def categories(self): + return self._categories + + def transform_item(self, item): + annotations = item.annotations + if item.attributes: + annotations.append(Label(self._label, attributes=item.attributes)) + item.attributes = {} + return item.wrap(annotations=annotations) + +class LabelAttrToAttr(Transform): + def __init__(self, extractor, label): + super().__init__(extractor) + + assert isinstance(label, str) + label_cat = self._extractor.categories().get(AnnotationType.label) + self._label = label_cat.find(label)[0] + + def transform_item(self, item): + annotations = item.annotations + attributes = item.attributes + if self._label != None: + labels = [ann for ann in annotations + if ann.type == AnnotationType.label \ + and ann.label == self._label] + if len(labels) == 1: + attributes.update(labels[0].attributes) + annotations.remove(labels[0]) + return item.wrap(annotations=annotations, attributes=attributes) + + +@exporter(name='Market-1501', ext='ZIP', version='1.0') +def _export(dst_file, task_data, save_images=False): + dataset = Dataset.from_extractors(CvatTaskDataExtractor( + task_data, include_images=save_images), env=dm_env) + with TemporaryDirectory() as temp_dir: + dataset.transform(LabelAttrToAttr, 'market-1501') + dataset.export(temp_dir, 'market1501', save_images=save_images) + make_zip_archive(temp_dir, dst_file) + +@importer(name='Market-1501', ext='ZIP', version='1.0') +def _import(src_file, task_data): + with TemporaryDirectory() as tmp_dir: + zipfile.ZipFile(src_file).extractall(tmp_dir) + + dataset = Dataset.import_from(tmp_dir, 'market1501', env=dm_env) + dataset.transform(AttrToLabelAttr, 'market-1501') + 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 22ded3fd832d..a4b2d77124b3 100644 --- a/cvat/apps/dataset_manager/formats/registry.py +++ b/cvat/apps/dataset_manager/formats/registry.py @@ -98,3 +98,4 @@ def make_exporter(name): import cvat.apps.dataset_manager.formats.widerface import cvat.apps.dataset_manager.formats.vggface2 import cvat.apps.dataset_manager.formats.icdar +import cvat.apps.dataset_manager.formats.market1501 diff --git a/cvat/apps/dataset_manager/tests/test_formats.py b/cvat/apps/dataset_manager/tests/test_formats.py index 93a49b52b3ef..fee3fe22fc67 100644 --- a/cvat/apps/dataset_manager/tests/test_formats.py +++ b/cvat/apps/dataset_manager/tests/test_formats.py @@ -287,6 +287,7 @@ def test_export_formats_query(self): 'ICDAR Recognition 1.0', 'ICDAR Localization 1.0', 'ICDAR Segmentation 1.0', + 'Market-1501 1.0', }) def test_import_formats_query(self): @@ -310,6 +311,7 @@ def test_import_formats_query(self): 'ICDAR Recognition 1.0', 'ICDAR Localization 1.0', 'ICDAR Segmentation 1.0', + 'Market-1501 1.0', }) def test_exports(self): @@ -355,6 +357,7 @@ def test_empty_images_are_exported(self): ('ICDAR Recognition 1.0', 'icdar_word_recognition'), ('ICDAR Localization 1.0', 'icdar_text_localization'), ('ICDAR Segmentation 1.0', 'icdar_text_segmentation'), + ('Market-1501 1.0', 'market1501'), ]: 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 8ad6d20f5dd2..e5bc2e61460a 100644 --- a/cvat/apps/engine/tests/test_rest_api.py +++ b/cvat/apps/engine/tests/test_rest_api.py @@ -2873,6 +2873,30 @@ def _create_task(self, owner, assignee, annotation_format=""): "mutable": False, "input_type": "text", "values": ["1 2", "2 4", "10 45"] + } + ] + }] + elif annotation_format == "Market-1501 1.0": + data["labels"] = [{ + "name": "market-1501", + "attributes": [ + { + "name": "query", + "mutable": False, + "input_type": "select", + "values": ["True", "False"] + }, + { + "name": "camera_id", + "mutable": False, + "input_type": "number", + "values": ["0", "1", "2", "3", "4", "5"] + }, + { + "name": "person_id", + "mutable": False, + "input_type": "number", + "values": ["1", "2", "3"] }, ] }] @@ -3856,7 +3880,8 @@ def _run_api_v1_tasks_id_annotations_dump_load(self, owner, assignee, annotator) def _get_initial_annotation(annotation_format): if annotation_format not in ["ICDAR Recognition 1.0", - "ICDAR Localization 1.0", "ICDAR Segmentation 1.0"]: + "ICDAR Localization 1.0", "ICDAR Segmentation 1.0", + "Market-1501 1.0"]: rectangle_tracks_with_attrs = [{ "frame": 0, "label_id": task["labels"][0]["id"], @@ -3997,17 +4022,6 @@ def _get_initial_annotation(annotation_format): "occluded": False, }] - rectangle_shapes_wo_attrs = [{ - "frame": 1, - "label_id": task["labels"][1]["id"], - "group": 0, - "source": "manual", - "attributes": [], - "points": [2.0, 2.1, 40, 50.7], - "type": "rectangle", - "occluded": False, - }] - rectangle_shapes_with_wider_attrs = [{ "frame": 0, "label_id": task["labels"][2]["id"], @@ -4120,11 +4134,11 @@ def _get_initial_annotation(annotation_format): }] annotations = { - "version": 0, - "tags": [], - "shapes": [], - "tracks": [], - } + "version": 0, + "tags": [], + "shapes": [], + "tracks": [], + } if annotation_format == "CVAT for video 1.1": annotations["tracks"] = rectangle_tracks_with_attrs \ + rectangle_tracks_wo_attrs \ @@ -4299,6 +4313,29 @@ def _get_initial_annotation(annotation_format): annotations["shapes"] = rectangle_shapes_with_attrs \ + polygon_shapes_with_attrs + elif annotation_format == "Market-1501 1.0": + tags_with_attrs = [{ + "frame": 1, + "label_id": task["labels"][0]["id"], + "group": 0, + "source": "manual", + "attributes": [ + { + "spec_id": task["labels"][0]["attributes"][0]["id"], + "value": task["labels"][0]["attributes"][0]["values"][1] + }, + { + "spec_id": task["labels"][0]["attributes"][1]["id"], + "value": task["labels"][0]["attributes"][1]["values"][2] + }, + { + "spec_id": task["labels"][0]["attributes"][2]["id"], + "value": task["labels"][0]["attributes"][2]["values"][0] + } + ], + }] + annotations["tags"] = tags_with_attrs + else: raise Exception("Unknown format {}".format(annotation_format)) @@ -4335,7 +4372,7 @@ def _get_initial_annotation(annotation_format): with self.subTest(export_format=export_format, import_format=import_format): # 1. create task - task, jobs = self._create_task(owner, assignee, export_format) + task, jobs = self._create_task(owner, assignee, import_format) # 2. add annotation data = _get_initial_annotation(export_format) diff --git a/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip b/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip index 14eee78e9a19..cc7d7842b920 100644 Binary files a/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip and b/tests/cypress/integration/canvas3d_functionality/assets/test_canvas3d.zip differ diff --git a/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js b/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js index d84a69db9ce4..961638177dbc 100644 --- a/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js +++ b/tests/cypress/integration/canvas3d_functionality/case_56_canvas3d_functionality_basic_actions.js @@ -33,19 +33,19 @@ context('Canvas 3D functionality. Basic actions.', () => { function testPerspectiveChangeOnWheel(screenshotNameBefore, screenshotNameAfter) { cy.get('.cvat-canvas3d-perspective').screenshot(screenshotNameBefore); - for (let i = 0; i < 5; i++) { - cy.get('.cvat-canvas3d-perspective').trigger('wheel', { deltaY: -200 }); + for (let i = 0; i < 3; i++) { + cy.get('.cvat-canvas3d-perspective').trigger('wheel', { deltaY: -50 }); } cy.get('.cvat-canvas3d-perspective').screenshot(screenshotNameAfter); compareImages(`${screenshotNameBefore}.png`, `${screenshotNameAfter}.png`); } - function testTopSideFrontChangeOnWheel(element, deltaY, screenshotNameBefore, screenshotNameAfter) { - cy.get(element).screenshot(screenshotNameBefore); - for (let i = 0; i < 10; i++) { - cy.get(element).trigger('wheel', { deltaY: deltaY }); + function testTopSideFrontChangeOnWheel(element, screenshotNameBefore, screenshotNameAfter) { + cy.get(element).find('.cvat-canvas3d-fullsize').screenshot(screenshotNameBefore); + for (let i = 0; i < 3; i++) { + cy.get(element).trigger('wheel', { deltaY: -100 }); } - cy.get(element).screenshot(screenshotNameAfter); + cy.get(element).find('.cvat-canvas3d-fullsize').screenshot(screenshotNameAfter); compareImages(`${screenshotNameBefore}.png`, `${screenshotNameAfter}.png`); } @@ -152,7 +152,6 @@ context('Canvas 3D functionality. Basic actions.', () => { }); it('Testing perspective visual regressions.', () => { - testPerspectiveChangeOnWheel('perspective_before_wheel', 'perspective_after_wheel'); testPerspectiveChangeOnKeyPress('u', 'before_press_altU', 'after_press_altU'); testPerspectiveChangeOnKeyPress('o', 'before_press_altO', 'after_press_altO'); testPerspectiveChangeOnKeyPress('i', 'before_press_altI', 'after_press_altI'); @@ -163,24 +162,14 @@ context('Canvas 3D functionality. Basic actions.', () => { testPerspectiveChangeOnArrowKeyPress('{downarrow}', 'before_press_downarrow', 'after_press_downarrow'); testPerspectiveChangeOnArrowKeyPress('{leftarrow}', 'before_press_leftarrow', 'after_press_leftarrow'); testPerspectiveChangeOnArrowKeyPress('{rightarrow}', 'before_press_rightarrow', 'after_press_rightarrow'); + testPerspectiveChangeOnWheel('perspective_before_wheel', 'perspective_after_wheel'); }); it('Testing top/side/front views visual regressions.', () => { - testTopSideFrontChangeOnWheel( - '.cvat-canvas3d-topview', - -1000, - 'topview_before_wheel', - 'topview_after_wheel', - ); - testTopSideFrontChangeOnWheel( - '.cvat-canvas3d-sideview', - -1000, - 'sideview_before_wheel', - 'sideview_after_wheel', - ); + testTopSideFrontChangeOnWheel('.cvat-canvas3d-topview', 'topview_before_wheel', 'topview_after_wheel'); + testTopSideFrontChangeOnWheel('.cvat-canvas3d-sideview', 'sideview_before_wheel', 'sideview_after_wheel'); testTopSideFrontChangeOnWheel( '.cvat-canvas3d-frontview', - -1000, 'frontview_before_wheel', 'frontview_after_wheel', );