Support for Market-1501 dataset format (#2869)

* Add support for Market-1501 dataset format

* fix data access

* Update Datumaro version

* Add transforms

* Update Changelog
main
Anastasia Yasakova 5 years ago committed by GitHub
parent d62e176529
commit ce1666f6f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [VGGFace2](https://github.com/ox-vgg/vgg_face2) format support (<https://github.com/openvinotoolkit/cvat/pull/2865>) - [VGGFace2](https://github.com/ox-vgg/vgg_face2) format support (<https://github.com/openvinotoolkit/cvat/pull/2865>)
- [Backup/Restore guide](cvat/apps/documentation/backup_guide.md) (<https://github.com/openvinotoolkit/cvat/pull/2964>) - [Backup/Restore guide](cvat/apps/documentation/backup_guide.md) (<https://github.com/openvinotoolkit/cvat/pull/2964>)
- Label deletion from tasks and projects (<https://github.com/openvinotoolkit/cvat/pull/2881>) - Label deletion from tasks and projects (<https://github.com/openvinotoolkit/cvat/pull/2881>)
- [Market-1501](https://www.aitribune.com/dataset/2018051063) format support (<https://github.com/openvinotoolkit/cvat/pull/2869>)
### Changed ### Changed

@ -64,6 +64,7 @@ For more information about supported formats look at the
| [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) | X | X | | [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) | X | X |
| [WIDER Face](http://shuoyang1213.me/WIDERFACE/) | X | X | | [WIDER Face](http://shuoyang1213.me/WIDERFACE/) | X | X |
| [VGGFace2](https://github.com/ox-vgg/vgg_face2) | X | X | | [VGGFace2](https://github.com/ox-vgg/vgg_face2) | X | X |
| [Market-1501](https://www.aitribune.com/dataset/2018051063) | X | X |
## Deep learning serverless functions for automatic labeling ## Deep learning serverless functions for automatic labeling

@ -22,6 +22,7 @@
- [CamVid](#camvid) - [CamVid](#camvid)
- [WIDER Face](#widerface) - [WIDER Face](#widerface)
- [VGGFace2](#vggface2) - [VGGFace2](#vggface2)
- [Market-1501](#market1501)
## How to add a new annotation format support<a id="how-to-add"></a> ## How to add a new annotation format support<a id="how-to-add"></a>
@ -937,3 +938,39 @@ label1 <class1>
Uploaded file: a zip archive of the structure above Uploaded file: a zip archive of the structure above
- supported annotations: Rectangles, Points (landmarks - groups of 5 points) - supported annotations: Rectangles, Points (landmarks - groups of 5 points)
### [Market-1501](https://www.aitribune.com/dataset/2018051063)<a id="market1501" />
#### Market-1501 Dumper
Downloaded file: a zip archive of the following structure:
```bash
taskname.zip/
├── bounding_box_<any_subset_name>/
│ └── image_name_1.jpg
└── query
├── image_name_2.jpg
└── image_name_3.jpg
# if we keep only annotation:
taskname.zip/
└── images_<any_subset_name>.txt
# images_<any_subset_name>.txt
query/image_name_1.jpg
bounding_box_<any_subset_name>/image_name_2.jpg
bounding_box_<any_subset_name>/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`)

@ -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)

@ -97,3 +97,4 @@ import cvat.apps.dataset_manager.formats.imagenet
import cvat.apps.dataset_manager.formats.camvid import cvat.apps.dataset_manager.formats.camvid
import cvat.apps.dataset_manager.formats.widerface import cvat.apps.dataset_manager.formats.widerface
import cvat.apps.dataset_manager.formats.vggface2 import cvat.apps.dataset_manager.formats.vggface2
import cvat.apps.dataset_manager.formats.market1501

@ -284,6 +284,7 @@ class TaskExportTest(_DbTestBase):
'CamVid 1.0', 'CamVid 1.0',
'WiderFace 1.0', 'WiderFace 1.0',
'VGGFace2 1.0', 'VGGFace2 1.0',
'Market-1501 1.0',
}) })
def test_import_formats_query(self): def test_import_formats_query(self):
@ -304,6 +305,7 @@ class TaskExportTest(_DbTestBase):
'CamVid 1.0', 'CamVid 1.0',
'WiderFace 1.0', 'WiderFace 1.0',
'VGGFace2 1.0', 'VGGFace2 1.0',
'Market-1501 1.0',
}) })
def test_exports(self): def test_exports(self):
@ -346,6 +348,7 @@ class TaskExportTest(_DbTestBase):
('CamVid 1.0', 'camvid'), ('CamVid 1.0', 'camvid'),
('WiderFace 1.0', 'wider_face'), ('WiderFace 1.0', 'wider_face'),
('VGGFace2 1.0', 'vgg_face2'), ('VGGFace2 1.0', 'vgg_face2'),
('Market-1501 1.0', 'market1501'),
]: ]:
with self.subTest(format=format_name): with self.subTest(format=format_name):
if not dm.formats.registry.EXPORT_FORMATS[format_name].ENABLED: if not dm.formats.registry.EXPORT_FORMATS[format_name].ENABLED:

