From 186255172d25fccddd638b1ec512bf75abd79be7 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Fri, 13 Dec 2024 19:19:28 +0000 Subject: [PATCH 01/10] Regression test for missing frames after exporting a CVAT dataset --- .vscode/launch.json | 2 + tests/python/requirements.txt | 2 + tests/python/rest_api/test_tasks.py | 67 ++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 78f24c96ca83..cb4b0f9dcf0f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -444,6 +444,8 @@ "python": "${command:python.interpreterPath}", "module": "pytest", "args": [ + "--verbose", + "--no-cov", // vscode debugger might not work otherwise "tests/python/rest_api/" ], "cwd": "${workspaceFolder}", diff --git a/tests/python/requirements.txt b/tests/python/requirements.txt index 5dfad3d6f7fb..d43d9b61d5df 100644 --- a/tests/python/requirements.txt +++ b/tests/python/requirements.txt @@ -10,3 +10,5 @@ Pillow==10.3.0 python-dateutil==2.8.2 pyyaml==6.0.0 numpy==2.0.0 + +# TODO: update pytest to 7.0.0 and pytest-timeout to 2.3.1 (better debug in vscode) \ No newline at end of file diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index ea81db5354bb..7dc4dc7152cb 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -24,7 +24,7 @@ from itertools import chain, groupby, product from math import ceil from operator import itemgetter -from pathlib import Path +from pathlib import Path, PurePosixPath from tempfile import NamedTemporaryFile, TemporaryDirectory from time import sleep, time from typing import Any, Callable, ClassVar, Optional, Union @@ -1012,6 +1012,71 @@ def test_datumaro_export_without_annotations_includes_image_info( if "size" in related_image: assert tuple(related_image["size"]) > (0, 0) + @pytest.mark.parametrize( + "format_name, num_frames, frame_step, expected_frames", + [ + ("Datumaro 1.0", 100, 5, 20), + ("COCO 1.0", 100, 5, 20), + ("CVAT for video 1.1", 100, 5, 20), # no remainder + ("CVAT for video 1.1", 97, 5, 20), # prime + ("CVAT for video 1.1", 97, 2, 49), + ("CVAT for video 1.1", 100, 3, 34), # three + # we assert that expected frames are ceil(frames / step) + ] + ) + def test_export_with_non_default_frame_step( + self, + # fixtures + tmp_path: Path, + admin_user: str, + # params + format_name: str, + num_frames: int, + frame_step: int, + expected_frames: int, + ): + # parameter validation + assert expected_frames == math.ceil(num_frames / frame_step), 'Test params are wrong' + + spec = { + 'name': f"test_video_frames_in_{format_name}_after_export", + "labels": [{"name": "goofy ahh car"}] + } + + data = { + 'image_quality': 70, + 'client_files': [generate_video_file(num_frames)], + 'frame_filter': f"step={frame_step}" + } + + # create a task and get its instance + (task_id, _) = create_task(admin_user, spec, data) + with make_sdk_client(admin_user) as client: + task_obj: Task = client.tasks.retrieve(task_id) + + # export the video + dataset_file = tmp_path / "dataset.zip" + task_obj.export_dataset(format_name, dataset_file, include_images=True) + + def get_png_index(zinfo: zipfile.ZipInfo) -> int: + name = PurePosixPath(zinfo.filename) + if name.suffix.lower() != '.png': + return -1 + name = os.path.basename(name).removesuffix(name.suffix) + idx = name[name.rfind('_') + 1:] + assert idx.isnumeric() + return int(idx) + + # get frames and sort them + with zipfile.ZipFile(dataset_file) as dataset: + frames = [png_idx for png_idx in map(get_png_index, dataset.filelist) if png_idx != -1] + frames.sort() + + + assert len(frames) == expected_frames, 'Some frames were lost' + assert frames == list(range(0, num_frames, frame_step)), 'Some frames are wrong' + + @pytest.mark.usefixtures("restore_db_per_function") @pytest.mark.usefixtures("restore_cvat_data_per_function") From 7dfe012843d022483c5329b5c544611b1994ac52 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Fri, 13 Dec 2024 20:28:32 +0000 Subject: [PATCH 02/10] linted changes with Black --- tests/python/rest_api/test_tasks.py | 44 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index 7dc4dc7152cb..6ca65d23073c 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -1013,16 +1013,16 @@ def test_datumaro_export_without_annotations_includes_image_info( assert tuple(related_image["size"]) > (0, 0) @pytest.mark.parametrize( - "format_name, num_frames, frame_step, expected_frames", - [ - ("Datumaro 1.0", 100, 5, 20), - ("COCO 1.0", 100, 5, 20), - ("CVAT for video 1.1", 100, 5, 20), # no remainder - ("CVAT for video 1.1", 97, 5, 20), # prime - ("CVAT for video 1.1", 97, 2, 49), - ("CVAT for video 1.1", 100, 3, 34), # three - # we assert that expected frames are ceil(frames / step) - ] + "format_name, num_frames, frame_step, expected_frames", + [ + ("Datumaro 1.0", 100, 5, 20), + ("COCO 1.0", 100, 5, 20), + ("CVAT for video 1.1", 100, 5, 20), # no remainder + ("CVAT for video 1.1", 97, 5, 20), # prime + ("CVAT for video 1.1", 97, 2, 49), + ("CVAT for video 1.1", 100, 3, 34), # three + # we assert that expected frames are ceil(frames / step) + ], ) def test_export_with_non_default_frame_step( self, @@ -1036,23 +1036,23 @@ def test_export_with_non_default_frame_step( expected_frames: int, ): # parameter validation - assert expected_frames == math.ceil(num_frames / frame_step), 'Test params are wrong' + assert expected_frames == math.ceil(num_frames / frame_step), "Test params are wrong" spec = { - 'name': f"test_video_frames_in_{format_name}_after_export", - "labels": [{"name": "goofy ahh car"}] + "name": f"test_video_frames_in_{format_name}_after_export", + "labels": [{"name": "goofy ahh car"}], } data = { - 'image_quality': 70, - 'client_files': [generate_video_file(num_frames)], - 'frame_filter': f"step={frame_step}" + "image_quality": 70, + "client_files": [generate_video_file(num_frames)], + "frame_filter": f"step={frame_step}", } # create a task and get its instance (task_id, _) = create_task(admin_user, spec, data) with make_sdk_client(admin_user) as client: - task_obj: Task = client.tasks.retrieve(task_id) + task_obj: Task = client.tasks.retrieve(task_id) # export the video dataset_file = tmp_path / "dataset.zip" @@ -1060,10 +1060,10 @@ def test_export_with_non_default_frame_step( def get_png_index(zinfo: zipfile.ZipInfo) -> int: name = PurePosixPath(zinfo.filename) - if name.suffix.lower() != '.png': + if name.suffix.lower() != ".png": return -1 name = os.path.basename(name).removesuffix(name.suffix) - idx = name[name.rfind('_') + 1:] + idx = name[name.rfind("_") + 1 :] assert idx.isnumeric() return int(idx) @@ -1072,10 +1072,8 @@ def get_png_index(zinfo: zipfile.ZipInfo) -> int: frames = [png_idx for png_idx in map(get_png_index, dataset.filelist) if png_idx != -1] frames.sort() - - assert len(frames) == expected_frames, 'Some frames were lost' - assert frames == list(range(0, num_frames, frame_step)), 'Some frames are wrong' - + assert len(frames) == expected_frames, "Some frames were lost" + assert frames == list(range(0, num_frames, frame_step)), "Some frames are wrong" @pytest.mark.usefixtures("restore_db_per_function") From 31963ef52b900b0f358bff9498a98fbb68fadf9f Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 16 Dec 2024 12:24:50 +0000 Subject: [PATCH 03/10] add fixtures for reseting db and cache per function --- tests/python/rest_api/test_tasks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index 6ca65d23073c..fdf3669f9562 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -1012,6 +1012,8 @@ def test_datumaro_export_without_annotations_includes_image_info( if "size" in related_image: assert tuple(related_image["size"]) > (0, 0) + @pytest.mark.usefixtures("restore_db_per_function") + @pytest.mark.usefixtures("restore_redis_ondisk_per_function") @pytest.mark.parametrize( "format_name, num_frames, frame_step, expected_frames", [ @@ -1060,7 +1062,7 @@ def test_export_with_non_default_frame_step( def get_png_index(zinfo: zipfile.ZipInfo) -> int: name = PurePosixPath(zinfo.filename) - if name.suffix.lower() != ".png": + if name.suffix.lower() != ".png": # png is usually for video return -1 name = os.path.basename(name).removesuffix(name.suffix) idx = name[name.rfind("_") + 1 :] From 0d3e47709edd15b1a30420e0ef1184a99ef69959 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Mon, 16 Dec 2024 12:30:12 +0000 Subject: [PATCH 04/10] linted code --- tests/python/rest_api/test_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index fdf3669f9562..e580ee863432 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -1062,7 +1062,7 @@ def test_export_with_non_default_frame_step( def get_png_index(zinfo: zipfile.ZipInfo) -> int: name = PurePosixPath(zinfo.filename) - if name.suffix.lower() != ".png": # png is usually for video + if name.suffix.lower() != ".png": # png is usually for video return -1 name = os.path.basename(name).removesuffix(name.suffix) idx = name[name.rfind("_") + 1 :] From 87312a420049059c600531ccfb074216f27bc2c5 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Tue, 17 Dec 2024 22:32:00 +0000 Subject: [PATCH 05/10] add params image/video, start_frame + fixture for an image/video task --- tests/python/rest_api/test_tasks.py | 135 +++++++++++++++------------- 1 file changed, 71 insertions(+), 64 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index e580ee863432..7f310b74e604 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -1012,70 +1012,6 @@ def test_datumaro_export_without_annotations_includes_image_info( if "size" in related_image: assert tuple(related_image["size"]) > (0, 0) - @pytest.mark.usefixtures("restore_db_per_function") - @pytest.mark.usefixtures("restore_redis_ondisk_per_function") - @pytest.mark.parametrize( - "format_name, num_frames, frame_step, expected_frames", - [ - ("Datumaro 1.0", 100, 5, 20), - ("COCO 1.0", 100, 5, 20), - ("CVAT for video 1.1", 100, 5, 20), # no remainder - ("CVAT for video 1.1", 97, 5, 20), # prime - ("CVAT for video 1.1", 97, 2, 49), - ("CVAT for video 1.1", 100, 3, 34), # three - # we assert that expected frames are ceil(frames / step) - ], - ) - def test_export_with_non_default_frame_step( - self, - # fixtures - tmp_path: Path, - admin_user: str, - # params - format_name: str, - num_frames: int, - frame_step: int, - expected_frames: int, - ): - # parameter validation - assert expected_frames == math.ceil(num_frames / frame_step), "Test params are wrong" - - spec = { - "name": f"test_video_frames_in_{format_name}_after_export", - "labels": [{"name": "goofy ahh car"}], - } - - data = { - "image_quality": 70, - "client_files": [generate_video_file(num_frames)], - "frame_filter": f"step={frame_step}", - } - - # create a task and get its instance - (task_id, _) = create_task(admin_user, spec, data) - with make_sdk_client(admin_user) as client: - task_obj: Task = client.tasks.retrieve(task_id) - - # export the video - dataset_file = tmp_path / "dataset.zip" - task_obj.export_dataset(format_name, dataset_file, include_images=True) - - def get_png_index(zinfo: zipfile.ZipInfo) -> int: - name = PurePosixPath(zinfo.filename) - if name.suffix.lower() != ".png": # png is usually for video - return -1 - name = os.path.basename(name).removesuffix(name.suffix) - idx = name[name.rfind("_") + 1 :] - assert idx.isnumeric() - return int(idx) - - # get frames and sort them - with zipfile.ZipFile(dataset_file) as dataset: - frames = [png_idx for png_idx in map(get_png_index, dataset.filelist) if png_idx != -1] - frames.sort() - - assert len(frames) == expected_frames, "Some frames were lost" - assert frames == list(range(0, num_frames, frame_step)), "Some frames are wrong" @pytest.mark.usefixtures("restore_db_per_function") @@ -2964,6 +2900,7 @@ def _uploaded_images_task_with_honeypots_and_segments_base( @fixture(scope="class") def fxt_uploaded_images_task_with_honeypots_and_segments( + self, request: pytest.FixtureRequest ) -> Generator[tuple[_TaskSpec, int], None, None]: yield from self._uploaded_images_task_with_honeypots_and_segments_base(request) @@ -6414,3 +6351,73 @@ def check_element_outside_count(track_idx, element_idx, expected_count): check_element_outside_count(1, 0, 1) check_element_outside_count(1, 1, 2) check_element_outside_count(1, 2, 2) + + +@pytest.mark.usefixtures("restore_db_per_class") +@pytest.mark.usefixtures("restore_redis_ondisk_per_function") +@pytest.mark.usefixtures("restore_redis_ondisk_after_class") +@pytest.mark.usefixtures("restore_redis_inmem_per_function") +class TestPatchExportFrames(TestTaskData): + + @fixture(scope='class') + @parametrize("media_type", [_SourceDataType.images, _SourceDataType.video]) + @parametrize("step", [5]) + @parametrize("frame_count", [20]) + @parametrize("start_frame", [None, 3]) + def fxt_uploaded_media_task( + self, + request: pytest.FixtureRequest, + media_type: _SourceDataType, + step: int, + frame_count: int, + start_frame: Optional[int], + ) -> Generator[tuple[_TaskSpec, Task, str], None, None]: + args = dict( + request=request, + frame_count=frame_count, + step=step, + start_frame=start_frame + ) + + match media_type: + case _SourceDataType.images: + (spec, task_id) = next(self._uploaded_images_task_fxt_base(**args)) + case _SourceDataType.video: + (spec, task_id) = next(self._uploaded_video_task_fxt_base(**args)) + + with make_sdk_client(self._USERNAME) as client: + task = client.tasks.retrieve(task_id) + + yield (spec, task, f"CVAT for {media_type} 1.1") + + + @pytest.mark.usefixtures("restore_redis_ondisk_per_function") + @parametrize("spec, task, format_name", [fixture_ref(fxt_uploaded_media_task)]) + def test_export_with_non_default_frame_step( + self, + tmp_path: Path, + spec: _TaskSpec, + task: Task, + format_name: str + ): + + dataset_file = tmp_path / "dataset.zip" + task.export_dataset(format_name, dataset_file, include_images=True) + + def get_img_index(zinfo: zipfile.ZipInfo) -> int: + name = PurePosixPath(zinfo.filename) + if name.suffix.lower() not in (".png", ".jpg", ".jpeg"): + return -1 + return int(name.stem.rsplit("_", maxsplit=1)[-1]) + + # get frames and sort them + with zipfile.ZipFile(dataset_file) as dataset: + frames = np.array([png_idx for png_idx in map(get_img_index, dataset.filelist) if png_idx != -1]) + frames.sort() + + task_meta = task.get_meta() + (src_start_frame, src_stop_frame, src_frame_step) = task_meta['start_frame'], task_meta['stop_frame'], spec.frame_step + src_end_frame = src_stop_frame - ((src_stop_frame - src_start_frame) % src_frame_step) + src_frame_step + + assert len(frames) == spec.size == task_meta['size'], "Some frames were lost" + assert np.all(frames == np.arange(src_start_frame, src_end_frame, src_frame_step)), "Some frames are wrong" From 7756d4e5d785f7161ec0db388eccd87acc00f5fa Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Tue, 17 Dec 2024 22:40:15 +0000 Subject: [PATCH 06/10] linted code, removed match statement --- tests/python/rest_api/test_tasks.py | 48 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index 7f310b74e604..906b25765651 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -1013,7 +1013,6 @@ def test_datumaro_export_without_annotations_includes_image_info( assert tuple(related_image["size"]) > (0, 0) - @pytest.mark.usefixtures("restore_db_per_function") @pytest.mark.usefixtures("restore_cvat_data_per_function") @pytest.mark.usefixtures("restore_redis_ondisk_per_function") @@ -6359,7 +6358,7 @@ def check_element_outside_count(track_idx, element_idx, expected_count): @pytest.mark.usefixtures("restore_redis_inmem_per_function") class TestPatchExportFrames(TestTaskData): - @fixture(scope='class') + @fixture(scope="class") @parametrize("media_type", [_SourceDataType.images, _SourceDataType.video]) @parametrize("step", [5]) @parametrize("frame_count", [20]) @@ -6372,33 +6371,22 @@ def fxt_uploaded_media_task( frame_count: int, start_frame: Optional[int], ) -> Generator[tuple[_TaskSpec, Task, str], None, None]: - args = dict( - request=request, - frame_count=frame_count, - step=step, - start_frame=start_frame - ) + args = dict(request=request, frame_count=frame_count, step=step, start_frame=start_frame) - match media_type: - case _SourceDataType.images: - (spec, task_id) = next(self._uploaded_images_task_fxt_base(**args)) - case _SourceDataType.video: - (spec, task_id) = next(self._uploaded_video_task_fxt_base(**args)) + if media_type == _SourceDataType.images: + (spec, task_id) = next(self._uploaded_images_task_fxt_base(**args)) + else: + (spec, task_id) = next(self._uploaded_video_task_fxt_base(**args)) with make_sdk_client(self._USERNAME) as client: task = client.tasks.retrieve(task_id) yield (spec, task, f"CVAT for {media_type} 1.1") - @pytest.mark.usefixtures("restore_redis_ondisk_per_function") @parametrize("spec, task, format_name", [fixture_ref(fxt_uploaded_media_task)]) def test_export_with_non_default_frame_step( - self, - tmp_path: Path, - spec: _TaskSpec, - task: Task, - format_name: str + self, tmp_path: Path, spec: _TaskSpec, task: Task, format_name: str ): dataset_file = tmp_path / "dataset.zip" @@ -6412,12 +6400,22 @@ def get_img_index(zinfo: zipfile.ZipInfo) -> int: # get frames and sort them with zipfile.ZipFile(dataset_file) as dataset: - frames = np.array([png_idx for png_idx in map(get_img_index, dataset.filelist) if png_idx != -1]) + frames = np.array( + [png_idx for png_idx in map(get_img_index, dataset.filelist) if png_idx != -1] + ) frames.sort() task_meta = task.get_meta() - (src_start_frame, src_stop_frame, src_frame_step) = task_meta['start_frame'], task_meta['stop_frame'], spec.frame_step - src_end_frame = src_stop_frame - ((src_stop_frame - src_start_frame) % src_frame_step) + src_frame_step - - assert len(frames) == spec.size == task_meta['size'], "Some frames were lost" - assert np.all(frames == np.arange(src_start_frame, src_end_frame, src_frame_step)), "Some frames are wrong" + (src_start_frame, src_stop_frame, src_frame_step) = ( + task_meta["start_frame"], + task_meta["stop_frame"], + spec.frame_step, + ) + src_end_frame = ( + src_stop_frame - ((src_stop_frame - src_start_frame) % src_frame_step) + src_frame_step + ) # taken from TestTaskData._compute_annotation_segment_params + + assert len(frames) == spec.size == task_meta["size"], "Some frames were lost" + assert np.all( + frames == np.arange(src_start_frame, src_end_frame, src_frame_step) + ), "Some frames are wrong" From 338d52aa96a5892f5c0671543f3f3103435b9277 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Tue, 17 Dec 2024 22:58:23 +0000 Subject: [PATCH 07/10] linted code with latest black version (vscode black turned old) --- tests/python/rest_api/test_tasks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index 906b25765651..fbe5914ad533 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -2899,7 +2899,6 @@ def _uploaded_images_task_with_honeypots_and_segments_base( @fixture(scope="class") def fxt_uploaded_images_task_with_honeypots_and_segments( - self, request: pytest.FixtureRequest ) -> Generator[tuple[_TaskSpec, int], None, None]: yield from self._uploaded_images_task_with_honeypots_and_segments_base(request) @@ -6413,7 +6412,7 @@ def get_img_index(zinfo: zipfile.ZipInfo) -> int: ) src_end_frame = ( src_stop_frame - ((src_stop_frame - src_start_frame) % src_frame_step) + src_frame_step - ) # taken from TestTaskData._compute_annotation_segment_params + ) # taken from TestTaskData._compute_annotation_segment_params assert len(frames) == spec.size == task_meta["size"], "Some frames were lost" assert np.all( From e41da1a10b11e2d3ab9566cb7f6d87b5ad6ee7c6 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Wed, 18 Dec 2024 14:46:18 +0000 Subject: [PATCH 08/10] move yield inside ctx to work with the same client later Co-authored-by: Maxim Zhiltsov --- tests/python/rest_api/test_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index fbe5914ad533..f7483925560b 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -6380,7 +6380,7 @@ def fxt_uploaded_media_task( with make_sdk_client(self._USERNAME) as client: task = client.tasks.retrieve(task_id) - yield (spec, task, f"CVAT for {media_type} 1.1") + yield (spec, task, f"CVAT for {media_type} 1.1") @pytest.mark.usefixtures("restore_redis_ondisk_per_function") @parametrize("spec, task, format_name", [fixture_ref(fxt_uploaded_media_task)]) From d9f0d5f6764dde6811bb2c0d9da9fda9672a1c48 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Thu, 19 Dec 2024 11:59:05 +0000 Subject: [PATCH 09/10] factor out end_frame calculations to utils --- tests/python/rest_api/test_tasks.py | 8 +++----- tests/python/rest_api/utils.py | 4 ++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index f7483925560b..7d0bf874bf7e 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -73,6 +73,7 @@ export_task_dataset, parse_frame_step, wait_until_task_is_created, + calc_end_frame, ) @@ -3111,7 +3112,7 @@ def _compute_annotation_segment_params(self, task_spec: _TaskSpec) -> list[tuple stop_frame = getattr(task_spec, "stop_frame", None) or ( start_frame + (task_spec.size - 1) * frame_step ) - end_frame = stop_frame - ((stop_frame - start_frame) % frame_step) + frame_step + end_frame = calc_end_frame(start_frame, stop_frame, frame_step) validation_params = getattr(task_spec, "validation_params", None) if validation_params and validation_params.mode.value == "gt_pool": @@ -6410,10 +6411,7 @@ def get_img_index(zinfo: zipfile.ZipInfo) -> int: task_meta["stop_frame"], spec.frame_step, ) - src_end_frame = ( - src_stop_frame - ((src_stop_frame - src_start_frame) % src_frame_step) + src_frame_step - ) # taken from TestTaskData._compute_annotation_segment_params - + src_end_frame = calc_end_frame(src_start_frame, src_stop_frame, src_frame_step) assert len(frames) == spec.size == task_meta["size"], "Some frames were lost" assert np.all( frames == np.arange(src_start_frame, src_end_frame, src_frame_step) diff --git a/tests/python/rest_api/utils.py b/tests/python/rest_api/utils.py index aa747d169e9d..50e36ab7e0d2 100644 --- a/tests/python/rest_api/utils.py +++ b/tests/python/rest_api/utils.py @@ -601,3 +601,7 @@ def _exclude_cb(obj, path): def parse_frame_step(frame_filter: str) -> int: return int((frame_filter or "step=1").split("=")[1]) + + +def calc_end_frame(start_frame: int, stop_frame: int, frame_step: int) -> int: + return stop_frame - ((stop_frame - start_frame) % frame_step) + frame_step From 4998837afbe69ca47a337eebcf5781be639d7975 Mon Sep 17 00:00:00 2001 From: Oleg Valiulin Date: Thu, 19 Dec 2024 12:05:15 +0000 Subject: [PATCH 10/10] sort imports --- tests/python/rest_api/test_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index 7d0bf874bf7e..21a38179f0f7 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -66,6 +66,7 @@ from .utils import ( DATUMARO_FORMAT_FOR_DIMENSION, CollectionSimpleFilterTestBase, + calc_end_frame, compare_annotations, create_task, export_dataset, @@ -73,7 +74,6 @@ export_task_dataset, parse_frame_step, wait_until_task_is_created, - calc_end_frame, )