Skip to content

Commit

Permalink
Regression test for missing frames after exporting a CVAT dataset
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleg Valiulin committed Dec 13, 2024
1 parent c729f18 commit 1862551
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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}",
Expand Down
2 changes: 2 additions & 0 deletions tests/python/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
67 changes: 66 additions & 1 deletion tests/python/rest_api/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 1862551

Please sign in to comment.