@ -2778,7 +2778,7 @@ class JobAnnotationAPITestCase(APITestCase):
def setUpTestData(cls): def setUpTestData(cls):
create_db_users(cls) create_db_users(cls)
def _create_task(self, owner, assignee): def _create_task(self, owner, assignee, annotation_format=""):
data = { data = {
"name": "my task #1", "name": "my task #1",
"owner_id": owner.id, "owner_id": owner.id,
@ -2833,6 +2833,30 @@ class JobAnnotationAPITestCase(APITestCase):
}, },
] ]
} }
if 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"]
},
]
}]
with ForceLogin(owner, self.client): with ForceLogin(owner, self.client):
response = self.client.post('/api/v1/tasks', data=data, format="json") response = self.client.post('/api/v1/tasks', data=data, format="json")
@ -3812,147 +3836,148 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
HTTP_201_CREATED = status.HTTP_401_UNAUTHORIZED HTTP_201_CREATED = status.HTTP_401_UNAUTHORIZED
def _get_initial_annotation(annotation_format): def _get_initial_annotation(annotation_format):
rectangle_tracks_with_attrs = [{ if annotation_format != "Market-1501 1.0":
"frame": 0, rectangle_tracks_with_attrs = [{
"label_id": task["labels"][0]["id"], "frame": 0,
"group": 0, "label_id": task["labels"][0]["id"],
"source": "manual", "group": 0,
"attributes": [ "source": "manual",
{ "attributes": [
"spec_id": task["labels"][0]["attributes"][0]["id"], {
"value": task["labels"][0]["attributes"][0]["values"][0] "spec_id": task["labels"][0]["attributes"][0]["id"],
}, "value": task["labels"][0]["attributes"][0]["values"][0]
], },
"shapes": [ ],
{ "shapes": [
"frame": 0, {
"points": [1.0, 2.1, 50.1, 30.22], "frame": 0,
"type": "rectangle", "points": [1.0, 2.1, 50.1, 30.22],
"occluded": False, "type": "rectangle",
"outside": False, "occluded": False,
"attributes": [ "outside": False,
{ "attributes": [
"spec_id": task["labels"][0]["attributes"][1]["id"], {
"value": task["labels"][0]["attributes"][1]["default_value"] "spec_id": task["labels"][0]["attributes"][1]["id"],
} "value": task["labels"][0]["attributes"][1]["default_value"]
] }
}, ]
{ },
"frame": 1, {
"points": [2.0, 2.1, 77.2, 36.22], "frame": 1,
"type": "rectangle", "points": [2.0, 2.1, 77.2, 36.22],
"occluded": True, "type": "rectangle",
"outside": False, "occluded": True,
"attributes": [ "outside": False,
{ "attributes": [
"spec_id": task["labels"][0]["attributes"][1]["id"], {
"value": task["labels"][0]["attributes"][1]["default_value"] "spec_id": task["labels"][0]["attributes"][1]["id"],
} "value": task["labels"][0]["attributes"][1]["default_value"]
] }
}, ]
{ },
"frame": 2, {
"points": [2.0, 2.1, 77.2, 36.22], "frame": 2,
"type": "rectangle", "points": [2.0, 2.1, 77.2, 36.22],
"occluded": True, "type": "rectangle",
"outside": True, "occluded": True,
"attributes": [ "outside": True,
{ "attributes": [
"spec_id": task["labels"][0]["attributes"][1]["id"], {
"value": task["labels"][0]["attributes"][1]["default_value"] "spec_id": task["labels"][0]["attributes"][1]["id"],
} "value": task["labels"][0]["attributes"][1]["default_value"]
] }
}, ]
] },
}] ]
rectangle_tracks_wo_attrs = [{ }]
"frame": 0, rectangle_tracks_wo_attrs = [{
"label_id": task["labels"][1]["id"], "frame": 0,
"group": 0, "label_id": task["labels"][1]["id"],
"source": "manual", "group": 0,
"attributes": [], "source": "manual",
"shapes": [ "attributes": [],
{ "shapes": [
"frame": 0, {
"attributes": [], "frame": 0,
"points": [1.0, 2.1, 50.2, 36.6], "attributes": [],
"type": "rectangle", "points": [1.0, 2.1, 50.2, 36.6],
"occluded": False, "type": "rectangle",
"outside": False, "occluded": False,
}, "outside": False,
{ },
"frame": 1, {
"attributes": [], "frame": 1,
"points": [1.0, 2.1, 51, 36.6], "attributes": [],
"type": "rectangle", "points": [1.0, 2.1, 51, 36.6],
"occluded": False, "type": "rectangle",
"outside": False "occluded": False,
}, "outside": False
{ },
"frame": 2, {
"attributes": [], "frame": 2,
"points": [1.0, 2.1, 51, 36.6], "attributes": [],
"type": "rectangle", "points": [1.0, 2.1, 51, 36.6],
"occluded": False, "type": "rectangle",
"outside": True, "occluded": False,
} "outside": True,
] }
}] ]
polygon_tracks_wo_attrs = [{ }]
"frame": 0, polygon_tracks_wo_attrs = [{
"label_id": task["labels"][1]["id"], "frame": 0,
"group": 0, "label_id": task["labels"][1]["id"],
"source": "manual", "group": 0,
"attributes": [], "source": "manual",
"shapes": [ "attributes": [],
{ "shapes": [
"frame": 0, {
"attributes": [], "frame": 0,
"points": [1.0, 2.1, 50.2, 36.6, 7.0, 10.0], "attributes": [],
"type": "polygon", "points": [1.0, 2.1, 50.2, 36.6, 7.0, 10.0],
"occluded": False, "type": "polygon",
"outside": False, "occluded": False,
}, "outside": False,
{ },
"frame": 1, {
"attributes": [], "frame": 1,
"points": [1.0, 2.1, 51, 36.6, 8.0, 11.0], "attributes": [],
"type": "polygon", "points": [1.0, 2.1, 51, 36.6, 8.0, 11.0],
"occluded": False, "type": "polygon",
"outside": False "occluded": False,
}, "outside": False
{ },
"frame": 2, {
"attributes": [], "frame": 2,
"points": [1.0, 2.1, 51, 36.6, 14.0, 15.0], "attributes": [],
"type": "polygon", "points": [1.0, 2.1, 51, 36.6, 14.0, 15.0],
"occluded": False, "type": "polygon",
"outside": True, "occluded": False,
} "outside": True,
] }
}] ]
}]
rectangle_shapes_with_attrs = [{ rectangle_shapes_with_attrs = [{
"frame": 0, "frame": 0,
"label_id": task["labels"][0]["id"], "label_id": task["labels"][0]["id"],
"group": 0, "group": 0,
"source": "manual", "source": "manual",
"attributes": [ "attributes": [
{ {
"spec_id": task["labels"][0]["attributes"][0]["id"], "spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0] "value": task["labels"][0]["attributes"][0]["values"][0]
}, },
{ {
"spec_id": task["labels"][0]["attributes"][1]["id"], "spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"] "value": task["labels"][0]["attributes"][1]["default_value"]
} }
], ],
"points": [1.0, 2.1, 10.6, 53.22], "points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle", "type": "rectangle",
"occluded": False, "occluded": False,
}] }]
rectangle_shapes_with_wider_attrs = [{ rectangle_shapes_with_wider_attrs = [{
"frame": 0, "frame": 0,
"label_id": task["labels"][2]["id"], "label_id": task["labels"][2]["id"],
"group": 0, "group": 0,
@ -3976,59 +4001,59 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
"occluded": False, "occluded": False,
}] }]
rectangle_shapes_wo_attrs = [{ rectangle_shapes_wo_attrs = [{
"frame": 1, "frame": 1,
"label_id": task["labels"][1]["id"], "label_id": task["labels"][1]["id"],
"group": 0, "group": 0,
"source": "manual", "source": "manual",
"attributes": [], "attributes": [],
"points": [2.0, 2.1, 40, 50.7], "points": [2.0, 2.1, 40, 50.7],
"type": "rectangle", "type": "rectangle",
"occluded": False, "occluded": False,
}] }]
polygon_shapes_wo_attrs = [{ polygon_shapes_wo_attrs = [{
"frame": 1, "frame": 1,
"label_id": task["labels"][1]["id"], "label_id": task["labels"][1]["id"],
"group": 0, "group": 0,
"source": "manual", "source": "manual",
"attributes": [], "attributes": [],
"points": [2.0, 2.1, 100, 30.22, 40, 77, 1, 3], "points": [2.0, 2.1, 100, 30.22, 40, 77, 1, 3],
"type": "polygon", "type": "polygon",
"occluded": False, "occluded": False,
}] }]
polygon_shapes_with_attrs = [{ polygon_shapes_with_attrs = [{
"frame": 2, "frame": 2,
"label_id": task["labels"][0]["id"], "label_id": task["labels"][0]["id"],
"group": 1, "group": 1,
"source": "manual", "source": "manual",
"attributes": [ "attributes": [
{ {
"spec_id": task["labels"][0]["attributes"][0]["id"], "spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][1] "value": task["labels"][0]["attributes"][0]["values"][1]
}, },
{ {
"spec_id": task["labels"][0]["attributes"][1]["id"], "spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"] "value": task["labels"][0]["attributes"][1]["default_value"]
} }
], ],
"points": [20.0, 0.1, 10, 3.22, 4, 7, 10, 30, 1, 2, 4.44, 5.55], "points": [20.0, 0.1, 10, 3.22, 4, 7, 10, 30, 1, 2, 4.44, 5.55],
"type": "polygon", "type": "polygon",
"occluded": True, "occluded": True,
}, },
{ {
"frame": 2, "frame": 2,
"label_id": task["labels"][1]["id"], "label_id": task["labels"][1]["id"],
"group": 1, "group": 1,
"source": "manual", "source": "manual",
"attributes": [], "attributes": [],
"points": [4, 7, 10, 30, 4, 5.55], "points": [4, 7, 10, 30, 4, 5.55],
"type": "polygon", "type": "polygon",
"occluded": False, "occluded": False,
}] }]
points_wo_attrs = [{ points_wo_attrs = [{
"frame": 1, "frame": 1,
"label_id": task["labels"][1]["id"], "label_id": task["labels"][1]["id"],
"group": 0, "group": 0,
@ -4039,36 +4064,36 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
"occluded": False, "occluded": False,
}] }]
tags_wo_attrs = [{ tags_wo_attrs = [{
"frame": 2, "frame": 2,
"label_id": task["labels"][1]["id"], "label_id": task["labels"][1]["id"],
"group": 0, "group": 0,
"source": "manual", "source": "manual",
"attributes": [], "attributes": [],
}] }]
tags_with_attrs = [{ tags_with_attrs = [{
"frame": 1, "frame": 1,
"label_id": task["labels"][0]["id"], "label_id": task["labels"][0]["id"],
"group": 3, "group": 3,
"source": "manual", "source": "manual",
"attributes": [ "attributes": [
{ {
"spec_id": task["labels"][0]["attributes"][0]["id"], "spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][1] "value": task["labels"][0]["attributes"][0]["values"][1]
}, },
{ {
"spec_id": task["labels"][0]["attributes"][1]["id"], "spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"] "value": task["labels"][0]["attributes"][1]["default_value"]
} }
], ],
}] }]
annotations = { annotations = {
"version": 0, "version": 0,
"tags": [], "tags": [],
"shapes": [], "shapes": [],
"tracks": [], "tracks": [],
} }
if annotation_format == "CVAT for video 1.1": if annotation_format == "CVAT for video 1.1":
annotations["tracks"] = rectangle_tracks_with_attrs \ annotations["tracks"] = rectangle_tracks_with_attrs \
+ rectangle_tracks_wo_attrs \ + rectangle_tracks_wo_attrs \
@ -4133,6 +4158,29 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
annotations["shapes"] = points_wo_attrs \ annotations["shapes"] = points_wo_attrs \
+ rectangle_shapes_wo_attrs + rectangle_shapes_wo_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: else:
raise Exception("Unknown format {}".format(annotation_format)) raise Exception("Unknown format {}".format(annotation_format))
@ -4169,7 +4217,7 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
with self.subTest(export_format=export_format, with self.subTest(export_format=export_format,
import_format=import_format): import_format=import_format):
# 1. create task # 1. create task
task, jobs = self._create_task(owner, assignee) task, jobs = self._create_task(owner, assignee, import_format)
# 2. add annotation # 2. add annotation
data = _get_initial_annotation(export_format) data = _get_initial_annotation(export_format)

Loading…
Cancel
Save