From fcf4ac1cf2c0f3e323a1018269da24b778c34150 Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Thu, 12 Dec 2024 19:38:11 +0100 Subject: [PATCH 1/7] add conv_mask_to_poly parameter to dataset upload --- ...241212_185016_knotekjaroslav_add_mask_poly_conv_param.md | 4 ++++ cvat-sdk/cvat_sdk/core/proxies/projects.py | 3 +++ cvat-sdk/cvat_sdk/core/uploading.py | 6 ++++++ 3 files changed, 13 insertions(+) create mode 100644 changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md diff --git a/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md b/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md new file mode 100644 index 000000000000..f7c9d6adf56b --- /dev/null +++ b/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md @@ -0,0 +1,4 @@ +### Added + +- Added parameter `conv_mask_to_poly` to SDK's `project.export_dataset` method to allow conversion upon adding annotations. + () diff --git a/cvat-sdk/cvat_sdk/core/proxies/projects.py b/cvat-sdk/cvat_sdk/core/proxies/projects.py index 0ea7063eaf72..4ae81e3e7095 100644 --- a/cvat-sdk/cvat_sdk/core/proxies/projects.py +++ b/cvat-sdk/cvat_sdk/core/proxies/projects.py @@ -49,6 +49,7 @@ def import_dataset( format_name: str, filename: StrPath, *, + conv_mask_to_poly: Optional[bool] = None, status_check_period: Optional[int] = None, pbar: Optional[ProgressReporter] = None, ): @@ -63,6 +64,7 @@ def import_dataset( filename, format_name, url_params={"id": self.id}, + conv_mask_to_poly: Optional[bool] = None, pbar=pbar, status_check_period=status_check_period, ) @@ -110,6 +112,7 @@ def create_from_dataset( dataset_format: str = "CVAT XML 1.1", status_check_period: int = None, pbar: Optional[ProgressReporter] = None, + conv_mask_to_poly: Optional[bool] = None, ) -> Project: """ Create a new project with the given name and labels JSON and diff --git a/cvat-sdk/cvat_sdk/core/uploading.py b/cvat-sdk/cvat_sdk/core/uploading.py index 068e4d89a0c3..b822beb52bbc 100644 --- a/cvat-sdk/cvat_sdk/core/uploading.py +++ b/cvat-sdk/cvat_sdk/core/uploading.py @@ -281,11 +281,17 @@ def upload_file_and_wait( format_name: str, *, url_params: Optional[dict[str, Any]] = None, + conv_mask_to_poly: Optional[bool] = None, pbar: Optional[ProgressReporter] = None, status_check_period: Optional[int] = None, ): url = self._client.api_map.make_endpoint_url(upload_endpoint.path, kwsub=url_params) params = {"format": format_name, "filename": filename.name} + + if conv_mask_top_poly is not None: + value = "true" if conv_mask_to_poly else "false" + params["conv_mask_to_poly"] = value + response = self.upload_file( url, filename, pbar=pbar, query_params=params, meta={"filename": params["filename"]} ) From d06efffffa62eede05c1b829fc44d4a2b4731470 Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Thu, 12 Dec 2024 19:52:46 +0100 Subject: [PATCH 2/7] fix typos --- cvat-sdk/cvat_sdk/core/proxies/projects.py | 2 +- cvat-sdk/cvat_sdk/core/uploading.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat-sdk/cvat_sdk/core/proxies/projects.py b/cvat-sdk/cvat_sdk/core/proxies/projects.py index 4ae81e3e7095..4f7277bd1d8f 100644 --- a/cvat-sdk/cvat_sdk/core/proxies/projects.py +++ b/cvat-sdk/cvat_sdk/core/proxies/projects.py @@ -64,7 +64,7 @@ def import_dataset( filename, format_name, url_params={"id": self.id}, - conv_mask_to_poly: Optional[bool] = None, + conv_mask_to_poly=conv_mask_to_poly, pbar=pbar, status_check_period=status_check_period, ) diff --git a/cvat-sdk/cvat_sdk/core/uploading.py b/cvat-sdk/cvat_sdk/core/uploading.py index b822beb52bbc..1781042619f9 100644 --- a/cvat-sdk/cvat_sdk/core/uploading.py +++ b/cvat-sdk/cvat_sdk/core/uploading.py @@ -288,7 +288,7 @@ def upload_file_and_wait( url = self._client.api_map.make_endpoint_url(upload_endpoint.path, kwsub=url_params) params = {"format": format_name, "filename": filename.name} - if conv_mask_top_poly is not None: + if conv_mask_to_poly is not None: value = "true" if conv_mask_to_poly else "false" params["conv_mask_to_poly"] = value From b15418a218d00bf61cc12e46a48e350157c890c7 Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Thu, 12 Dec 2024 19:54:51 +0100 Subject: [PATCH 3/7] fix PR changelog according to bots recommendation --- ...1212_185016_knotekjaroslav_add_mask_poly_conv_param.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md b/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md index f7c9d6adf56b..4ec140f87a08 100644 --- a/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md +++ b/changelog.d/20241212_185016_knotekjaroslav_add_mask_poly_conv_param.md @@ -1,4 +1,8 @@ ### Added -- Added parameter `conv_mask_to_poly` to SDK's `project.export_dataset` method to allow conversion upon adding annotations. - () +Added parameter `conv_mask_to_poly` to the following SDK methods to control mask-to-polygon conversion: +- `project.export_dataset` +- `project.import_dataset` +- `project.create_from_dataset` +- `DatasetUploader.upload_file_and_wait` +([#8823](https://github.com/cvat-ai/cvat/pull/8823)) From 039d5121ac919e0f21df80c2d86f46154d3bb1c9 Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Thu, 2 Jan 2025 14:31:42 +0100 Subject: [PATCH 4/7] add missing parameter passing --- cvat-sdk/cvat_sdk/core/proxies/projects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cvat-sdk/cvat_sdk/core/proxies/projects.py b/cvat-sdk/cvat_sdk/core/proxies/projects.py index 4f7277bd1d8f..97d65016134c 100644 --- a/cvat-sdk/cvat_sdk/core/proxies/projects.py +++ b/cvat-sdk/cvat_sdk/core/proxies/projects.py @@ -129,6 +129,7 @@ def create_from_dataset( filename=dataset_path, pbar=pbar, status_check_period=status_check_period, + conv_mask_to_poly=conv_mask_to_poly, ) project.fetch() From f2a634ea8e9b7c77dc2fb4e3b2c47dd2ee7f6b9a Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Thu, 2 Jan 2025 14:31:48 +0100 Subject: [PATCH 5/7] add test --- tests/python/sdk/test_projects.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/python/sdk/test_projects.py b/tests/python/sdk/test_projects.py index db0b5f265586..e4277de0525c 100644 --- a/tests/python/sdk/test_projects.py +++ b/tests/python/sdk/test_projects.py @@ -162,6 +162,23 @@ def test_can_create_project_from_dataset(self, fxt_coco_dataset: Path): assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1] assert self.stdout.getvalue() == "" + def test_can_create_project_from_dataset_without_poly_masks(self, fxt_coco_dataset: Path): + pbar_out = io.StringIO() + pbar = make_pbar(file=pbar_out) + + project = self.client.projects.create_from_dataset( + spec=models.ProjectWriteRequest(name="project with data"), + dataset_path=fxt_coco_dataset, + dataset_format="COCO 1.0", + conv_mask_to_poly=False, + pbar=pbar, + ) + + assert project.get_tasks()[0].size == 1 + assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1] + assert self.stdout.getvalue() == "" + + def test_can_retrieve_project(self, fxt_new_project: Project): project_id = fxt_new_project.id From 68d273563bb8523261e42caa4dd9494ed4b8b965 Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Mon, 13 Jan 2025 11:08:39 +0100 Subject: [PATCH 6/7] add suggested testing changes --- tests/python/sdk/test_projects.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/python/sdk/test_projects.py b/tests/python/sdk/test_projects.py index e4277de0525c..92901047a4e2 100644 --- a/tests/python/sdk/test_projects.py +++ b/tests/python/sdk/test_projects.py @@ -162,7 +162,10 @@ def test_can_create_project_from_dataset(self, fxt_coco_dataset: Path): assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1] assert self.stdout.getvalue() == "" - def test_can_create_project_from_dataset_without_poly_masks(self, fxt_coco_dataset: Path): + @pytest.mark.parametrize("convert", [True, False]) + def test_can_create_project_from_dataset_with_polygons_to_masks_param( + self, fxt_coco_dataset: Path, convert: bool + ): pbar_out = io.StringIO() pbar = make_pbar(file=pbar_out) @@ -178,6 +181,12 @@ def test_can_create_project_from_dataset_without_poly_masks(self, fxt_coco_datas assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1] assert self.stdout.getvalue() == "" + task = project.get_tasks()[0] + imported_annotations = task.get_annotations() + assert all([ + s.type.value == "polygon" if convert else "mask" + for s in imported_annotations.shapes + ]) def test_can_retrieve_project(self, fxt_new_project: Project): project_id = fxt_new_project.id From fd1bcb889f2e5912d2e71cee008b718cc874a3eb Mon Sep 17 00:00:00 2001 From: Jaroslav Knotek Date: Fri, 24 Jan 2025 12:45:37 +0100 Subject: [PATCH 7/7] replace tests format coco with camvid --- tests/python/sdk/fixtures.py | 36 +++++++++++++++++++++++++++++++ tests/python/sdk/test_projects.py | 8 +++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/tests/python/sdk/fixtures.py b/tests/python/sdk/fixtures.py index f495c4ce8ef4..6b3cd391ad64 100644 --- a/tests/python/sdk/fixtures.py +++ b/tests/python/sdk/fixtures.py @@ -60,6 +60,42 @@ def fxt_login(admin_user: str, restore_db_per_class): yield (client, user) +@pytest.fixture +def fxt_camvid_dataset(tmp_path: Path): + img_path = tmp_path / "img.png" + with img_path.open("wb") as f: + f.write(generate_image_file(filename=str(img_path), size=(5, 10)).getvalue()) + + annot_path = tmp_path / "annot.png" + r,g,b = (127,0,0) + annot = generate_image_file( + filename=str(annot_path), + size=(5, 10), + color=(r, g, b), + ).getvalue() + with annot_path.open("wb") as f: + f.write(annot) + + label_colors_path = tmp_path / "label_colors.txt" + with open(label_colors_path, "w") as f: + f.write(f"{r} {g} {b} ROI\n") + + dataset_img_path = "default/img.png" + dataset_annot_path = "default/annot.png" + default_txt_path = tmp_path / "default.txt" + with open(default_txt_path, "w") as f: + f.write(f"{dataset_img_path} {dataset_annot_path}") + + dataset_path = tmp_path / "camvid_dataset.zip" + with ZipFile(dataset_path, "x") as f: + f.write(img_path, arcname=dataset_img_path) + f.write(annot_path, arcname=dataset_annot_path) + f.write(default_txt_path, arcname="default.txt") + f.write(label_colors_path, arcname="label_colors.txt") + + yield dataset_path + + @pytest.fixture def fxt_coco_dataset(tmp_path: Path, fxt_image_file: Path, fxt_coco_file: Path): dataset_path = tmp_path / "coco_dataset.zip" diff --git a/tests/python/sdk/test_projects.py b/tests/python/sdk/test_projects.py index 92901047a4e2..2a565593581f 100644 --- a/tests/python/sdk/test_projects.py +++ b/tests/python/sdk/test_projects.py @@ -164,16 +164,16 @@ def test_can_create_project_from_dataset(self, fxt_coco_dataset: Path): @pytest.mark.parametrize("convert", [True, False]) def test_can_create_project_from_dataset_with_polygons_to_masks_param( - self, fxt_coco_dataset: Path, convert: bool + self, fxt_camvid_dataset: Path, convert: bool ): pbar_out = io.StringIO() pbar = make_pbar(file=pbar_out) project = self.client.projects.create_from_dataset( spec=models.ProjectWriteRequest(name="project with data"), - dataset_path=fxt_coco_dataset, - dataset_format="COCO 1.0", - conv_mask_to_poly=False, + dataset_path=fxt_camvid_dataset, + dataset_format="Camvid 1.0", + conv_mask_to_poly=convert, pbar=pbar, )