From e099cab551712673362949ea9b40e2b8639e0d7b Mon Sep 17 00:00:00 2001 From: FelixAbrahamsson Date: Mon, 26 May 2025 09:09:34 +0200 Subject: [PATCH 1/2] feature: upload polyline annotations --- next_cvat/__init__.py | 1 + next_cvat/client/job_annotations.py | 16 +++++++++ next_cvat/types/polyline.py | 18 ++++++++++ tests/test_add_polyline.py | 54 +++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 tests/test_add_polyline.py diff --git a/next_cvat/__init__.py b/next_cvat/__init__.py index 6682968..a7534f7 100644 --- a/next_cvat/__init__.py +++ b/next_cvat/__init__.py @@ -12,6 +12,7 @@ LabelAttribute, Mask, Polygon, + Polyline, Project, Tag, Task, diff --git a/next_cvat/client/job_annotations.py b/next_cvat/client/job_annotations.py index db5d124..c1ae380 100644 --- a/next_cvat/client/job_annotations.py +++ b/next_cvat/client/job_annotations.py @@ -35,6 +35,22 @@ def add_mask_( return self + def add_polyline_( + self, + polyline: next_cvat.Polyline, + image_name: str, + group: int = 0, + ) -> JobAnnotations: + label = self.job.task.project.label(name=polyline.label) + + frame = self.job.task.frame(image_name=image_name) + + self.annotations["shapes"].append( + polyline.request(frame=frame.id, label_id=label.id, group=group) + ) + + return self + def request(self) -> models.LabeledDataRequest: request = models.LabeledDataRequest() request.version = self.annotations["version"] diff --git a/next_cvat/types/polyline.py b/next_cvat/types/polyline.py index f0dd526..f2be625 100644 --- a/next_cvat/types/polyline.py +++ b/next_cvat/types/polyline.py @@ -2,6 +2,8 @@ from typing import List, Tuple +import numpy as np +from cvat_sdk.api_client import models from pydantic import BaseModel, field_validator from .attribute import Attribute @@ -33,3 +35,19 @@ def topmost(self) -> float: def bottommost(self) -> float: return max([y for _, y in self.points]) + + def request( + self, frame: int, label_id: int, group: int = 0 + ) -> models.LabeledShapeRequest: + return models.LabeledShapeRequest( + type="polyline", + occluded=bool(self.occluded), + points=np.array(self.points).flatten().tolist(), + rotation=0.0, + outside=False, + attributes=[attr.model_dump() for attr in self.attributes], + group=group, + source=self.source, + frame=frame, + label_id=label_id, + ) diff --git a/tests/test_add_polyline.py b/tests/test_add_polyline.py new file mode 100644 index 0000000..9dfc804 --- /dev/null +++ b/tests/test_add_polyline.py @@ -0,0 +1,54 @@ +from pathlib import Path + +import numpy as np +import pytest + +import next_cvat + + +def test_add_polyline(): + if not Path(".env.cvat.secrets").exists(): + pytest.skip("No secrets file found") + + client = next_cvat.Client.from_env_file(".env.cvat.secrets") + + project_id = 198488 # Using the same test project as in test_add_mask + job_id = 1442235 + task_id = 999670 + deformation_attribute_id = 1389238 + + job = client.project(project_id).task(task_id).job(job_id) + + annotations = job.annotations() + + # Create a simple polyline (e.g., a zigzag pattern) + points = np.array( + [ + [100, 100], + [200, 150], + [300, 100], + [400, 150], + ] + ).tolist() + + annotations.add_polyline_( + next_cvat.Polyline( + points=points, + label="Deformation", + source="manual", + occluded=False, + z_order=0, + attributes=[ + next_cvat.Attribute( + name="Severity", + value="High", + spec_id=deformation_attribute_id, + ) + ], + ), + image_name="20240916_000854_2011T_437.bmp", + ) + + job.update_annotations_(annotations) + + print("Successfully added polyline annotation") From 7c248706a1e86b849ab1f484db8ba1026086c833 Mon Sep 17 00:00:00 2001 From: FelixAbrahamsson Date: Mon, 26 May 2025 11:11:43 +0200 Subject: [PATCH 2/2] feature: upload tags --- next_cvat/client/job_annotations.py | 16 ++++++++++++ next_cvat/types/tag.py | 12 +++++++++ tests/test_add_tag.py | 40 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/test_add_tag.py diff --git a/next_cvat/client/job_annotations.py b/next_cvat/client/job_annotations.py index c1ae380..dd8106c 100644 --- a/next_cvat/client/job_annotations.py +++ b/next_cvat/client/job_annotations.py @@ -51,6 +51,22 @@ def add_polyline_( return self + def add_tag_( + self, + tag: next_cvat.Tag, + image_name: str, + group: int = 0, + ) -> JobAnnotations: + label = self.job.task.project.label(name=tag.label) + + frame = self.job.task.frame(image_name=image_name) + + self.annotations["tags"].append( + tag.request(frame=frame.id, label_id=label.id, group=group) + ) + + return self + def request(self) -> models.LabeledDataRequest: request = models.LabeledDataRequest() request.version = self.annotations["version"] diff --git a/next_cvat/types/tag.py b/next_cvat/types/tag.py index e696b88..48cd089 100644 --- a/next_cvat/types/tag.py +++ b/next_cvat/types/tag.py @@ -2,6 +2,7 @@ from typing import List +from cvat_sdk.api_client import models from pydantic import BaseModel from .attribute import Attribute @@ -11,3 +12,14 @@ class Tag(BaseModel): label: str source: str attributes: List[Attribute] + + def request( + self, frame: int, label_id: int, group: int = 0 + ) -> models.LabeledImageRequest: + return models.LabeledImageRequest( + frame=frame, + label_id=label_id, + group=group, + source=self.source, + attributes=[attr.model_dump() for attr in self.attributes], + ) diff --git a/tests/test_add_tag.py b/tests/test_add_tag.py new file mode 100644 index 0000000..5b29024 --- /dev/null +++ b/tests/test_add_tag.py @@ -0,0 +1,40 @@ +from pathlib import Path + +import pytest + +import next_cvat + + +def test_add_tag(): + if not Path(".env.cvat.secrets").exists(): + pytest.skip("No secrets file found") + + client = next_cvat.Client.from_env_file(".env.cvat.secrets") + + project_id = 198488 # Using the same test project as in other tests + job_id = 1442235 + task_id = 999670 + deformation_attribute_id = 1389238 + + job = client.project(project_id).task(task_id).job(job_id) + + annotations = job.annotations() + + annotations.add_tag_( + next_cvat.Tag( + label="Deformation", + source="manual", + attributes=[ + next_cvat.Attribute( + name="Severity", + value="High", + spec_id=deformation_attribute_id, + ) + ], + ), + image_name="20240916_000854_2011T_437.bmp", + ) + + job.update_annotations_(annotations) + + print("Successfully added tag annotation")