diff --git a/README.md b/README.md index fb83fd25e7e..2a252b4eaed 100644 --- a/README.md +++ b/README.md @@ -176,9 +176,10 @@ For more information about the supported formats, see: | [LFW](http://vis-www.cs.umass.edu/lfw/) | ✔️ | ✔️ | | [Supervisely Point Cloud Format](https://docs.supervise.ly/data-organization/00_ann_format_navi) | ✔️ | ✔️ | | [YOLOv8 Detection](https://docs.ultralytics.com/datasets/detect/) | ✔️ | ✔️ | -| [YOLOv8 Oriented Bounding Boxes](https://docs.ultralytics.com/datasets/obb/) | ✔️ | ✔️ | -| [YOLOv8 Segmentation](https://docs.ultralytics.com/datasets/segment/) | ✔️ | ✔️ | -| [YOLOv8 Pose](https://docs.ultralytics.com/datasets/pose/) | ✔️ | ✔️ | +| [YOLOv8 Oriented Bounding Boxes](https://docs.ultralytics.com/datasets/obb/) | ✔️ | ✔️ | +| [YOLOv8 Segmentation](https://docs.ultralytics.com/datasets/segment/) | ✔️ | ✔️ | +| [YOLOv8 Pose](https://docs.ultralytics.com/datasets/pose/) | ✔️ | ✔️ | +| [YOLOv8 Classification](https://docs.ultralytics.com/datasets/classify/) | ✔️ | ✔️ | diff --git a/changelog.d/20240926_152800_dmitrii.lavrukhin_yolov8_classification.md b/changelog.d/20240926_152800_dmitrii.lavrukhin_yolov8_classification.md new file mode 100644 index 00000000000..4a38def8a57 --- /dev/null +++ b/changelog.d/20240926_152800_dmitrii.lavrukhin_yolov8_classification.md @@ -0,0 +1,4 @@ +### Added + +- Support for YOLOv8 Classification format + () diff --git a/cvat/apps/dataset_manager/formats/yolo.py b/cvat/apps/dataset_manager/formats/yolo.py index a8c8177b055..887232b8e66 100644 --- a/cvat/apps/dataset_manager/formats/yolo.py +++ b/cvat/apps/dataset_manager/formats/yolo.py @@ -102,6 +102,11 @@ def _export_yolov8_pose(*args, **kwargs): _export_common(*args, format_name='yolov8_pose', **kwargs) +@exporter(name='YOLOv8 Classification', ext='ZIP', version='1.0') +def _export_yolov8_classification(*args, **kwargs): + _export_common(*args, format_name='yolov8_classification', **kwargs) + + @importer(name='YOLOv8 Detection', ext="ZIP", version="1.0") def _import_yolov8_detection(*args, **kwargs): _import_common(*args, format_name="yolov8_detection", **kwargs) @@ -134,3 +139,8 @@ def _import_yolov8_pose(src_file, temp_dir, instance_data, **kwargs): import_kwargs=dict(skeleton_sub_labels=true_skeleton_point_labels), **kwargs ) + + +@importer(name='YOLOv8 Classification', ext="ZIP", version="1.0") +def _import_yolov8_classification(*args, **kwargs): + _import_common(*args, format_name="yolov8_classification", **kwargs) diff --git a/cvat/apps/dataset_manager/tests/assets/annotations.json b/cvat/apps/dataset_manager/tests/assets/annotations.json index 51b13dd681a..9f7c27b94bc 100644 --- a/cvat/apps/dataset_manager/tests/assets/annotations.json +++ b/cvat/apps/dataset_manager/tests/assets/annotations.json @@ -976,6 +976,20 @@ ], "tracks": [] }, + "YOLOv8 Classification 1.0": { + "version": 0, + "tags": [ + { + "frame": 0, + "label_id": null, + "group": 0, + "source": "manual", + "attributes": [] + } + ], + "shapes": [], + "tracks": [] + }, "YOLOv8 Detection 1.0": { "version": 0, "tags": [], diff --git a/cvat/apps/dataset_manager/tests/test_formats.py b/cvat/apps/dataset_manager/tests/test_formats.py index 42b2337304b..7b9eebd5ab9 100644 --- a/cvat/apps/dataset_manager/tests/test_formats.py +++ b/cvat/apps/dataset_manager/tests/test_formats.py @@ -290,6 +290,7 @@ def test_export_formats_query(self): 'LFW 1.0', 'Cityscapes 1.0', 'Open Images V6 1.0', + 'YOLOv8 Classification 1.0', 'YOLOv8 Oriented Bounding Boxes 1.0', 'YOLOv8 Detection 1.0', 'YOLOv8 Pose 1.0', @@ -326,6 +327,7 @@ def test_import_formats_query(self): 'Open Images V6 1.0', 'Datumaro 1.0', 'Datumaro 3D 1.0', + 'YOLOv8 Classification 1.0', 'YOLOv8 Oriented Bounding Boxes 1.0', 'YOLOv8 Detection 1.0', 'YOLOv8 Pose 1.0', @@ -379,6 +381,7 @@ def test_empty_images_are_exported(self): # ('KITTI 1.0', 'kitti') format does not support empty annotations ('LFW 1.0', 'lfw'), # ('Cityscapes 1.0', 'cityscapes'), does not support, empty annotations + ('YOLOv8 Classification 1.0', 'yolov8_classification'), ('YOLOv8 Oriented Bounding Boxes 1.0', 'yolov8_oriented_boxes'), ('YOLOv8 Detection 1.0', 'yolov8_detection'), ('YOLOv8 Pose 1.0', 'yolov8_pose'), diff --git a/cvat/apps/dataset_manager/tests/test_rest_api_formats.py b/cvat/apps/dataset_manager/tests/test_rest_api_formats.py index a0717c1ef11..d60660a55c7 100644 --- a/cvat/apps/dataset_manager/tests/test_rest_api_formats.py +++ b/cvat/apps/dataset_manager/tests/test_rest_api_formats.py @@ -52,6 +52,22 @@ with open(annotation_path) as file: annotations = json.load(file) +DEFAULT_ATTRIBUTES_FORMATS = [ + "VGGFace2 1.0", + "WiderFace 1.0", + "YOLOv8 Classification 1.0", + "YOLO 1.1", + "YOLOv8 Detection 1.0", + "YOLOv8 Segmentation 1.0", + "YOLOv8 Oriented Bounding Boxes 1.0", + "YOLOv8 Pose 1.0", + "PASCAL VOC 1.1", + "Segmentation mask 1.1", + "ImageNet 1.0", + "Cityscapes 1.0", + "MOTS PNG 1.0", +] + def generate_image_file(filename, size=(100, 50)): f = BytesIO() @@ -401,14 +417,8 @@ def test_api_v2_dump_and_upload_annotations_with_objects_type_is_shape(self): else: task = self._create_task(tasks["main"], images) task_id = task["id"] - if dump_format_name in [ - "Cityscapes 1.0", "Datumaro 1.0", - "ImageNet 1.0", "MOTS PNG 1.0", - "PASCAL VOC 1.1", "Segmentation mask 1.1", - "VGGFace2 1.0", - "WiderFace 1.0", "YOLO 1.1", - "YOLOv8 Detection 1.0", "YOLOv8 Segmentation 1.0", - "YOLOv8 Oriented Bounding Boxes 1.0", "YOLOv8 Pose 1.0", + if dump_format_name in DEFAULT_ATTRIBUTES_FORMATS + [ + "Datumaro 1.0", ]: self._create_annotations(task, dump_format_name, "default") else: @@ -510,14 +520,7 @@ def test_api_v2_dump_annotations_with_objects_type_is_track(self): task = self._create_task(tasks["main"], video) task_id = task["id"] - if dump_format_name in [ - "Cityscapes 1.0", "ImageNet 1.0", - "MOTS PNG 1.0", "PASCAL VOC 1.1", - "Segmentation mask 1.1", - "VGGFace2 1.0", "WiderFace 1.0", "YOLO 1.1", - "YOLOv8 Detection 1.0", "YOLOv8 Segmentation 1.0", - "YOLOv8 Oriented Bounding Boxes 1.0", "YOLOv8 Pose 1.0", - ]: + if dump_format_name in DEFAULT_ATTRIBUTES_FORMATS: self._create_annotations(task, dump_format_name, "default") else: self._create_annotations(task, dump_format_name, "random") @@ -951,13 +954,8 @@ def test_api_v2_rewriting_annotations(self): task = self._create_task(tasks["main"], images) task_id = task["id"] - if dump_format_name in [ - "MOT 1.1", "PASCAL VOC 1.1", "Segmentation mask 1.1", - "YOLO 1.1", "ImageNet 1.0", - "WiderFace 1.0", "VGGFace2 1.0", - "Datumaro 1.0", "Open Images V6 1.0", "KITTI 1.0", - "YOLOv8 Detection 1.0", "YOLOv8 Segmentation 1.0", - "YOLOv8 Oriented Bounding Boxes 1.0", "YOLOv8 Pose 1.0", + if dump_format_name in DEFAULT_ATTRIBUTES_FORMATS + [ + "MOT 1.1", "Datumaro 1.0", "Open Images V6 1.0", "KITTI 1.0", ]: self._create_annotations(task, dump_format_name, "default") else: @@ -1067,14 +1065,9 @@ def test_api_v2_tasks_annotations_dump_and_upload_with_datumaro(self): task = self._create_task(tasks["main"], images) # create annotations - if dump_format_name in [ - "MOT 1.1", "MOTS PNG 1.0", - "PASCAL VOC 1.1", "Segmentation mask 1.1", - "YOLO 1.1", "ImageNet 1.0", - "WiderFace 1.0", "VGGFace2 1.0", "LFW 1.0", + if dump_format_name in DEFAULT_ATTRIBUTES_FORMATS + [ + "MOT 1.1", "LFW 1.0", "Open Images V6 1.0", "Datumaro 1.0", "KITTI 1.0", - "YOLOv8 Detection 1.0", "YOLOv8 Segmentation 1.0", - "YOLOv8 Oriented Bounding Boxes 1.0", "YOLOv8 Pose 1.0", ]: self._create_annotations(task, dump_format_name, "default") else: @@ -2101,11 +2094,8 @@ def test_api_v2_export_import_dataset(self): url = self._generate_url_dump_project_dataset(project['id'], dump_format_name) - if dump_format_name in [ - "Cityscapes 1.0", "Datumaro 1.0", "ImageNet 1.0", - "MOT 1.1", "MOTS PNG 1.0", "PASCAL VOC 1.1", - "Segmentation mask 1.1", "VGGFace2 1.0", - "WiderFace 1.0", "YOLO 1.1", "YOLOv8 Detection 1.0", + if dump_format_name in DEFAULT_ATTRIBUTES_FORMATS + [ + "Datumaro 1.0", "MOT 1.1", ]: self._create_annotations(task, dump_format_name, "default") else: diff --git a/cvat/requirements/base.in b/cvat/requirements/base.in index 2bc36c18d8e..edd8c065dbc 100644 --- a/cvat/requirements/base.in +++ b/cvat/requirements/base.in @@ -12,7 +12,7 @@ azure-storage-blob==12.13.0 boto3==1.17.61 clickhouse-connect==0.6.8 coreapi==2.3.3 -datumaro @ git+https://github.com/cvat-ai/datumaro.git@393cb666529067060ff57e30cb6e448669274f35 +datumaro @ git+https://github.com/cvat-ai/datumaro.git@e612d1bfb76a3c3d3d545187338c841a246619fb dj-pagination==2.5.0 # Despite direct indication allauth in requirements we should keep 'with_social' for dj-rest-auth # to avoid possible further versions conflicts (we use registration functionality) diff --git a/cvat/requirements/base.txt b/cvat/requirements/base.txt index ff350486075..ffaf10bd0e7 100644 --- a/cvat/requirements/base.txt +++ b/cvat/requirements/base.txt @@ -1,4 +1,4 @@ -# SHA1:576a1a528bac33626df6c8d11c983263f7a175eb +# SHA1:9ff984f33ae139c68d90acc3e338c6cef7ecf6e9 # # This file is autogenerated by pip-compile-multi # To update, run: @@ -15,7 +15,7 @@ attrs==21.4.0 # -r cvat/requirements/base.in # datumaro # jsonschema -azure-core==1.30.2 +azure-core==1.31.0 # via # azure-storage-blob # msrest @@ -29,12 +29,12 @@ botocore==1.20.112 # s3transfer cachetools==5.5.0 # via google-auth -certifi==2024.7.4 +certifi==2024.8.30 # via # clickhouse-connect # msrest # requests -cffi==1.17.0 +cffi==1.17.1 # via cryptography charset-normalizer==3.3.2 # via requests @@ -50,13 +50,13 @@ coreschema==0.0.4 # via coreapi crontab==1.0.1 # via rq-scheduler -cryptography==43.0.0 +cryptography==43.0.1 # via # azure-storage-blob # pyjwt cycler==0.12.1 # via matplotlib -datumaro @ git+https://github.com/cvat-ai/datumaro.git@393cb666529067060ff57e30cb6e448669274f35 +datumaro @ git+https://github.com/cvat-ai/datumaro.git@e612d1bfb76a3c3d3d545187338c841a246619fb # via -r cvat/requirements/base.in defusedxml==0.7.1 # via @@ -66,9 +66,9 @@ deprecated==1.2.14 # via limits dj-pagination==2.5.0 # via -r cvat/requirements/base.in -dj-rest-auth[with_social]==5.0.2 +dj-rest-auth[with-social]==5.0.2 # via -r cvat/requirements/base.in -django==4.2.15 +django==4.2.16 # via # -r cvat/requirements/base.in # dj-rest-auth @@ -116,17 +116,17 @@ easyprocess==1.1 # via pyunpack entrypoint2==1.1 # via pyunpack -fonttools==4.53.1 +fonttools==4.54.1 # via matplotlib freezegun==1.5.1 # via rq-scheduler furl==2.1.0 # via -r cvat/requirements/base.in -google-api-core==2.19.2 +google-api-core==2.20.0 # via # google-cloud-core # google-cloud-storage -google-auth==2.34.0 +google-auth==2.35.0 # via # google-api-core # google-cloud-core @@ -135,19 +135,19 @@ google-cloud-core==2.4.1 # via google-cloud-storage google-cloud-storage==1.42.0 # via -r cvat/requirements/base.in -google-crc32c==1.5.0 +google-crc32c==1.6.0 # via google-resumable-media google-resumable-media==2.7.2 # via google-cloud-storage googleapis-common-protos==1.65.0 # via google-api-core -h5py==3.11.0 +h5py==3.12.1 # via datumaro -idna==3.8 +idna==3.10 # via requests -importlib-metadata==8.4.0 +importlib-metadata==8.5.0 # via clickhouse-connect -importlib-resources==6.4.4 +importlib-resources==6.4.5 # via limits inflection==0.5.1 # via drf-spectacular @@ -165,7 +165,7 @@ jmespath==0.10.0 # botocore jsonschema==4.17.3 # via drf-spectacular -kiwisolver==1.4.5 +kiwisolver==1.4.7 # via matplotlib limits==3.13.0 # via python-logstash-async @@ -183,7 +183,7 @@ matplotlib==3.8.4 # via # datumaro # pycocotools -mmh3==4.1.0 +mmh3==5.0.1 # via pottery msrest==0.7.1 # via azure-storage-blob @@ -203,7 +203,7 @@ packaging==24.1 # matplotlib # nibabel # tensorboardx -pandas==2.2.2 +pandas==2.2.3 # via datumaro patool==1.12 # via -r cvat/requirements/base.in @@ -213,7 +213,7 @@ pottery==3.0.0 # via -r cvat/requirements/base.in proto-plus==1.24.0 # via google-api-core -protobuf==5.27.4 +protobuf==5.28.2 # via # google-api-core # googleapis-common-protos @@ -223,12 +223,12 @@ psutil==5.9.4 # via -r cvat/requirements/base.in psycopg2-binary==2.9.5 # via -r cvat/requirements/base.in -pyasn1==0.6.0 +pyasn1==0.6.1 # via # pyasn1-modules # python-ldap # rsa -pyasn1-modules==0.4.0 +pyasn1-modules==0.4.1 # via # google-auth # python-ldap @@ -261,7 +261,7 @@ python3-openid==3.2.0 # via django-allauth python3-saml==1.16.0 # via django-allauth -pytz==2024.1 +pytz==2024.2 # via # clickhouse-connect # djangorestframework @@ -311,7 +311,7 @@ ruamel-yaml==0.18.6 # via datumaro ruamel-yaml-clib==0.2.8 # via ruamel-yaml -rules==3.4 +rules==3.5 # via -r cvat/requirements/base.in s3transfer==0.4.2 # via boto3 @@ -337,13 +337,13 @@ typing-extensions==4.12.2 # datumaro # limits # pottery -tzdata==2024.1 +tzdata==2024.2 # via pandas uritemplate==4.1.1 # via # coreapi # drf-spectacular -urllib3==1.26.19 +urllib3==1.26.20 # via # botocore # clickhouse-connect @@ -354,7 +354,7 @@ xmlsec==1.3.14 # via # -r cvat/requirements/base.in # python3-saml -zipp==3.20.1 +zipp==3.20.2 # via importlib-metadata zstandard==0.23.0 # via clickhouse-connect diff --git a/cvat/requirements/development.txt b/cvat/requirements/development.txt index bb98bd744f9..3a76b304851 100644 --- a/cvat/requirements/development.txt +++ b/cvat/requirements/development.txt @@ -12,7 +12,7 @@ autopep8==2.3.1 # via django-silk black==24.8.0 # via -r cvat/requirements/development.in -dill==0.3.8 +dill==0.3.9 # via pylint django-extensions==3.0.8 # via -r cvat/requirements/development.in @@ -30,7 +30,7 @@ mypy-extensions==1.0.0 # via black pathspec==0.12.1 # via black -platformdirs==4.2.2 +platformdirs==4.3.6 # via # black # pylint @@ -51,7 +51,7 @@ rope==0.17.0 # via -r cvat/requirements/development.in snakeviz==2.1.0 # via -r cvat/requirements/development.in -tomli==2.0.1 +tomli==2.0.2 # via # autopep8 # black @@ -62,5 +62,5 @@ tornado==6.4.1 # via snakeviz # The following packages are considered to be unsafe in a requirements file: -setuptools==74.0.0 +setuptools==75.1.0 # via astroid diff --git a/cvat/requirements/production.txt b/cvat/requirements/production.txt index a109c47c2ef..9c845a83424 100644 --- a/cvat/requirements/production.txt +++ b/cvat/requirements/production.txt @@ -6,7 +6,7 @@ # pip-compile-multi # -r base.txt -anyio==4.4.0 +anyio==4.6.0 # via watchfiles coverage==7.2.3 # via -r cvat/requirements/production.in @@ -26,5 +26,5 @@ uvloop==0.20.0 # via uvicorn watchfiles==0.24.0 # via uvicorn -websockets==13.0 +websockets==13.1 # via uvicorn diff --git a/site/content/en/docs/manual/advanced/formats/_index.md b/site/content/en/docs/manual/advanced/formats/_index.md index 1640e582c14..f4d30a45baa 100644 --- a/site/content/en/docs/manual/advanced/formats/_index.md +++ b/site/content/en/docs/manual/advanced/formats/_index.md @@ -24,7 +24,7 @@ The table below outlines the available formats for data export in CVAT. | Format | Type | Computer Vision Task | Models | Shapes | Attributes | Video Tracks | -|------------------------------------------------------------------------------------------------------------------------------------| ------------- |-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| -------------------- | ------------- | +|------------------------------------------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| -------------------- | ------------- | | [CamVid 1.0](format-camvid) | .txt
.png | Semantic
Segmentation | U-Net, SegNet, DeepLab,
PSPNet, FCN, Mask R-CNN,
ICNet, ERFNet, HRNet,
V-Net, and others. | Polygons | Not supported | Not supported | | [Cityscapes 1.0](format-cityscapes) | .txt
.png | Semantic
Segmentation | U-Net, SegNet, DeepLab,
PSPNet, FCN, ERFNet,
ICNet, Mask R-CNN, HRNet,
ENet, and others. | Polygons | Specific attributes | Not supported | | [COCO 1.0](format-coco) | JSON | Detection, Semantic
Segmentation | YOLO (You Only Look Once),
Faster R-CNN, Mask R-CNN, SSD (Single Shot MultiBox Detector),
RetinaNet, EfficientDet, UNet,
DeepLabv3+, CenterNet, Cascade R-CNN, and others. | Bounding Boxes, Polygons | Specific attributes | Not supported | @@ -50,6 +50,8 @@ The table below outlines the available formats for data export in CVAT. | [YOLOv8 Segmentation 1.0](format-yolov8) | .txt | Instance Segmentation | YOLOv8 | Polygons, Masks | No attributes | Not supported | | [YOLOv8 Pose 1.0](format-yolov8) | .txt | Keypoints | YOLOv8 | Skeletons | No attributes | Not supported | | [YOLOv8 Oriented Bounding Boxes 1.0](format-yolov8) | .txt | Detection | YOLOv8 | Bounding Boxes | No attributes | Not supported | +| [YOLOv8 Classification 1.0](format-yolov8-classification) | .jpg | Classification | YOLOv8 | Tags | No attributes | Not supported | + diff --git a/site/content/en/docs/manual/advanced/formats/format-yolov8-classification.md b/site/content/en/docs/manual/advanced/formats/format-yolov8-classification.md new file mode 100644 index 00000000000..8857c11518b --- /dev/null +++ b/site/content/en/docs/manual/advanced/formats/format-yolov8-classification.md @@ -0,0 +1,40 @@ +--- +title: 'YOLOv8-Classification' +linkTitle: 'YOLOv8-Classification' +weight: 7 +description: 'How to export and import data in YOLOv8 Classification format' +--- + +For more information, see: + +- [Format specification](https://docs.ultralytics.com/datasets/classify/) +- [Dataset examples](https://github.com/cvat-ai/datumaro/tree/develop/tests/assets/yolo_dataset/yolov8_classification) + +## YOLOv8 Classification export + +For export of images: + +- Supported annotations: Tags. +- Attributes: Not supported. +- Tracks: Not supported. + +The downloaded file is a .zip archive with the following structure: + +```bash +archive.zip/ +├── train +│ ├── labels.json # CVAT extension. Contains original ids and labels +│ │ # is not needed when using dataset with YOLOv8 framework +│ │ # but is useful when importing it back to CVAT +│ ├── label_0 +│ │ ├── .jpg +│ │ ├── .jpg +│ │ ├── .jpg +│ │ ├── ... +│ ├── label_1 +│ │ ├── .jpg +│ │ ├── .jpg +│ │ ├── .jpg +│ │ ├── ... +├── ... +```