Skip to content

Commit

Permalink
YOLOv8 classification format (#8475)
Browse files Browse the repository at this point in the history
<!-- Raise an issue to propose your change
(https://github.com/cvat-ai/cvat/issues).
It helps to avoid duplication of efforts from multiple independent
contributors.
Discuss your ideas with maintainers to be sure that changes will be
approved and merged.
Read the [Contribution guide](https://docs.cvat.ai/docs/contributing/).
-->

<!-- Provide a general summary of your changes in the Title above -->

### Motivation and context
<!-- Why is this change required? What problem does it solve? If it
fixes an open
issue, please link to the issue here. Describe your changes in detail,
add
screenshots. -->
Supporting YOLOv8 classification format

### How has this been tested?
<!-- Please describe in detail how you tested your changes.
Include details of your testing environment, and the tests you ran to
see how your change affects other areas of the code, etc. -->

### Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply.
If an item isn't applicable for some reason, then ~~explicitly
strikethrough~~ the whole
line. If you don't do that, GitHub will show incorrect progress for the
pull request.
If you're unsure about any of these, don't hesitate to ask. We're here
to help! -->
- [ ] I submit my changes into the `develop` branch
- [ ] I have created a changelog fragment <!-- see top comment in
CHANGELOG.md -->
- [ ] I have updated the documentation accordingly
- [ ] I have added tests to cover my changes
- [ ] I have linked related issues (see [GitHub docs](

https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword))
- [ ] I have increased versions of npm packages if it is necessary

([cvat-canvas](https://github.com/cvat-ai/cvat/tree/develop/cvat-canvas#versioning),

[cvat-core](https://github.com/cvat-ai/cvat/tree/develop/cvat-core#versioning),

[cvat-data](https://github.com/cvat-ai/cvat/tree/develop/cvat-data#versioning)
and

[cvat-ui](https://github.com/cvat-ai/cvat/tree/develop/cvat-ui#versioning))

### License

- [ ] I submit _my code changes_ under the same [MIT License](
https://github.com/cvat-ai/cvat/blob/develop/LICENSE) that covers the
project.
  Feel free to contact the maintainers if that's a concern.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for the "YOLOv8 Classification" format in the
application, including export and import functionalities.
- Updated documentation to include details about the new YOLOv8
Classification format.

- **Bug Fixes**
- Enhanced test coverage for the new YOLOv8 Classification format to
ensure proper functionality in export and import processes.

- **Documentation**
- Integrated new entries in the README and detailed documentation for
the YOLOv8 Classification format.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
Eldies authored Oct 7, 2024
1 parent 1285858 commit 3cde309
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 73 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/) | ✔️ | ✔️ |

<!--lint enable maximum-line-length-->

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Added

- Support for YOLOv8 Classification format
(<https://github.com/cvat-ai/cvat/pull/8475>)
10 changes: 10 additions & 0 deletions cvat/apps/dataset_manager/formats/yolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
14 changes: 14 additions & 0 deletions cvat/apps/dataset_manager/tests/assets/annotations.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [],
Expand Down
3 changes: 3 additions & 0 deletions cvat/apps/dataset_manager/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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'),
Expand Down
60 changes: 25 additions & 35 deletions cvat/apps/dataset_manager/tests/test_rest_api_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion cvat/requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
54 changes: 27 additions & 27 deletions cvat/requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SHA1:576a1a528bac33626df6c8d11c983263f7a175eb
# SHA1:9ff984f33ae139c68d90acc3e338c6cef7ecf6e9
#
# This file is autogenerated by pip-compile-multi
# To update, run:
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Loading

0 comments on commit 3cde309

Please sign in to comment.