From 5e18ef51a61cc2073cd1915d63971e0a27a757c4 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 18 Feb 2025 12:40:47 +0000 Subject: [PATCH 1/2] Do not skip duplicate video sub-annotations for keyframes --- darwin/importer/importer.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 9d00b8aa5..70da7642d 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -1677,17 +1677,21 @@ def _handle_video_annotation_subs(annotation: dt.VideoAnnotation): Remove duplicate sub-annotations from the VideoAnnotation.annotation(s) to be imported. """ last_subs = None - for _, _annotation in annotation.frames.items(): + for frame_index, _annotation in annotation.frames.items(): _annotation: dt.Annotation subs = [] for sub in _annotation.subs: - if last_subs is not None and all( - any( - last_sub.annotation_type == sub.annotation_type - and last_sub.data == sub.data - for last_sub in last_subs + if ( + last_subs is not None + and all( + any( + last_sub.annotation_type == sub.annotation_type + and last_sub.data == sub.data + for last_sub in last_subs + ) + for sub in _annotation.subs ) - for sub in _annotation.subs + and frame_index not in annotation.keyframes ): # drop sub-annotation whenever we know it didn't change since last one # which likely wouldn't create on backend side sub-annotation keyframe. From 5c0fc4123166ffca658b3e5df503bc903d134391 Mon Sep 17 00:00:00 2001 From: John Wilkie Date: Tue, 18 Feb 2025 13:25:07 +0000 Subject: [PATCH 2/2] Fix + unit test --- darwin/importer/importer.py | 2 +- tests/darwin/importer/importer_test.py | 49 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 70da7642d..a887fee61 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -1691,7 +1691,7 @@ def _handle_video_annotation_subs(annotation: dt.VideoAnnotation): ) for sub in _annotation.subs ) - and frame_index not in annotation.keyframes + and not annotation.keyframes[frame_index] ): # drop sub-annotation whenever we know it didn't change since last one # which likely wouldn't create on backend side sub-annotation keyframe. diff --git a/tests/darwin/importer/importer_test.py b/tests/darwin/importer/importer_test.py index 2aaf76812..72158063c 100644 --- a/tests/darwin/importer/importer_test.py +++ b/tests/darwin/importer/importer_test.py @@ -725,6 +725,55 @@ def test__get_annotation_data_video_annotation_with_attributes_that_become_empty assert result["frames"][4]["attributes"] == {"attributes": []} +def test__get_annotation_data_video_annotation_does_not_wipe_sub_annotations_when_keyframe_is_true() -> ( + None +): + from darwin.importer.importer import _get_annotation_data + + video_annotation_class = dt.AnnotationClass("video_class", "bounding_box") + video_annotation = dt.VideoAnnotation(video_annotation_class, {}, {}, [], False) + video_annotation.keyframes = {1: True, 2: False, 3: True} + video_annotation.frames = { + 1: dt.Annotation( + annotation_class=video_annotation_class, + data={"x": 1, "y": 2, "w": 3, "h": 4}, + subs=[ + dt.SubAnnotation( + annotation_type="attributes", + data=["attribute_1", "attribute_2"], + ) + ], + slot_names=[], + ), + 2: dt.Annotation( + annotation_class=video_annotation_class, + data={"x": 1, "y": 2, "w": 3, "h": 4}, + subs=[ + dt.SubAnnotation( + annotation_type="attributes", + data=["attribute_1", "attribute_2"], + ) + ], + slot_names=[], + ), + 3: dt.Annotation( + annotation_class=video_annotation_class, + data={"x": 5, "y": 6, "w": 7, "h": 8}, + subs=[ + dt.SubAnnotation( + annotation_type="attributes", + data=["attribute_1", "attribute_2"], + ) + ], + slot_names=[], + ), + } + attributes = {"video_class_id": {"attribute_1": "id_1", "attribute_2": "id_2"}} + result = _get_annotation_data(video_annotation, "video_class_id", attributes) + assert result["frames"][1]["attributes"] == {"attributes": ["id_1", "id_2"]} + assert result["frames"][3]["attributes"] == {"attributes": ["id_1", "id_2"]} + + def __expectation_factory(i: int, slot_names: List[str]) -> dt.Annotation: annotation = dt.Annotation( dt.AnnotationClass(f"class_{i}", f"TEST_TYPE_{i}"), {}, [], []