未验证 提交 4bdaf3c0 编写于 作者: K Kirill Sizov 提交者: GitHub

Add Cityscapes format (#3758)

上级 cc1b8190
......@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add a tutorial on attaching cloud storage AWS-S3 (<https://github.com/openvinotoolkit/cvat/pull/3745>)
and Azure Blob Container (<https://github.com/openvinotoolkit/cvat/pull/3778>)
- The feature to remove annotations in a specified range of frames (<https://github.com/openvinotoolkit/cvat/pull/3617>)
- Add Cityscapes format (<https://github.com/openvinotoolkit/cvat/pull/3758>)
- Add Open Images V6 format (<https://github.com/openvinotoolkit/cvat/pull/3679>)
### Changed
......
# Copyright (C) 2021 Intel Corporation
#
# SPDX-License-Identifier: MIT
import os.path as osp
from tempfile import TemporaryDirectory
from datumaro.components.dataset import Dataset
from datumaro.plugins.cityscapes_format import write_label_map
from pyunpack import Archive
from cvat.apps.dataset_manager.bindings import (GetCVATDataExtractor,
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='Cityscapes', ext='ZIP', version='1.0')
def _export(dst_file, instance_data, save_images=False):
dataset = Dataset.from_extractors(GetCVATDataExtractor(
instance_data, include_images=save_images), env=dm_env)
dataset.transform('polygons_to_masks')
dataset.transform('boxes_to_masks')
dataset.transform('merge_instance_segments')
with TemporaryDirectory() as temp_dir:
dataset.export(temp_dir, 'cityscapes', save_images=save_images,
apply_colormap=True, label_map={label: info[0]
for label, info in make_colormap(instance_data).items()})
make_zip_archive(temp_dir, dst_file)
@importer(name='Cityscapes', ext='ZIP', version='1.0')
def _import(src_file, instance_data):
with TemporaryDirectory() as tmp_dir:
Archive(src_file.name).extractall(tmp_dir)
labelmap_file = osp.join(tmp_dir, 'label_colors.txt')
if not osp.isfile(labelmap_file):
colormap = {label: info[0]
for label, info in make_colormap(instance_data).items()}
write_label_map(labelmap_file, colormap)
dataset = Dataset.import_from(tmp_dir, 'cityscapes', env=dm_env)
dataset.transform('masks_to_polygons')
import_dm_annotations(dataset, instance_data)
......@@ -121,5 +121,6 @@ import cvat.apps.dataset_manager.formats.market1501
import cvat.apps.dataset_manager.formats.icdar
import cvat.apps.dataset_manager.formats.velodynepoint
import cvat.apps.dataset_manager.formats.pointcloud
import cvat.apps.dataset_manager.formats.cityscapes
import cvat.apps.dataset_manager.formats.openimages
......@@ -1197,5 +1197,23 @@
}
],
"tracks": []
},
"Cityscapes 1.0": {
"version": 0,
"tags": [],
"shapes": [
{
"type": "polygon",
"occluded": false,
"z_order": 0,
"points": [11.9, 21.5, 35.2, 21.9, 33.6, 31.9, 12.4, 30.47],
"frame": 0,
"label_id": null,
"group": 0,
"source": "manual",
"attributes": []
}
],
"tracks": []
}
}
......@@ -54,7 +54,7 @@
}
]
},
"icdar_localization_and_recognition": {
"ICDAR Localization 1.0": {
"name": "icdar localization/recogntion task",
"overlap": 0,
"segment_size": 100,
......@@ -74,7 +74,27 @@
}
]
},
"icdar_segmentation": {
"ICDAR Recognition 1.0": {
"name": "icdar localization/recogntion task",
"overlap": 0,
"segment_size": 100,
"owner_id": 1,
"assignee_id": 2,
"labels": [
{
"name": "icdar",
"attributes": [
{
"name": "text",
"mutable": false,
"input_type": "text",
"values": ["word_1", "word_2", "word_3"]
}
]
}
]
},
"ICDAR Segmentation 1.0": {
"name": "icdar segmentation task",
"overlap": 0,
"segment_size": 100,
......@@ -112,7 +132,7 @@
}
]
},
"market1501": {
"Market-1501 1.0": {
"name": "market1501 task",
"overlap": 0,
"segment_size": 100,
......@@ -144,6 +164,38 @@
}
]
},
"Cityscapes 1.0": {
"name": "cityscapes task",
"overlap": 0,
"segment_size": 100,
"owner_id": 1,
"assignee_id": 2,
"labels": [
{
"name": "car",
"color": "#2080c0",
"attributes": [
{
"name": "is_crowd",
"mutable": false,
"input_type": "checkbox",
"default_value": "false",
"values": ["false", "true"]
}
]
},
{
"name": "person",
"color": "#c06060",
"attributes": []
},
{
"name": "background",
"color": "#000000",
"attributes": []
}
]
},
"wrong_checkbox_value": {
"name": "wrong checkbox value task",
"overlap": 0,
......
......@@ -27,7 +27,7 @@ from cvat.apps.dataset_manager.util import make_zip_archive
from cvat.apps.engine.models import Task
def generate_image_file(filename, size=(100, 50)):
def generate_image_file(filename, size=(100, 100)):
f = BytesIO()
image = Image.new('RGB', size=size)
image.save(f, 'jpeg')
......@@ -296,6 +296,7 @@ class TaskExportTest(_DbTestBase):
'ICDAR Segmentation 1.0',
'Kitti Raw Format 1.0',
'Sly Point Cloud Format 1.0',
'Cityscapes 1.0',
'Open Images V6 1.0'
})
......@@ -323,6 +324,7 @@ class TaskExportTest(_DbTestBase):
'ICDAR Segmentation 1.0',
'Kitti Raw Format 1.0',
'Sly Point Cloud Format 1.0',
'Cityscapes 1.0',
'Open Images V6 1.0',
'Datumaro 1.0',
'Datumaro 3D 1.0'
......@@ -372,6 +374,7 @@ class TaskExportTest(_DbTestBase):
('ICDAR Recognition 1.0', 'icdar_word_recognition'),
('ICDAR Localization 1.0', 'icdar_text_localization'),
('ICDAR Segmentation 1.0', 'icdar_text_segmentation'),
# ('Cityscapes 1.0', 'cityscapes'), does not support, empty annotations
]:
with self.subTest(format=format_name):
if not dm.formats.registry.EXPORT_FORMATS[format_name].ENABLED:
......@@ -382,16 +385,6 @@ class TaskExportTest(_DbTestBase):
def check(file_path):
def load_dataset(src):
if importer_name == 'datumaro_project':
project = datumaro.components.project. \
Project.load(src)
# NOTE: can't import cvat.utils.cli
# for whatever reason, so remove the dependency
#
project.config.remove('sources')
return project.make_dataset()
return datumaro.components.dataset. \
Dataset.import_from(src, importer_name, env=dm_env)
......@@ -702,6 +695,10 @@ class TaskAnnotationsImportTest(_DbTestBase):
}
]
},
{
"name": "background",
"attributes": [],
},
{"name": "person"}
]
......@@ -838,7 +835,7 @@ class TaskAnnotationsImportTest(_DbTestBase):
{
"frame": 0,
"attributes": [],
"points": [1.0, 2.1, 10.6, 53.22, 100, 300.222],
"points": [1.0, 2.1, 10.6, 53.22, 90, 90.222],
"type": "polygon",
"occluded": False,
"outside": False
......@@ -879,10 +876,10 @@ class TaskAnnotationsImportTest(_DbTestBase):
elif annotation_format == "MOTS PNG 1.0":
tracks = [track_wo_attrs]
else:
shapes = [rectangle_shape_wo_attrs,
shapes = [rectangle_shape_wo_attrs, \
rectangle_shape_with_attrs]
tags = tag_wo_attrs
tracks = track_wo_attrs
tags = [tag_wo_attrs]
tracks = [track_wo_attrs]
annotations = {
"version": 0,
......
......@@ -355,12 +355,12 @@ class TaskDumpUploadTest(_DbTestBase):
with self.subTest(format=dump_format_name):
images = self._generate_task_images(3)
# create task with annotations
if dump_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], images)
elif dump_format_name in ["ICDAR Localization 1.0", "ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], images)
elif dump_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], images)
if dump_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[dump_format_name], images)
else:
task = self._create_task(tasks["main"], images)
task_id = task["id"]
......@@ -368,7 +368,8 @@ class TaskDumpUploadTest(_DbTestBase):
"MOT 1.1", "MOTS PNG 1.0", \
"PASCAL VOC 1.1", "Segmentation mask 1.1", \
"TFRecord 1.0", "YOLO 1.1", "ImageNet 1.0", \
"WiderFace 1.0", "VGGFace2 1.0", "Datumaro 1.0"\
"WiderFace 1.0", "VGGFace2 1.0", "Cityscapes 1.0", \
"Datumaro 1.0"\
]:
self._create_annotations(task, dump_format_name, "default")
else:
......@@ -417,12 +418,12 @@ class TaskDumpUploadTest(_DbTestBase):
for user, edata in list(expected.items()):
# remove all annotations from task (create new task without annotation)
images = self._generate_task_images(3)
if upload_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], images)
elif upload_format_name in ["ICDAR Localization 1.0", "ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], images)
elif upload_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], images)
if upload_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[upload_format_name], images)
else:
task = self._create_task(tasks["main"], images)
task_id = task["id"]
......@@ -460,12 +461,12 @@ class TaskDumpUploadTest(_DbTestBase):
with self.subTest(format=dump_format_name):
# create task with annotations
video = self._generate_task_videos(1)
if dump_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], video)
elif dump_format_name in ["ICDAR Localization 1.0", "ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], video)
elif dump_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], video)
if dump_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[dump_format_name], video)
else:
task = self._create_task(tasks["main"], video)
task_id = task["id"]
......@@ -474,7 +475,7 @@ class TaskDumpUploadTest(_DbTestBase):
"MOT 1.1", "MOTS PNG 1.0", \
"PASCAL VOC 1.1", "Segmentation mask 1.1", \
"TFRecord 1.0", "YOLO 1.1", "ImageNet 1.0", \
"WiderFace 1.0", "VGGFace2 1.0", \
"WiderFace 1.0", "VGGFace2 1.0", "Cityscapes 1.0" \
]:
self._create_annotations(task, dump_format_name, "default")
else:
......@@ -521,12 +522,12 @@ class TaskDumpUploadTest(_DbTestBase):
for user, edata in list(expected.items()):
# remove all annotations from task (create new task without annotation)
video = self._generate_task_videos(1)
if upload_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], video)
elif upload_format_name in ["ICDAR Localization 1.0", "ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], video)
elif upload_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], video)
if upload_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[upload_format_name], video)
else:
task = self._create_task(tasks["main"], video)
task_id = task["id"]
......@@ -802,12 +803,12 @@ class TaskDumpUploadTest(_DbTestBase):
with self.subTest(format=dump_format_name):
images = self._generate_task_images(3)
# create task with annotations
if dump_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], images)
elif dump_format_name in ["ICDAR Localization 1.0", "ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], images)
elif dump_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], images)
if dump_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[dump_format_name], images)
else:
task = self._create_task(tasks["main"], images)
task_id = task["id"]
......@@ -871,6 +872,7 @@ class TaskDumpUploadTest(_DbTestBase):
with self.subTest(format=upload_format_name):
if upload_format_name in [
"MOTS PNG 1.0", # issue #2925 and changed points values
"Cityscapes 1.0" # formats doesn't support empty annotations
]:
self.skipTest("Format is fail")
images = self._generate_task_images(3)
......@@ -899,25 +901,26 @@ class TaskDumpUploadTest(_DbTestBase):
"MOTS PNG 1.0", # issue #2925 and changed points values
'Kitti Raw Format 1.0',
'Sly Point Cloud Format 1.0',
'Datumaro 3D 1.0'
'Datumaro 3D 1.0',
"Cityscapes 1.0" # expanding annotations due to background mask
]:
self.skipTest("Format is fail")
images = self._generate_task_images(3)
if dump_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], images)
elif dump_format_name in ["ICDAR Localization 1.0", "ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], images)
elif dump_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], images)
if dump_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[dump_format_name], images)
else:
task = self._create_task(tasks["main"], images)
task_id = task["id"]
if dump_format_name in [
"MOT 1.1", "MOTS PNG 1.0", \
"PASCAL VOC 1.1", "Segmentation mask 1.1", \
"TFRecord 1.0", "YOLO 1.1", "ImageNet 1.0", \
"WiderFace 1.0", "VGGFace2 1.0", "Datumaro 1.0",\
"Open Images V6 1.0" \
"MOT 1.1", "MOTS PNG 1.0",
"PASCAL VOC 1.1", "Segmentation mask 1.1",
"TFRecord 1.0", "YOLO 1.1", "ImageNet 1.0",
"WiderFace 1.0", "VGGFace2 1.0", "Cityscapes 1.0",
"Datumaro 1.0", "Open Images V6 1.0"
]:
self._create_annotations(task, dump_format_name, "default")
else:
......@@ -1009,19 +1012,19 @@ class TaskDumpUploadTest(_DbTestBase):
"Open Images V6 1.0", # changed points values
'Kitti Raw Format 1.0',
'Sly Point Cloud Format 1.0',
'Cityscapes 1.0', # changed points value
'Datumaro 3D 1.0'
]:
self.skipTest("Format is fail")
# create task
images = self._generate_task_images(3)
if dump_format_name == "Market-1501 1.0":
task = self._create_task(tasks["market1501"], images)
elif dump_format_name in ["ICDAR Localization 1.0",
"ICDAR Recognition 1.0"]:
task = self._create_task(tasks["icdar_localization_and_recognition"], images)
elif dump_format_name == "ICDAR Segmentation 1.0":
task = self._create_task(tasks["icdar_segmentation"], images)
if dump_format_name in [
"Market-1501 1.0", "Cityscapes 1.0", \
"ICDAR Localization 1.0", "ICDAR Recognition 1.0", \
"ICDAR Segmentation 1.0"
]:
task = self._create_task(tasks[dump_format_name], images)
else:
task = self._create_task(tasks["main"], images)
......
......@@ -4810,7 +4810,9 @@ class TaskAnnotationAPITestCase(JobAnnotationAPITestCase):
annotations["tags"] = tags_wo_attrs
annotations["shapes"] = points_wo_attrs \
+ rectangle_shapes_wo_attrs
elif annotation_format == "Cityscapes 1.0":
annotations["shapes"] = points_wo_attrs \
+ rectangle_shapes_wo_attrs
elif annotation_format == "Open Images V6 1.0":
annotations["tags"] = tags_wo_attrs
annotations["shapes"] = rectangle_shapes_wo_attrs \
......
---
linkTitle: 'Cityscapes'
weight: 16
---
# [Cityscapes](https://www.cityscapes-dataset.com/login/)
- [Format specification](https://github.com/mcordts/cityscapesScripts#the-cityscapes-dataset)
- Supported annotations
- Polygons (segmentation task)
- Supported attributes
- 'is_crowd' (boolean, should be defined for labels as `checkbox` -es)
Specifies if the annotation label can distinguish between different instances.
If False, the annotation id field encodes the instance id.
# Cityscapes export
Downloaded file: a zip archive of the following structure:
```
.
├── label_color.txt
├── gtFine
│ ├── <subset_name>
│ │ └── <city_name>
│ │ ├── image_0_gtFine_instanceIds.png
│ │ ├── image_0_gtFine_color.png
│ │ ├── image_0_gtFine_labelIds.png
│ │ ├── image_1_gtFine_instanceIds.png
│ │ ├── image_1_gtFine_color.png
│ │ ├── image_1_gtFine_labelIds.png
│ │ ├── ...
└── imgsFine # if saving images was requested
└── leftImg8bit
├── <subset_name>
│ └── <city_name>
│ ├── image_0_leftImg8bit.png
│ ├── image_1_leftImg8bit.png
│ ├── ...
```
- `label_color.txt` a file that describes the color for each label
```
# label_color.txt example
# r g b label_name
0 0 0 background
0 255 0 tree
...
```
- `*_gtFine_color.png` class labels encoded by its color.
- `*_gtFine_labelIds.png` class labels are encoded by its index.
- `*_gtFine_instanceIds.png` class and instance labels encoded
by an instance ID. The pixel values encode class and the individual instance:
the integer part of a division by 1000 of each ID provides class ID,
the remainder is the instance ID. If a certain annotation describes multiple
instances, then the pixels have the regular ID of that class
# Cityscapes annotations import
Uploaded file: a zip archive with the following structure:
```
.
├── label_color.txt # optional
└── gtFine
└── <city_name>
├── image_0_gtFine_instanceIds.png
├── image_1_gtFine_instanceIds.png
├── ...
```
# Creating task with Cityscapes dataset
Create a task with the labels you need
or you can use the labels and colors of the original dataset.
To work with the Cityscapes format, you must have a black color label
for the background.
Original Cityscapes color map:
<details>
```JSON
[
{"name": "unlabeled", "color": "#000000", "attributes": []},
{"name": "egovehicle", "color": "#000000", "attributes": []},
{"name": "rectificationborder", "color": "#000000", "attributes": []},
{"name": "outofroi", "color": "#000000", "attributes": []},
{"name": "static", "color": "#000000", "attributes": []},
{"name": "dynamic", "color": "#6f4a00", "attributes": []},
{"name": "ground", "color": "#510051", "attributes": []},
{"name": "road", "color": "#804080", "attributes": []},
{"name": "sidewalk", "color": "#f423e8", "attributes": []},
{"name": "parking", "color": "#faaaa0", "attributes": []},
{"name": "railtrack", "color": "#e6968c", "attributes": []},
{"name": "building", "color": "#464646", "attributes": []},
{"name": "wall", "color": "#66669c", "attributes": []},
{"name": "fence", "color": "#be9999", "attributes": []},
{"name": "guardrail", "color": "#b4a5b4", "attributes": []},
{"name": "bridge", "color": "#966464", "attributes": []},
{"name": "tunnel", "color": "#96785a", "attributes": []},
{"name": "pole", "color": "#999999", "attributes": []},
{"name": "polegroup", "color": "#999999", "attributes": []},
{"name": "trafficlight", "color": "#faaa1e", "attributes": []},
{"name": "trafficsign", "color": "#dcdc00", "attributes": []},
{"name": "vegetation", "color": "#6b8e23", "attributes": []},
{"name": "terrain", "color": "#98fb98", "attributes": []},
{"name": "sky", "color": "#4682b4", "attributes": []},
{"name": "person", "color": "#dc143c", "attributes": []},
{"name": "rider", "color": "#ff0000", "attributes": []},
{"name": "car", "color": "#00008e", "attributes": []},
{"name": "truck", "color": "#000046", "attributes": []},
{"name": "bus", "color": "#003c64", "attributes": []},
{"name": "caravan", "color": "#00005a", "attributes": []},
{"name": "trailer", "color": "#00006e", "attributes": []},
{"name": "train", "color": "#005064", "attributes": []},
{"name": "motorcycle", "color": "#0000e6", "attributes": []},
{"name": "bicycle", "color": "#770b20", "attributes": []},
{"name": "licenseplate", "color": "#00000e", "attributes": []}
]
```
<details>
Upload images when creating a task:
```
images.zip/
├── image_0.jpg
├── image_1.jpg
├── ...
```
After creating the task, upload the Cityscapes annotations as described
in the previous section.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册