Skip to content

Commit

Permalink
Merge branch 'refs/heads/develop' into dl/yolo-preserve-rotation
Browse files Browse the repository at this point in the history
# Conflicts:
#	cvat/requirements/base.in
#	cvat/requirements/base.txt
  • Loading branch information
Eldies committed Jan 8, 2025
2 parents a43d3dc + 8b621c2 commit b8e8669
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 15 deletions.
4 changes: 4 additions & 0 deletions changelog.d/20250107_163424_dmitrii.lavrukhin_yolo_tracks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Added

- Ultralytics YOLO formats now support tracks
(<https://github.com/cvat-ai/cvat/pull/8883>)
6 changes: 5 additions & 1 deletion cvat/apps/dataset_manager/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2185,7 +2185,11 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[ProjectDa
'coco',
'coco_instances',
'coco_person_keypoints',
'voc'
'voc',
'yolo_ultralytics_detection',
'yolo_ultralytics_segmentation',
'yolo_ultralytics_oriented_boxes',
'yolo_ultralytics_pose',
]

label_cat = dm_dataset.categories()[dm.AnnotationType.label]
Expand Down
13 changes: 13 additions & 0 deletions cvat/apps/dataset_manager/formats/transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def transform_item(self, item):

return item.wrap(annotations=annotations)


class MaskConverter:
@staticmethod
def cvat_rle_to_dm_rle(shape, img_h: int, img_w: int) -> dm.RleMask:
Expand Down Expand Up @@ -100,6 +101,7 @@ def rle(cls, arr: np.ndarray) -> list[int]:

return cvat_rle


class EllipsesToMasks:
@staticmethod
def convert_ellipse(ellipse, img_h, img_w):
Expand All @@ -115,6 +117,7 @@ def convert_ellipse(ellipse, img_h, img_w):
return dm.RleMask(rle=rle, label=ellipse.label, z_order=ellipse.z_order,
attributes=ellipse.attributes, group=ellipse.group)


class MaskToPolygonTransformation:
"""
Manages common logic for mask to polygons conversion in dataset import.
Expand All @@ -130,3 +133,13 @@ def convert_dataset(cls, dataset, **kwargs):
if kwargs.get('conv_mask_to_poly', True):
dataset.transform('masks_to_polygons')
return dataset


class SetKeyframeForEveryTrackShape(dm.ItemTransform):
def transform_item(self, item):
annotations = []
for ann in item.annotations:
if "track_id" in ann.attributes:
ann = ann.wrap(attributes=dict(ann.attributes, keyframe=True))
annotations.append(ann)
return item.wrap(annotations=annotations)
32 changes: 25 additions & 7 deletions cvat/apps/dataset_manager/formats/yolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
# SPDX-License-Identifier: MIT
import os.path as osp
from glob import glob
from typing import Callable, Optional

from datumaro.components.annotation import AnnotationType
from datumaro.components.extractor import DatasetItem
from datumaro.components.project import Dataset
from pyunpack import Archive

from cvat.apps.dataset_manager.bindings import (
CommonData,
GetCVATDataExtractor,
ProjectData,
detect_dataset,
find_dataset_root,
import_dm_annotations,
Expand All @@ -20,12 +23,21 @@
from cvat.apps.dataset_manager.util import make_zip_archive

from .registry import dm_env, exporter, importer
from .transformations import SetKeyframeForEveryTrackShape


def _export_common(dst_file, temp_dir, instance_data, format_name, *, save_images=False):
def _export_common(
dst_file: str,
temp_dir: str,
instance_data: ProjectData | CommonData,
format_name: str,
*,
save_images: bool = False,
**kwargs
):
with GetCVATDataExtractor(instance_data, include_images=save_images) as extractor:
dataset = Dataset.from_extractors(extractor, env=dm_env)
dataset.export(temp_dir, format_name, save_images=save_images)
dataset.export(temp_dir, format_name, save_images=save_images, **kwargs)

make_zip_archive(temp_dir, dst_file)

Expand All @@ -37,12 +49,12 @@ def _export_yolo(*args, **kwargs):

def _import_common(
src_file,
temp_dir,
instance_data,
format_name,
temp_dir: str,
instance_data: ProjectData | CommonData,
format_name: str,
*,
load_data_callback=None,
import_kwargs=None,
load_data_callback: Optional[Callable] = None,
import_kwargs: dict | None = None,
**kwargs
):
Archive(src_file.name).extractall(temp_dir)
Expand All @@ -67,6 +79,7 @@ def _import_common(
detect_dataset(temp_dir, format_name=format_name, importer=dm_env.importers.get(format_name))
dataset = Dataset.import_from(temp_dir, format_name,
env=dm_env, image_info=image_info, **(import_kwargs or {}))
dataset = dataset.transform(SetKeyframeForEveryTrackShape)
if load_data_callback is not None:
load_data_callback(dataset, instance_data)
import_dm_annotations(dataset, instance_data)
Expand All @@ -82,6 +95,11 @@ def _export_yolo_ultralytics_detection(*args, **kwargs):
_export_common(*args, format_name='yolo_ultralytics_detection', **kwargs)


@exporter(name='Ultralytics YOLO Detection Track', ext='ZIP', version='1.0')
def _export_yolo_ultralytics_detection_track(*args, **kwargs):
_export_common(*args, format_name='yolo_ultralytics_detection', write_track_id=True, **kwargs)


@exporter(name='Ultralytics YOLO Oriented Bounding Boxes', ext='ZIP', version='1.0')
def _export_yolo_ultralytics_oriented_boxes(*args, **kwargs):
_export_common(*args, format_name='yolo_ultralytics_oriented_boxes', **kwargs)
Expand Down
48 changes: 48 additions & 0 deletions cvat/apps/dataset_manager/tests/assets/annotations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,54 @@
],
"tracks": []
},
"Ultralytics YOLO Detection Track 1.0": {
"version": 0,
"tags": [],
"shapes": [
{
"type": "rectangle",
"occluded": false,
"z_order": 0,
"points": [0.3, 0.1, 0.2, 0.8],
"frame": 0,
"label_id": null,
"group": 0,
"source": "manual",
"attributes": []
}
],
"tracks": [
{
"frame": 0,
"label_id": null,
"group": 0,
"source": "manual",
"shapes": [
{
"type": "rectangle",
"occluded": false,
"z_order": 0,
"points": [0.2, 0.1, 0.2, 0.8],
"frame": 0,
"outside": false,
"attributes": [],
"keyframe": true
},
{
"type": "rectangle",
"occluded": false,
"z_order": 0,
"points": [0.4, 0.1, 0.2, 0.8],
"frame": 1,
"outside": true,
"attributes": [],
"keyframe": true
}
],
"attributes": []
}
]
},
"Ultralytics YOLO Oriented Bounding Boxes 1.0": {
"version": 0,
"tags": [],
Expand Down
1 change: 1 addition & 0 deletions cvat/apps/dataset_manager/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ def test_export_formats_query(self):
'Ultralytics YOLO Classification 1.0',
'Ultralytics YOLO Oriented Bounding Boxes 1.0',
'Ultralytics YOLO Detection 1.0',
'Ultralytics YOLO Detection Track 1.0',
'Ultralytics YOLO Pose 1.0',
'Ultralytics YOLO Segmentation 1.0',
})
Expand Down
5 changes: 5 additions & 0 deletions cvat/apps/dataset_manager/tests/test_rest_api_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"Ultralytics YOLO Classification 1.0",
"YOLO 1.1",
"Ultralytics YOLO Detection 1.0",
"Ultralytics YOLO Detection Track 1.0",
"Ultralytics YOLO Segmentation 1.0",
"Ultralytics YOLO Oriented Bounding Boxes 1.0",
"Ultralytics YOLO Pose 1.0",
Expand Down Expand Up @@ -980,6 +981,8 @@ def test_api_v2_rewriting_annotations(self):

if dump_format_name == "CVAT for images 1.1" or dump_format_name == "CVAT for video 1.1":
dump_format_name = "CVAT 1.1"
elif dump_format_name == "Ultralytics YOLO Detection Track 1.0":
dump_format_name = "Ultralytics YOLO Detection 1.0"
url = self._generate_url_upload_tasks_annotations(task_id, dump_format_name)

with open(file_zip_name, 'rb') as binary_file:
Expand Down Expand Up @@ -1093,6 +1096,8 @@ def test_api_v2_tasks_annotations_dump_and_upload_with_datumaro(self):
# upload annotations
if dump_format_name in ["CVAT for images 1.1", "CVAT for video 1.1"]:
upload_format_name = "CVAT 1.1"
elif dump_format_name in ['Ultralytics YOLO Detection Track 1.0']:
upload_format_name = 'Ultralytics YOLO Detection 1.0'
else:
upload_format_name = dump_format_name
url = self._generate_url_upload_tasks_annotations(task_id, upload_format_name)
Expand Down
3 changes: 3 additions & 0 deletions cvat/apps/engine/tests/test_rest_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6398,6 +6398,9 @@ def _get_initial_annotation(annotation_format):
formats['CVAT for video 1.1'] = 'CVAT 1.1'
if 'CVAT for images 1.1' in export_formats:
formats['CVAT for images 1.1'] = 'CVAT 1.1'
if 'Ultralytics YOLO Detection 1.0' in import_formats:
if 'Ultralytics YOLO Detection Track 1.0' in export_formats:
formats['Ultralytics YOLO Detection Track 1.0'] = 'Ultralytics YOLO Detection 1.0'
if set(import_formats) ^ set(export_formats):
# NOTE: this may not be an error, so we should not fail
print("The following import formats have no pair:",
Expand Down
6 changes: 3 additions & 3 deletions cvat/requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SHA1:8d2c3b645ed1f9efb7ca325d5da7eb9e07aced87
# SHA1:d210065224fc86c1fba6b5d22b77ab38d02bcbb1
#
# This file is autogenerated by pip-compile-multi
# To update, run:
Expand Down Expand Up @@ -242,7 +242,7 @@ pyjwt[crypto]==2.10.1
# via django-allauth
pylogbeat==2.0.1
# via python-logstash-async
pyparsing==3.2.0
pyparsing==3.2.1
# via matplotlib
pyrsistent==0.20.0
# via jsonschema
Expand Down Expand Up @@ -308,7 +308,7 @@ rq-scheduler==0.13.1
# via -r cvat/requirements/base.in
rsa==4.9
# via google-auth
ruamel-yaml==0.18.7
ruamel-yaml==0.18.8
# via datumaro
ruamel-yaml-clib==0.2.12
# via ruamel-yaml
Expand Down
6 changes: 3 additions & 3 deletions tests/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1146,9 +1146,9 @@ crc32-stream@^4.0.2:
readable-stream "^3.4.0"

cross-spawn@^7.0.0, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
Expand Down
2 changes: 1 addition & 1 deletion utils/dataset_manifest/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ numpy==1.22.4
# via opencv-python-headless
opencv-python-headless==4.10.0.84
# via -r utils/dataset_manifest/requirements.in
pillow==11.0.0
pillow==11.1.0
# via -r utils/dataset_manifest/requirements.in
tqdm==4.67.1
# via -r utils/dataset_manifest/requirements.in

0 comments on commit b8e8669

Please sign in to comment.