Update Datumaro version (#4984)

main
Anastasia Yasakova 3 years ago committed by GitHub
parent be5f9773d0
commit 9e67bcb53c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- `api/docs`, `api/swagger`, `api/schema` endpoints now allow unauthorized access (<https://github.com/opencv/cvat/pull/4928>) - `api/docs`, `api/swagger`, `api/schema` endpoints now allow unauthorized access (<https://github.com/opencv/cvat/pull/4928>)
- Datumaro version (<https://github.com/opencv/cvat/pull/4984>)
### Deprecated ### Deprecated
- TDB - TDB
@ -20,9 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- TDB - TDB
### Fixed ### Fixed
- Removed a possibly duplicated encodeURI() calls in `server-proxy.ts` to prevent doubly encoding - Removed a possibly duplicated encodeURI() calls in `server-proxy.ts` to prevent doubly encoding
non-ascii paths while adding files from "Connected file share" (issue #4428) non-ascii paths while adding files from "Connected file share" (issue #4428)
- Removed unnecessary volumes defined in docker-compose.serverless.yml - Removed unnecessary volumes defined in docker-compose.serverless.yml
(<https://github.com/openvinotoolkit/cvat/pull/4659>) (<https://github.com/openvinotoolkit/cvat/pull/4659>)
### Security ### Security

@ -1,5 +1,6 @@
# Copyright (C) 2019-2022 Intel Corporation # Copyright (C) 2019-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -12,13 +13,10 @@ from types import SimpleNamespace
from typing import (Any, Callable, DefaultDict, Dict, List, Literal, Mapping, from typing import (Any, Callable, DefaultDict, Dict, List, Literal, Mapping,
NamedTuple, OrderedDict, Set, Tuple, Union) NamedTuple, OrderedDict, Set, Tuple, Union)
import datumaro.components.annotation as datum_annotation import datumaro as dm
import datumaro.components.extractor as datum_extractor
import rq import rq
from attr import attrib, attrs from attr import attrib, attrs
from datumaro.components.dataset import Dataset from datumaro.components.media import PointCloud
from datumaro.util import cast
from datumaro.util.image import ByteImage, Image
from django.utils import timezone from django.utils import timezone
from cvat.apps.dataset_manager.formats.utils import get_label_color from cvat.apps.dataset_manager.formats.utils import get_label_color
@ -241,7 +239,7 @@ class TaskData(InstanceLabelData):
("bugtracker", db_task.bug_tracker), ("bugtracker", db_task.bug_tracker),
("created", str(timezone.localtime(db_task.created_date))), ("created", str(timezone.localtime(db_task.created_date))),
("updated", str(timezone.localtime(db_task.updated_date))), ("updated", str(timezone.localtime(db_task.updated_date))),
("subset", db_task.subset or datum_extractor.DEFAULT_SUBSET_NAME), ("subset", db_task.subset or dm.DEFAULT_SUBSET_NAME),
("start_frame", str(db_task.data.start_frame)), ("start_frame", str(db_task.data.start_frame)),
("stop_frame", str(db_task.data.stop_frame)), ("stop_frame", str(db_task.data.stop_frame)),
("frame_filter", db_task.data.frame_filter), ("frame_filter", db_task.data.frame_filter),
@ -781,7 +779,7 @@ class ProjectData(InstanceLabelData):
) for db_task in self._db_tasks.values() ) for db_task in self._db_tasks.values()
]), ]),
("subsets", '\n'.join([s if s else datum_extractor.DEFAULT_SUBSET_NAME for s in self._subsets])), ("subsets", '\n'.join([s if s else dm.DEFAULT_SUBSET_NAME for s in self._subsets])),
("owner", OrderedDict([ ("owner", OrderedDict([
("username", self._db_project.owner.username), ("username", self._db_project.owner.username),
@ -1024,7 +1022,7 @@ class ProjectData(InstanceLabelData):
def _get_filename(path): def _get_filename(path):
return osp.splitext(path)[0] return osp.splitext(path)[0]
def match_frame(self, path: str, subset: str=datum_extractor.DEFAULT_SUBSET_NAME, root_hint: str=None, path_has_ext: bool=True): def match_frame(self, path: str, subset: str=dm.DEFAULT_SUBSET_NAME, root_hint: str=None, path_has_ext: bool=True):
if path_has_ext: if path_has_ext:
path = self._get_filename(path) path = self._get_filename(path)
match_task, match_frame = self._frame_mapping.get((subset, path), (None, None)) match_task, match_frame = self._frame_mapping.get((subset, path), (None, None))
@ -1040,11 +1038,11 @@ class ProjectData(InstanceLabelData):
return frame_number return frame_number
return None return None
def split_dataset(self, dataset: Dataset): def split_dataset(self, dataset: dm.Dataset):
for task_data in self.task_data: for task_data in self.task_data:
if task_data._db_task.id not in self.new_tasks: if task_data._db_task.id not in self.new_tasks:
continue continue
subset_dataset: Dataset = dataset.subsets()[task_data.db_task.subset].as_dataset() subset_dataset: dm.Dataset = dataset.subsets()[task_data.db_task.subset].as_dataset()
yield subset_dataset, task_data yield subset_dataset, task_data
def add_labels(self, labels: List[dict]): def add_labels(self, labels: List[dict]):
@ -1060,7 +1058,7 @@ class ProjectData(InstanceLabelData):
self._project_annotation.add_task(task, files, self) self._project_annotation.add_task(task, files, self)
class CVATDataExtractorMixin: class CVATDataExtractorMixin:
def __init__(self): def __init__(self, media_type=dm.Image):
super().__init__() super().__init__()
def categories(self) -> dict: def categories(self) -> dict:
@ -1068,28 +1066,27 @@ class CVATDataExtractorMixin:
@staticmethod @staticmethod
def _load_categories(labels: list): def _load_categories(labels: list):
categories: Dict[datum_annotation.AnnotationType, categories: Dict[dm.AnnotationType,
datum_annotation.Categories] = {} dm.Categories] = {}
label_categories = datum_annotation.LabelCategories(attributes=['occluded']) label_categories = dm.LabelCategories(attributes=['occluded'])
point_categories = datum_annotation.PointsCategories() point_categories = dm.PointsCategories()
for _, label in labels: for _, label in labels:
if label.get('parent') is None: label_id = label_categories.add(label['name'], label.get('parent'))
label_id = label_categories.add(label['name']) for _, attr in label['attributes']:
for _, attr in label['attributes']: label_categories.attributes.add(attr['name'])
label_categories.attributes.add(attr['name'])
if label['type'] == str(LabelType.SKELETON): if label['type'] == str(LabelType.SKELETON):
labels_from = list(map(int, re.findall(r'data-node-from="(\d+)"', label['svg']))) labels_from = list(map(int, re.findall(r'data-node-from="(\d+)"', label['svg'])))
labels_to = list(map(int, re.findall(r'data-node-to="(\d+)"', label['svg']))) labels_to = list(map(int, re.findall(r'data-node-to="(\d+)"', label['svg'])))
sublabels = re.findall(r'data-label-name="(\w+)"', label['svg']) sublabels = re.findall(r'data-label-name="(\w+)"', label['svg'])
joints = zip(labels_from, labels_to) joints = zip(labels_from, labels_to)
point_categories.add(label_id, sublabels, joints) point_categories.add(label_id, sublabels, joints)
categories[datum_annotation.AnnotationType.label] = label_categories categories[dm.AnnotationType.label] = label_categories
categories[datum_annotation.AnnotationType.points] = point_categories categories[dm.AnnotationType.points] = point_categories
return categories return categories
@ -1103,7 +1100,7 @@ class CVATDataExtractorMixin:
def _read_cvat_anno(self, cvat_frame_anno: Union[ProjectData.Frame, TaskData.Frame], labels: list): def _read_cvat_anno(self, cvat_frame_anno: Union[ProjectData.Frame, TaskData.Frame], labels: list):
categories = self.categories() categories = self.categories()
label_cat = categories[datum_annotation.AnnotationType.label] label_cat = categories[dm.AnnotationType.label]
def map_label(name): return label_cat.find(name)[0] def map_label(name): return label_cat.find(name)[0]
label_attrs = { label_attrs = {
label['name']: label['attributes'] label['name']: label['attributes']
@ -1113,9 +1110,9 @@ class CVATDataExtractorMixin:
return convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label) return convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label)
class CvatTaskDataExtractor(datum_extractor.SourceExtractor, CVATDataExtractorMixin): class CvatTaskDataExtractor(dm.SourceExtractor, CVATDataExtractorMixin):
def __init__(self, task_data, include_images=False, format_type=None, dimension=DimensionType.DIM_2D): def __init__(self, task_data, include_images=False, format_type=None, dimension=DimensionType.DIM_2D):
super().__init__() super().__init__(media_type=dm.Image if dimension == DimensionType.DIM_2D else PointCloud)
self._categories = self._load_categories(task_data.meta['task']['labels']) self._categories = self._load_categories(task_data.meta['task']['labels'])
self._user = self._load_user_info(task_data.meta['task']) if dimension == DimensionType.DIM_3D else {} self._user = self._load_user_info(task_data.meta['task']) if dimension == DimensionType.DIM_3D else {}
self._dimension = dimension self._dimension = dimension
@ -1148,14 +1145,14 @@ class CvatTaskDataExtractor(datum_extractor.SourceExtractor, CVATDataExtractorMi
loader = lambda _: frame_provider.get_frame(i, loader = lambda _: frame_provider.get_frame(i,
quality=frame_provider.Quality.ORIGINAL, quality=frame_provider.Quality.ORIGINAL,
out_type=frame_provider.Type.NUMPY_ARRAY)[0] out_type=frame_provider.Type.NUMPY_ARRAY)[0]
return Image(loader=loader, **kwargs) return dm.Image(data=loader, **kwargs)
else: else:
# for images use encoded data to avoid recoding # for images use encoded data to avoid recoding
def _make_image(i, **kwargs): def _make_image(i, **kwargs):
loader = lambda _: frame_provider.get_frame(i, loader = lambda _: frame_provider.get_frame(i,
quality=frame_provider.Quality.ORIGINAL, quality=frame_provider.Quality.ORIGINAL,
out_type=frame_provider.Type.BUFFER)[0].getvalue() out_type=frame_provider.Type.BUFFER)[0].getvalue()
return ByteImage(data=loader, **kwargs) return dm.ByteImage(data=loader, **kwargs)
for frame_data in task_data.group_by_frame(include_empty=True): for frame_data in task_data.group_by_frame(include_empty=True):
image_args = { image_args = {
@ -1168,13 +1165,13 @@ class CvatTaskDataExtractor(datum_extractor.SourceExtractor, CVATDataExtractorMi
elif include_images: elif include_images:
dm_image = _make_image(frame_data.idx, **image_args) dm_image = _make_image(frame_data.idx, **image_args)
else: else:
dm_image = Image(**image_args) dm_image = dm.Image(**image_args)
dm_anno = self._read_cvat_anno(frame_data, task_data.meta['task']['labels']) dm_anno = self._read_cvat_anno(frame_data, task_data.meta['task']['labels'])
if dimension == DimensionType.DIM_2D: if dimension == DimensionType.DIM_2D:
dm_item = datum_extractor.DatasetItem( dm_item = dm.DatasetItem(
id=osp.splitext(frame_data.name)[0], id=osp.splitext(frame_data.name)[0],
annotations=dm_anno, image=dm_image, annotations=dm_anno, media=dm_image,
attributes={'frame': frame_data.frame attributes={'frame': frame_data.frame
}) })
elif dimension == DimensionType.DIM_3D: elif dimension == DimensionType.DIM_3D:
@ -1188,9 +1185,9 @@ class CvatTaskDataExtractor(datum_extractor.SourceExtractor, CVATDataExtractorMi
attributes["labels"].append({"label_id": idx, "name": label["name"], "color": label["color"], "type": label["type"]}) attributes["labels"].append({"label_id": idx, "name": label["name"], "color": label["color"], "type": label["type"]})
attributes["track_id"] = -1 attributes["track_id"] = -1
dm_item = datum_extractor.DatasetItem( dm_item = dm.DatasetItem(
id=osp.splitext(osp.split(frame_data.name)[-1])[0], id=osp.splitext(osp.split(frame_data.name)[-1])[0],
annotations=dm_anno, point_cloud=dm_image[0], related_images=dm_image[1], annotations=dm_anno, media=PointCloud(dm_image[0]), related_images=dm_image[1],
attributes=attributes attributes=attributes
) )
@ -1200,24 +1197,24 @@ class CvatTaskDataExtractor(datum_extractor.SourceExtractor, CVATDataExtractorMi
def _read_cvat_anno(self, cvat_frame_anno: TaskData.Frame, labels: list): def _read_cvat_anno(self, cvat_frame_anno: TaskData.Frame, labels: list):
categories = self.categories() categories = self.categories()
label_cat = categories[datum_annotation.AnnotationType.label] label_cat = categories[dm.AnnotationType.label]
def map_label(name): return label_cat.find(name)[0] def map_label(name, parent=""): return label_cat.find(name, parent)[0]
label_attrs = { label_attrs = {
label['name']: label['attributes'] label.get("parent", "") + label['name']: label['attributes']
for _, label in labels for _, label in labels
} }
return convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, self._format_type, self._dimension) return convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, self._format_type, self._dimension)
class CVATProjectDataExtractor(datum_extractor.Extractor, CVATDataExtractorMixin): class CVATProjectDataExtractor(dm.Extractor, CVATDataExtractorMixin):
def __init__(self, project_data: ProjectData, include_images: bool = False, format_type: str = None, dimension: DimensionType = DimensionType.DIM_2D): def __init__(self, project_data: ProjectData, include_images: bool = False, format_type: str = None, dimension: DimensionType = DimensionType.DIM_2D):
super().__init__() super().__init__(media_type=dm.Image if dimension == DimensionType.DIM_2D else PointCloud)
self._categories = self._load_categories(project_data.meta['project']['labels']) self._categories = self._load_categories(project_data.meta['project']['labels'])
self._user = self._load_user_info(project_data.meta['project']) if dimension == DimensionType.DIM_3D else {} self._user = self._load_user_info(project_data.meta['project']) if dimension == DimensionType.DIM_3D else {}
self._dimension = dimension self._dimension = dimension
self._format_type = format_type self._format_type = format_type
dm_items: List[datum_extractor.DatasetItem] = [] dm_items: List[dm.DatasetItem] = []
ext_per_task: Dict[int, str] = {} ext_per_task: Dict[int, str] = {}
image_maker_per_task: Dict[int, Callable] = {} image_maker_per_task: Dict[int, Callable] = {}
@ -1251,7 +1248,7 @@ class CVATProjectDataExtractor(datum_extractor.Extractor, CVATDataExtractorMixin
loader = lambda _: frame_provider.get_frame(i, loader = lambda _: frame_provider.get_frame(i,
quality=frame_provider.Quality.ORIGINAL, quality=frame_provider.Quality.ORIGINAL,
out_type=frame_provider.Type.NUMPY_ARRAY)[0] out_type=frame_provider.Type.NUMPY_ARRAY)[0]
return Image(loader=loader, **kwargs) return dm.Image(data=loader, **kwargs)
return _make_image return _make_image
else: else:
# for images use encoded data to avoid recoding # for images use encoded data to avoid recoding
@ -1261,7 +1258,7 @@ class CVATProjectDataExtractor(datum_extractor.Extractor, CVATDataExtractorMixin
loader = lambda _: frame_provider.get_frame(i, loader = lambda _: frame_provider.get_frame(i,
quality=frame_provider.Quality.ORIGINAL, quality=frame_provider.Quality.ORIGINAL,
out_type=frame_provider.Type.BUFFER)[0].getvalue() out_type=frame_provider.Type.BUFFER)[0].getvalue()
return ByteImage(data=loader, **kwargs) return dm.ByteImage(data=loader, **kwargs)
return _make_image return _make_image
image_maker_per_task[task.id] = image_maker_factory(task) image_maker_per_task[task.id] = image_maker_factory(task)
@ -1275,12 +1272,12 @@ class CVATProjectDataExtractor(datum_extractor.Extractor, CVATDataExtractorMixin
elif include_images: elif include_images:
dm_image = image_maker_per_task[frame_data.task_id](frame_data.idx, **image_args) dm_image = image_maker_per_task[frame_data.task_id](frame_data.idx, **image_args)
else: else:
dm_image = Image(**image_args) dm_image = dm.Image(**image_args)
dm_anno = self._read_cvat_anno(frame_data, project_data.meta['project']['labels']) dm_anno = self._read_cvat_anno(frame_data, project_data.meta['project']['labels'])
if self._dimension == DimensionType.DIM_2D: if self._dimension == DimensionType.DIM_2D:
dm_item = datum_extractor.DatasetItem( dm_item = dm.DatasetItem(
id=osp.splitext(frame_data.name)[0], id=osp.splitext(frame_data.name)[0],
annotations=dm_anno, image=dm_image, annotations=dm_anno, media=dm_image,
subset=frame_data.subset, subset=frame_data.subset,
attributes={'frame': frame_data.frame} attributes={'frame': frame_data.frame}
) )
@ -1295,9 +1292,9 @@ class CVATProjectDataExtractor(datum_extractor.Extractor, CVATDataExtractorMixin
attributes["labels"].append({"label_id": idx, "name": label["name"], "color": label["color"], "type": label["type"]}) attributes["labels"].append({"label_id": idx, "name": label["name"], "color": label["color"], "type": label["type"]})
attributes["track_id"] = -1 attributes["track_id"] = -1
dm_item = datum_extractor.DatasetItem( dm_item = dm.DatasetItem(
id=osp.splitext(osp.split(frame_data.name)[-1])[0], id=osp.splitext(osp.split(frame_data.name)[-1])[0],
annotations=dm_anno, point_cloud=dm_image[0], related_images=dm_image[1], annotations=dm_anno, media=PointCloud(dm_image[0]), related_images=dm_image[1],
attributes=attributes, subset=frame_data.subset attributes=attributes, subset=frame_data.subset
) )
dm_items.append(dm_item) dm_items.append(dm_item)
@ -1348,13 +1345,13 @@ def get_defaulted_subset(subset: str, subsets: List[str]) -> str:
if subset: if subset:
return subset return subset
else: else:
if datum_extractor.DEFAULT_SUBSET_NAME not in subsets: if dm.DEFAULT_SUBSET_NAME not in subsets:
return datum_extractor.DEFAULT_SUBSET_NAME return dm.DEFAULT_SUBSET_NAME
else: else:
i = 1 i = 1
while i < sys.maxsize: while i < sys.maxsize:
if f'{datum_extractor.DEFAULT_SUBSET_NAME}_{i}' not in subsets: if f'{dm.DEFAULT_SUBSET_NAME}_{i}' not in subsets:
return f'{datum_extractor.DEFAULT_SUBSET_NAME}_{i}' return f'{dm.DEFAULT_SUBSET_NAME}_{i}'
i += 1 i += 1
raise Exception('Cannot find default name for subset') raise Exception('Cannot find default name for subset')
@ -1385,7 +1382,7 @@ def convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, format_name
anno_label = map_label(tag_obj.label) anno_label = map_label(tag_obj.label)
anno_attr = convert_attrs(tag_obj.label, tag_obj.attributes) anno_attr = convert_attrs(tag_obj.label, tag_obj.attributes)
anno = datum_annotation.Label(label=anno_label, anno = dm.Label(label=anno_label,
attributes=anno_attr, group=anno_group) attributes=anno_attr, group=anno_group)
item_anno.append(anno) item_anno.append(anno)
@ -1408,7 +1405,7 @@ def convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, format_name
anno_points = shape_obj.points anno_points = shape_obj.points
if shape_obj.type == ShapeType.POINTS: if shape_obj.type == ShapeType.POINTS:
anno = datum_annotation.Points(anno_points, anno = dm.Points(anno_points,
label=anno_label, attributes=anno_attr, group=anno_group, label=anno_label, attributes=anno_attr, group=anno_group,
z_order=shape_obj.z_order) z_order=shape_obj.z_order)
elif shape_obj.type == ShapeType.ELLIPSE: elif shape_obj.type == ShapeType.ELLIPSE:
@ -1424,16 +1421,16 @@ def convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, format_name
"attributes": anno_attr, "attributes": anno_attr,
}), cvat_frame_anno.height, cvat_frame_anno.width) }), cvat_frame_anno.height, cvat_frame_anno.width)
elif shape_obj.type == ShapeType.POLYLINE: elif shape_obj.type == ShapeType.POLYLINE:
anno = datum_annotation.PolyLine(anno_points, anno = dm.PolyLine(anno_points,
label=anno_label, attributes=anno_attr, group=anno_group, label=anno_label, attributes=anno_attr, group=anno_group,
z_order=shape_obj.z_order) z_order=shape_obj.z_order)
elif shape_obj.type == ShapeType.POLYGON: elif shape_obj.type == ShapeType.POLYGON:
anno = datum_annotation.Polygon(anno_points, anno = dm.Polygon(anno_points,
label=anno_label, attributes=anno_attr, group=anno_group, label=anno_label, attributes=anno_attr, group=anno_group,
z_order=shape_obj.z_order) z_order=shape_obj.z_order)
elif shape_obj.type == ShapeType.RECTANGLE: elif shape_obj.type == ShapeType.RECTANGLE:
x0, y0, x1, y1 = anno_points x0, y0, x1, y1 = anno_points
anno = datum_annotation.Bbox(x0, y0, x1 - x0, y1 - y0, anno = dm.Bbox(x0, y0, x1 - x0, y1 - y0,
label=anno_label, attributes=anno_attr, group=anno_group, label=anno_label, attributes=anno_attr, group=anno_group,
z_order=shape_obj.z_order) z_order=shape_obj.z_order)
elif shape_obj.type == ShapeType.CUBOID: elif shape_obj.type == ShapeType.CUBOID:
@ -1443,27 +1440,30 @@ def convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, format_name
else: else:
anno_id = index anno_id = index
position, rotation, scale = anno_points[0:3], anno_points[3:6], anno_points[6:9] position, rotation, scale = anno_points[0:3], anno_points[3:6], anno_points[6:9]
anno = datum_annotation.Cuboid3d( anno = dm.Cuboid3d(
id=anno_id, position=position, rotation=rotation, scale=scale, id=anno_id, position=position, rotation=rotation, scale=scale,
label=anno_label, attributes=anno_attr, group=anno_group label=anno_label, attributes=anno_attr, group=anno_group
) )
else: else:
continue continue
elif shape_obj.type == ShapeType.SKELETON: elif shape_obj.type == ShapeType.SKELETON:
points = [] elements = []
vis = []
for element in shape_obj.elements: for element in shape_obj.elements:
points.extend(element.points) element_attr = convert_attrs(shape_obj.label + element.label, element.attributes)
element_vis = datum_annotation.Points.Visibility.visible
if hasattr(element, 'track_id'):
element_attr['track_id'] = element.track_id
element_attr['keyframe'] = element.keyframe
element_vis = dm.Points.Visibility.visible
if element.outside: if element.outside:
element_vis = datum_annotation.Points.Visibility.absent element_vis = dm.Points.Visibility.absent
elif element.occluded: elif element.occluded:
element_vis = datum_annotation.Points.Visibility.hidden element_vis = dm.Points.Visibility.hidden
vis.append(element_vis) elements.append(dm.Points(element.points, [element_vis],
label=map_label(element.label, shape_obj.label), attributes=element_attr))
anno = datum_annotation.Points(points, vis, anno = dm.Skeleton(elements, label=anno_label,
label=anno_label, attributes=anno_attr, group=anno_group, attributes=anno_attr, group=anno_group, z_order=shape_obj.z_order)
z_order=shape_obj.z_order)
else: else:
raise Exception("Unknown shape type '%s'" % shape_obj.type) raise Exception("Unknown shape type '%s'" % shape_obj.type)
@ -1480,9 +1480,9 @@ def match_dm_item(item, task_data, root_hint=None):
if frame_number is None: if frame_number is None:
frame_number = task_data.match_frame(item.id, root_hint, path_has_ext=False) frame_number = task_data.match_frame(item.id, root_hint, path_has_ext=False)
if frame_number is None: if frame_number is None:
frame_number = cast(item.attributes.get('frame', item.id), int) frame_number = dm.util.cast(item.attributes.get('frame', item.id), int)
if frame_number is None and is_video: if frame_number is None and is_video:
frame_number = cast(osp.basename(item.id)[len('frame_'):], int) frame_number = dm.util.cast(osp.basename(item.id)[len('frame_'):], int)
if not frame_number in task_data.frame_info: if not frame_number in task_data.frame_info:
raise CvatImportError("Could not match item id: " raise CvatImportError("Could not match item id: "
@ -1505,7 +1505,7 @@ def find_dataset_root(dm_dataset, instance_data: Union[TaskData, ProjectData]):
prefix = prefix[:-1] prefix = prefix[:-1]
return prefix return prefix
def import_dm_annotations(dm_dataset: Dataset, instance_data: Union[TaskData, ProjectData]): def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[TaskData, ProjectData]):
if len(dm_dataset) == 0: if len(dm_dataset) == 0:
return return
@ -1518,15 +1518,15 @@ def import_dm_annotations(dm_dataset: Dataset, instance_data: Union[TaskData, Pr
return return
shapes = { shapes = {
datum_annotation.AnnotationType.bbox: ShapeType.RECTANGLE, dm.AnnotationType.bbox: ShapeType.RECTANGLE,
datum_annotation.AnnotationType.polygon: ShapeType.POLYGON, dm.AnnotationType.polygon: ShapeType.POLYGON,
datum_annotation.AnnotationType.polyline: ShapeType.POLYLINE, dm.AnnotationType.polyline: ShapeType.POLYLINE,
datum_annotation.AnnotationType.points: ShapeType.POINTS, dm.AnnotationType.points: ShapeType.POINTS,
datum_annotation.AnnotationType.cuboid_3d: ShapeType.CUBOID dm.AnnotationType.cuboid_3d: ShapeType.CUBOID,
dm.AnnotationType.skeleton: ShapeType.SKELETON
} }
label_cat = dm_dataset.categories()[datum_annotation.AnnotationType.label] label_cat = dm_dataset.categories()[dm.AnnotationType.label]
point_cat = dm_dataset.categories().get(datum_annotation.AnnotationType.points)
root_hint = find_dataset_root(dm_dataset, instance_data) root_hint = find_dataset_root(dm_dataset, instance_data)
@ -1563,54 +1563,57 @@ def import_dm_annotations(dm_dataset: Dataset, instance_data: Union[TaskData, Pr
] ]
if ann.type in shapes: if ann.type in shapes:
if ann.type == datum_annotation.AnnotationType.cuboid_3d: points = []
try: if ann.type == dm.AnnotationType.cuboid_3d:
ann.points = [*ann.position,*ann.rotation,*ann.scale,0,0,0,0,0,0,0] points = [*ann.position, *ann.rotation, *ann.scale, 0, 0, 0, 0, 0, 0, 0]
except Exception: elif ann.type != dm.AnnotationType.skeleton:
ann.points = ann.points points = ann.points
ann.z_order = 0
# Use safe casting to bool instead of plain reading # Use safe casting to bool instead of plain reading
# because in some formats return type can be different # because in some formats return type can be different
# from bool / None # from bool / None
# https://github.com/openvinotoolkit/datumaro/issues/719 # https://github.com/openvinotoolkit/datumaro/issues/719
occluded = cast(ann.attributes.pop('occluded', None), bool) is True occluded = dm.util.cast(ann.attributes.pop('occluded', None), bool) is True
keyframe = cast(ann.attributes.get('keyframe', None), bool) is True keyframe = dm.util.cast(ann.attributes.get('keyframe', None), bool) is True
outside = cast(ann.attributes.pop('outside', None), bool) is True outside = dm.util.cast(ann.attributes.pop('outside', None), bool) is True
track_id = ann.attributes.pop('track_id', None) track_id = ann.attributes.pop('track_id', None)
source = ann.attributes.pop('source').lower() \ source = ann.attributes.pop('source').lower() \
if ann.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual' if ann.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
shape_type = shapes[ann.type] shape_type = shapes[ann.type]
elements = []
if point_cat and shape_type == ShapeType.POINTS:
labels = point_cat.items[ann.label].labels
shape_type = ShapeType.SKELETON
for i in range(len(ann.points) // 2):
label = None
if i < len(labels):
label = labels[i]
elements.append(instance_data.LabeledShape(
type=ShapeType.POINTS,
frame=frame_number,
points=ann.points[2 * i : 2 * i + 2],
label=label,
occluded=ann.visibility[i] == datum_annotation.Points.Visibility.hidden,
source=source,
attributes=[],
outside=ann.visibility[i] == datum_annotation.Points.Visibility.absent,
))
if track_id is None or dm_dataset.format != 'cvat' : if track_id is None or dm_dataset.format != 'cvat' :
elements = []
if ann.type == dm.AnnotationType.skeleton:
for element in ann.elements:
element_attributes = [
instance_data.Attribute(name=n, value=str(v))
for n, v in element.attributes.items()
]
element_occluded = element.visibility[0] == dm.Points.Visibility.hidden
element_outside = element.visibility[0] == dm.Points.Visibility.absent
element_source = element.attributes.pop('source').lower() \
if element.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
elements.append(instance_data.LabeledShape(
type=shapes[element.type],
frame=frame_number,
points=element.points,
label=label_cat.items[element.label].name,
occluded=element_occluded,
z_order=ann.z_order,
group=group_map.get(ann.group, 0),
source=element_source,
attributes=element_attributes,
elements=[],
outside=element_outside,
))
instance_data.add_shape(instance_data.LabeledShape( instance_data.add_shape(instance_data.LabeledShape(
type=shape_type, type=shape_type,
frame=frame_number, frame=frame_number,
points=ann.points, points=points,
label=label_cat.items[ann.label].name, label=label_cat.items[ann.label].name,
occluded=occluded, occluded=occluded,
z_order=ann.z_order, z_order=ann.z_order if ann.type != dm.AnnotationType.cuboid_3d else 0,
group=group_map.get(ann.group, 0), group=group_map.get(ann.group, 0),
source=source, source=source,
attributes=attributes, attributes=attributes,
@ -1619,29 +1622,59 @@ def import_dm_annotations(dm_dataset: Dataset, instance_data: Union[TaskData, Pr
continue continue
if keyframe or outside: if keyframe or outside:
if track_id not in tracks:
tracks[track_id] = {
'label': label_cat.items[ann.label].name,
'group': group_map.get(ann.group, 0),
'source': source,
'shapes': [],
'elements':{},
}
track = instance_data.TrackedShape( track = instance_data.TrackedShape(
type=shapes[ann.type], type=shapes[ann.type],
frame=frame_number, frame=frame_number,
occluded=occluded, occluded=occluded,
outside=outside, outside=outside,
keyframe=keyframe, keyframe=keyframe,
points=ann.points, points=points,
z_order=ann.z_order, z_order=ann.z_order if ann.type != dm.AnnotationType.cuboid_3d else 0,
source=source, source=source,
attributes=attributes, attributes=attributes,
) )
if track_id not in tracks: tracks[track_id]['shapes'].append(track)
tracks[track_id] = instance_data.Track(
label=label_cat.items[ann.label].name, if ann.type == dm.AnnotationType.skeleton:
group=group_map.get(ann.group, 0), for element in ann.elements:
source=source, if element.label not in tracks[track_id]['elements']:
shapes=[], tracks[track_id]['elements'][element.label] = instance_data.Track(
) label=label_cat.items[element.label].name,
group=0,
tracks[track_id].shapes.append(track) source=source,
shapes=[],
elif ann.type == datum_annotation.AnnotationType.label: )
element_attributes = [
instance_data.Attribute(name=n, value=str(v))
for n, v in element.attributes.items()
]
element_occluded = dm.util.cast(element.attributes.pop('occluded', None), bool) is True
element_outside = dm.util.cast(element.attributes.pop('outside', None), bool) is True
element_source = element.attributes.pop('source').lower() \
if element.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
tracks[track_id]['elements'][element.label].shapes.append(instance_data.TrackedShape(
type=shapes[element.type],
frame=frame_number,
occluded=element_occluded,
outside=element_outside,
keyframe=keyframe,
points=element.points,
z_order=element.z_order,
source=element_source,
attributes=element_attributes,
))
elif ann.type == dm.AnnotationType.label:
instance_data.add_tag(instance_data.Tag( instance_data.add_tag(instance_data.Tag(
frame=frame_number, frame=frame_number,
label=label_cat.items[ann.label].name, label=label_cat.items[ann.label].name,
@ -1654,13 +1687,14 @@ def import_dm_annotations(dm_dataset: Dataset, instance_data: Union[TaskData, Pr
"#{} ({}): {}".format(item.id, idx, ann.type.name, e)) from e "#{} ({}): {}".format(item.id, idx, ann.type.name, e)) from e
for track in tracks.values(): for track in tracks.values():
instance_data.add_track(track) track['elements'] = list(track['elements'].values())
instance_data.add_track(instance_data.Track(**track))
def import_labels_to_project(project_annotation, dataset: Dataset): def import_labels_to_project(project_annotation, dataset: dm.Dataset):
labels = [] labels = []
label_colors = [] label_colors = []
for label in dataset.categories()[datum_annotation.AnnotationType.label].items: for label in dataset.categories()[dm.AnnotationType.label].items:
db_label = Label( db_label = Label(
name=label.name, name=label.name,
color=get_label_color(label.name, label_colors), color=get_label_color(label.name, label_colors),
@ -1670,11 +1704,11 @@ def import_labels_to_project(project_annotation, dataset: Dataset):
label_colors.append(db_label.color) label_colors.append(db_label.color)
project_annotation.add_labels(labels) project_annotation.add_labels(labels)
def load_dataset_data(project_annotation, dataset: Dataset, project_data): def load_dataset_data(project_annotation, dataset: dm.Dataset, project_data):
if not project_annotation.db_project.label_set.count(): if not project_annotation.db_project.label_set.count():
import_labels_to_project(project_annotation, dataset) import_labels_to_project(project_annotation, dataset)
else: else:
for label in dataset.categories()[datum_annotation.AnnotationType.label].items: for label in dataset.categories()[dm.AnnotationType.label].items:
if not project_annotation.db_project.label_set.filter(name=label.name).exists(): if not project_annotation.db_project.label_set.filter(name=label.name).exists():
raise CvatImportError(f'Target project does not have label with name "{label.name}"') raise CvatImportError(f'Target project does not have label with name "{label.name}"')
for subset_id, subset in enumerate(dataset.subsets().values()): for subset_id, subset in enumerate(dataset.subsets().values()):

@ -1,4 +1,5 @@
# Copyright (C) 2018-2022 Intel Corporation # Copyright (C) 2018-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -409,7 +410,7 @@ class CvatExtractor(Extractor):
di.subset = subset or DEFAULT_SUBSET_NAME di.subset = subset or DEFAULT_SUBSET_NAME
di.annotations = item_desc.get('annotations') di.annotations = item_desc.get('annotations')
di.attributes = {'frame': int(frame_id)} di.attributes = {'frame': int(frame_id)}
di.image = image if isinstance(image, Image) else di.image di.media = image if isinstance(image, Image) else di.media
image_items[(subset, osp.splitext(name)[0])] = di image_items[(subset, osp.splitext(name)[0])] = di
return image_items return image_items

@ -1,4 +1,5 @@
# Copyright (C) 2021-2022 Intel Corporation # Copyright (C) 2021-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -90,7 +91,7 @@ def _import(src_file, instance_data, load_data_callback=None):
with TemporaryDirectory() as tmp_dir: with TemporaryDirectory() as tmp_dir:
zipfile.ZipFile(src_file).extractall(tmp_dir) zipfile.ZipFile(src_file).extractall(tmp_dir)
dataset = Dataset.import_from(tmp_dir, 'icdar_word_recognition', env=dm_env) dataset = Dataset.import_from(tmp_dir, 'icdar_word_recognition', env=dm_env)
dataset.transform(CaptionToLabel, 'icdar') dataset.transform(CaptionToLabel, label='icdar')
if load_data_callback is not None: if load_data_callback is not None:
load_data_callback(dataset, instance_data) load_data_callback(dataset, instance_data)
import_dm_annotations(dataset, instance_data) import_dm_annotations(dataset, instance_data)
@ -110,7 +111,7 @@ def _import(src_file, instance_data, load_data_callback=None):
zipfile.ZipFile(src_file).extractall(tmp_dir) zipfile.ZipFile(src_file).extractall(tmp_dir)
dataset = Dataset.import_from(tmp_dir, 'icdar_text_localization', env=dm_env) dataset = Dataset.import_from(tmp_dir, 'icdar_text_localization', env=dm_env)
dataset.transform(AddLabelToAnns, 'icdar') dataset.transform(AddLabelToAnns, label='icdar')
if load_data_callback is not None: if load_data_callback is not None:
load_data_callback(dataset, instance_data) load_data_callback(dataset, instance_data)
import_dm_annotations(dataset, instance_data) import_dm_annotations(dataset, instance_data)
@ -133,7 +134,7 @@ def _import(src_file, instance_data, load_data_callback=None):
with TemporaryDirectory() as tmp_dir: with TemporaryDirectory() as tmp_dir:
zipfile.ZipFile(src_file).extractall(tmp_dir) zipfile.ZipFile(src_file).extractall(tmp_dir)
dataset = Dataset.import_from(tmp_dir, 'icdar_text_segmentation', env=dm_env) dataset = Dataset.import_from(tmp_dir, 'icdar_text_segmentation', env=dm_env)
dataset.transform(AddLabelToAnns, 'icdar') dataset.transform(AddLabelToAnns, label='icdar')
dataset.transform('masks_to_polygons') dataset.transform('masks_to_polygons')
if load_data_callback is not None: if load_data_callback is not None:
load_data_callback(dataset, instance_data) load_data_callback(dataset, instance_data)

@ -1,4 +1,5 @@
# Copyright (C) 2021-2022 Intel Corporation # Copyright (C) 2021-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -65,7 +66,7 @@ def _export(dst_file, instance_data, save_images=False):
dataset = Dataset.from_extractors(GetCVATDataExtractor( dataset = Dataset.from_extractors(GetCVATDataExtractor(
instance_data, include_images=save_images), env=dm_env) instance_data, include_images=save_images), env=dm_env)
with TemporaryDirectory() as temp_dir: with TemporaryDirectory() as temp_dir:
dataset.transform(LabelAttrToAttr, 'market-1501') dataset.transform(LabelAttrToAttr, label='market-1501')
dataset.export(temp_dir, 'market1501', save_images=save_images) dataset.export(temp_dir, 'market1501', save_images=save_images)
make_zip_archive(temp_dir, dst_file) make_zip_archive(temp_dir, dst_file)
@ -75,7 +76,7 @@ def _import(src_file, instance_data, load_data_callback=None):
zipfile.ZipFile(src_file).extractall(tmp_dir) zipfile.ZipFile(src_file).extractall(tmp_dir)
dataset = Dataset.import_from(tmp_dir, 'market1501', env=dm_env) dataset = Dataset.import_from(tmp_dir, 'market1501', env=dm_env)
dataset.transform(AttrToLabelAttr, 'market-1501') dataset.transform(AttrToLabelAttr, label='market-1501')
if load_data_callback is not None: if load_data_callback is not None:
load_data_callback(dataset, instance_data) load_data_callback(dataset, instance_data)
import_dm_annotations(dataset, instance_data) import_dm_annotations(dataset, instance_data)

@ -9,7 +9,7 @@ from itertools import chain
from pycocotools import mask as mask_utils from pycocotools import mask as mask_utils
from datumaro.components.extractor import ItemTransform from datumaro.components.extractor import ItemTransform
import datumaro.components.annotation as datum_annotation import datumaro.components.annotation as dm
class RotatedBoxesToPolygons(ItemTransform): class RotatedBoxesToPolygons(ItemTransform):
def _rotate_point(self, p, angle, cx, cy): def _rotate_point(self, p, angle, cx, cy):
@ -20,7 +20,7 @@ class RotatedBoxesToPolygons(ItemTransform):
def transform_item(self, item): def transform_item(self, item):
annotations = item.annotations[:] annotations = item.annotations[:]
anns = [p for p in annotations if p.type == datum_annotation.AnnotationType.bbox and p.attributes['rotation']] anns = [p for p in annotations if p.type == dm.AnnotationType.bbox and p.attributes['rotation']]
for ann in anns: for ann in anns:
rotation = math.radians(ann.attributes['rotation']) rotation = math.radians(ann.attributes['rotation'])
x0, y0, x1, y1 = ann.points x0, y0, x1, y1 = ann.points
@ -30,7 +30,7 @@ class RotatedBoxesToPolygons(ItemTransform):
)) ))
annotations.remove(ann) annotations.remove(ann)
annotations.append(datum_annotation.Polygon(anno_points, annotations.append(dm.Polygon(anno_points,
label=ann.label, attributes=ann.attributes, group=ann.group, label=ann.label, attributes=ann.attributes, group=ann.group,
z_order=ann.z_order)) z_order=ann.z_order))
@ -48,5 +48,5 @@ class EllipsesToMasks:
mat = np.zeros((img_h, img_w), dtype=np.uint8) mat = np.zeros((img_h, img_w), dtype=np.uint8)
cv2.ellipse(mat, center, axis, angle, 0, 360, 255, thickness=-1) cv2.ellipse(mat, center, axis, angle, 0, 360, 255, thickness=-1)
rle = mask_utils.encode(np.asfortranarray(mat)) rle = mask_utils.encode(np.asfortranarray(mat))
return datum_annotation.RleMask(rle=rle, label=ellipse.label, z_order=ellipse.z_order, return dm.RleMask(rle=rle, label=ellipse.label, z_order=ellipse.z_order,
attributes=ellipse.attributes, group=ellipse.group) attributes=ellipse.attributes, group=ellipse.group)

@ -1,4 +1,5 @@
# Copyright (C) 2021-2022 Intel Corporation # Copyright (C) 2021-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -29,7 +30,7 @@ def _import(src_file, instance_data, load_data_callback=None):
zipfile.ZipFile(src_file).extractall(tmp_dir) zipfile.ZipFile(src_file).extractall(tmp_dir)
dataset = Dataset.import_from(tmp_dir, 'vgg_face2', env=dm_env) dataset = Dataset.import_from(tmp_dir, 'vgg_face2', env=dm_env)
dataset.transform('rename', r"|([^/]+/)?(.+)|\2|") dataset.transform('rename', regex=r"|([^/]+/)?(.+)|\2|")
if load_data_callback is not None: if load_data_callback is not None:
load_data_callback(dataset, instance_data) load_data_callback(dataset, instance_data)
import_dm_annotations(dataset, instance_data) import_dm_annotations(dataset, instance_data)

@ -533,15 +533,7 @@
}, },
"Market-1501 1.0": { "Market-1501 1.0": {
"version": 0, "version": 0,
"tags": [ "tags": [],
{
"frame": 1,
"label_id": null,
"group": 0,
"source": "manual",
"attributes": []
}
],
"shapes": [], "shapes": [],
"tracks": [] "tracks": []
}, },

@ -336,7 +336,14 @@
"name": "skeleton", "name": "skeleton",
"color": "#2080c0", "color": "#2080c0",
"type": "skeleton", "type": "skeleton",
"attributes": [], "attributes": [
{
"name": "attr",
"mutable": false,
"input_type": "select",
"values": ["0", "1", "2"]
}
],
"sublabels": [ "sublabels": [
{ {
"name": "1", "name": "1",
@ -353,14 +360,7 @@
{ {
"name": "3", "name": "3",
"color": "#479ffe", "color": "#479ffe",
"attributes": [ "attributes": [],
{
"name": "attr",
"mutable": false,
"input_type": "select",
"values": ["0", "1", "2"]
}
],
"type": "points" "type": "points"
} }
], ],

@ -1,5 +1,6 @@
# Copyright (C) 2020-2022 Intel Corporation # Copyright (C) 2020-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -617,6 +618,14 @@ class TaskAnnotationsImportTest(_DbTestBase):
images["image_quality"] = 75 images["image_quality"] = 75
return images return images
def _generate_task_images_by_names(self, names, **image_params):
images = {
f"client_files[{i}]": generate_image_file(f"{name}.jpg", **image_params)
for i, name in enumerate(names)
}
images["image_quality"] = 75
return images
def _generate_task(self, images, annotation_format, **overrides): def _generate_task(self, images, annotation_format, **overrides):
labels = [] labels = []
if annotation_format in ["ICDAR Recognition 1.0", if annotation_format in ["ICDAR Recognition 1.0",
@ -911,7 +920,10 @@ class TaskAnnotationsImportTest(_DbTestBase):
for f in dm.views.get_import_formats(): for f in dm.views.get_import_formats():
format_name = f.DISPLAY_NAME format_name = f.DISPLAY_NAME
images = self._generate_task_images(3, "img0.0.0") if format_name == "Market-1501 1.0":
images = self._generate_task_images_by_names(["img0.0.0_0", "1.0_c3s1_000000_00", "img0.0.0_1"])
else:
images = self._generate_task_images(3, "img0.0.0")
task = self._generate_task(images, format_name) task = self._generate_task(images, format_name)
self._generate_annotations(task, format_name) self._generate_annotations(task, format_name)

@ -45,9 +45,7 @@ diskcache==5.0.2
boto3==1.17.61 boto3==1.17.61
azure-storage-blob==12.13.0 azure-storage-blob==12.13.0
google-cloud-storage==1.42.0 google-cloud-storage==1.42.0
# --no-binary=datumaro: workaround for pip to install git+https://github.com/cvat-ai/datumaro.git@bac20235bd6c792b4d068a54fd3ad14be50bb26f
# opencv-headless instead of regular opencv, to actually run setup script
datumaro==0.2.0 --no-binary=datumaro
urllib3>=1.26.5 # not directly required, pinned by Snyk to avoid a vulnerability urllib3>=1.26.5 # not directly required, pinned by Snyk to avoid a vulnerability
natsort==8.0.0 natsort==8.0.0
mistune>=2.0.1 # not directly required, pinned by Snyk to avoid a vulnerability mistune>=2.0.1 # not directly required, pinned by Snyk to avoid a vulnerability

Loading…
Cancel
Save