diff --git a/src/superannotate/lib/core/video_convertor.py b/src/superannotate/lib/core/video_convertor.py index 758478f7e..6bce376e1 100644 --- a/src/superannotate/lib/core/video_convertor.py +++ b/src/superannotate/lib/core/video_convertor.py @@ -74,8 +74,12 @@ def _interpolate( "x": round(data["x"] + steps["x"] * idx, 2), "y": round(data["y"] + steps["y"] * idx, 2), } - elif annotation_type != AnnotationTypes.EVENT: - tmp_data["points"] = data["points"] + elif ( + annotation_type != AnnotationTypes.EVENT + ): # AnnotationTypes.POLYGON, AnnotationTypes.POLYLINE + tmp_data["points"] = [] + for i in range(len(data["points"])): + tmp_data["points"].append(data["points"][i] + idx * steps[i]) annotations[frame_idx] = Annotation( instanceId=instance_id, @@ -157,12 +161,16 @@ def _interpolate_frames( "y": (to_frame["y"] - from_frame["y"]) / frames_diff, } elif annotation_type in (AnnotationTypes.POLYGON, AnnotationTypes.POLYLINE): - steps = [ - (to_point - from_point) / frames_diff - for from_point, to_point in zip( - from_frame["points"], to_frame["points"] - ) - ] + if len(from_frame["points"]) == len(to_frame["points"]): + steps = [ + (to_point - from_point) / frames_diff + for from_point, to_point in zip( + from_frame["points"], to_frame["points"] + ) + ] + else: + steps = [0] * len(from_frame["points"]) + return self._interpolate( class_name=class_name, class_id=class_id, diff --git a/tests/data_set/unit/one_frame_video_annotation.json b/tests/data_set/unit/one_frame_video_annotation.json new file mode 100644 index 000000000..710804031 --- /dev/null +++ b/tests/data_set/unit/one_frame_video_annotation.json @@ -0,0 +1,90 @@ +{ + "metadata": { + "width": 992, + "height": 568, + "duration": 6.5, + "name": null, + "lastAction": { + "timestamp": 1680778235176, + "email": "karen@superannotate.com" + } + }, + "instances": [ + { + "id": "MzE4NzcuODYwMzQ2Nzc5NTg=", + "type": "bbox", + "classId": -1, + "createdBy": { + "email": "karen@superannotate.com", + "role": "Admin" + }, + "createdAt": "2023-04-06T10:49:58.410Z", + "updatedBy": { + "email": "karen@superannotate.com", + "role": "Admin" + }, + "updatedAt": "2023-04-06T10:49:59.799Z", + "locked": false, + "timeline": { + "0": { + "active": true, + "points": { + "x1": 161.52, + "y1": 82, + "x2": 372.8, + "y2": 318.92 + } + }, + "0.100000": { + "active": false, + "points": { + "x1": 161.52, + "y1": 82, + "x2": 372.8, + "y2": 318.92 + } + } + }, + "pointLabels": {} + }, + { + "id": "NDEyMDguNzUwNzYwMDc4MjU2", + "type": "bbox", + "classId": -1, + "createdBy": { + "email": "karen@superannotate.com", + "role": "Admin" + }, + "createdAt": "2023-04-06T10:50:07.741Z", + "updatedBy": { + "email": "karen@superannotate.com", + "role": "Admin" + }, + "updatedAt": "2023-04-06T10:50:09.264Z", + "locked": false, + "timeline": { + "0.900001": { + "active": true, + "points": { + "x1": 186.35, + "y1": 164.85, + "x2": 375.17, + "y2": 366.01 + } + }, + "1.000000": { + "active": false, + "points": { + "x1": 186.35, + "y1": 164.85, + "x2": 375.17, + "y2": 366.01 + } + } + }, + "pointLabels": {} + } + ], + "tags": [], + "name": "ernest mtom 10 fps" +} \ No newline at end of file diff --git a/tests/data_set/unit/video_annotation.json b/tests/data_set/unit/video_annotation.json new file mode 100644 index 000000000..ce16d8ee2 --- /dev/null +++ b/tests/data_set/unit/video_annotation.json @@ -0,0 +1,674 @@ +{ + "metadata": { + "name": "1.mp4", + "duration": 30526667, + "width": 1920, + "height": 1080, + "lastAction": { + "timestamp": 1680762192661, + "email": "arturn@superannotate.com" + }, + "projectId": 460663, + "url": "https://sa-public-files.s3.us-west-2.amazonaws.com/Video+project/video_file_example_1.mp4", + "status": "InProgress", + "error": null, + "annotatorEmail": null, + "qaEmail": null + }, + "instances": [ + { + "meta": { + "type": "polygon", + "classId": 4797926, + "className": "class_obj_1", + "createdBy": { + "email": "arturn@superannotate.com", + "role": "Admin" + }, + "createdAt": "2023-04-05T08:24:12.256Z", + "updatedBy": { + "email": "arturn@superannotate.com", + "role": "Admin" + }, + "updatedAt": "2023-04-05T08:27:34.436Z", + "start": 900001, + "end": 5400001 + }, + "parameters": [ + { + "start": 900001, + "end": 5400001, + "timestamps": [ + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 226.36, + 240.15, + 191.47, + 456.31, + 283.59, + 604.41, + 585.2, + 808.18, + 949.66, + 592.18, + 957.41, + 235.89, + 596.02, + 186.13 + ], + "timestamp": 900001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 226.36, + 240.15, + 191.47, + 456.31, + 240.71, + 648.53, + 585.2, + 808.18, + 949.66, + 592.18, + 957.41, + 235.89, + 596.02, + 186.13 + ], + "timestamp": 2300001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 226.36, + 240.15, + 191.47, + 456.31, + 240.71, + 648.53, + 585.2, + 808.18, + 1342.69, + 779.37, + 957.41, + 235.89, + 596.02, + 186.13 + ], + "timestamp": 4000001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 226.36, + 240.15, + 191.47, + 456.31, + 240.71, + 648.53, + 585.2, + 808.18, + 906.83, + 933.12, + 1342.69, + 779.37, + 957.41, + 235.89, + 596.02, + 186.13 + ], + "timestamp": 4500001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 226.36, + 240.15, + 191.47, + 456.31, + 437.49, + 569.12, + 585.2, + 808.18, + 906.83, + 933.12, + 801.66, + 625.14, + 957.41, + 235.89, + 606.13, + 563.07 + ], + "timestamp": 4900001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 226.36, + 240.15, + 191.47, + 456.31, + 437.49, + 569.12, + 585.2, + 808.18, + 906.83, + 933.12, + 801.66, + 625.14, + 957.41, + 235.89, + 606.13, + 563.07 + ], + "timestamp": 5400001 + } + ] + } + ] + }, + { + "meta": { + "type": "polyline", + "classId": 4797926, + "className": "class_obj_1", + "createdBy": { + "email": "arturn@superannotate.com", + "role": "Admin" + }, + "createdAt": "2023-04-06T06:22:05.555Z", + "updatedBy": { + "email": "arturn@superannotate.com", + "role": "Admin" + }, + "updatedAt": "2023-04-06T06:23:20.303Z", + "start": 6200001, + "end": 11000001 + }, + "parameters": [ + { + "start": 6200001, + "end": 11000001, + "timestamps": [ + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 958.95, + 86.53, + 953.95, + 509.98, + 1085.52, + 221.98 + ], + "timestamp": 6200001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1008.46, + 86.38, + 953.95, + 509.98, + 1085.52, + 221.98 + ], + "timestamp": 6400001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1065.41, + 102.04, + 953.95, + 509.98, + 1085.52, + 221.98 + ], + "timestamp": 6800001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1164.59, + 153.47, + 953.95, + 509.98, + 1085.52, + 221.98 + ], + "timestamp": 7300001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 953.95, + 509.98, + 1110.52, + 232.45 + ], + "timestamp": 7700001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 1197.22, + 435.16, + 953.95, + 509.98, + 1110.52, + 232.45 + ], + "timestamp": 8200001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 1262.25, + 529.69, + 953.95, + 509.98, + 1110.52, + 232.45 + ], + "timestamp": 8800001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 1270.38, + 587.25, + 953.95, + 509.98, + 1110.52, + 232.45 + ], + "timestamp": 9300001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 1270.38, + 587.25, + 995.77, + 913.51, + 953.95, + 509.98, + 1110.52, + 232.45 + ], + "timestamp": 9800001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 1270.38, + 587.25, + 995.77, + 913.51, + 953.95, + 509.98, + 1234.05, + 191.96 + ], + "timestamp": 10300001 + }, + { + "attributes": [ + { + "id": 9251166, + "groupId": 4402994, + "name": "atr_multi1", + "groupName": "group_MULTI" + }, + { + "id": 9251168, + "groupId": 4402994, + "name": "atr_multi3", + "groupName": "group_MULTI" + }, + { + "id": 9251170, + "groupId": 4402995, + "name": "atr_single2", + "groupName": "group_SINGLE" + } + ], + "points": [ + 1237.61, + 194.59, + 1270.38, + 587.25, + 995.77, + 913.51, + 953.95, + 509.98, + 1234.05, + 191.96 + ], + "timestamp": 11000001 + } + ] + } + ] + } + ], + "tags": [] +} \ No newline at end of file diff --git a/tests/unit/test_per_frame_convertor.py b/tests/unit/test_per_frame_convertor.py new file mode 100644 index 000000000..0926bdf86 --- /dev/null +++ b/tests/unit/test_per_frame_convertor.py @@ -0,0 +1,97 @@ +import json +import os.path +from unittest import TestCase + +from src.superannotate.lib.core.video_convertor import VideoFrameGenerator +from tests import DATA_SET_PATH + + +class TestConvertor(TestCase): + ANNOTATION_PATH = os.path.join(DATA_SET_PATH, "unit", "video_annotation.json") + ONE_FRAME_ANNOTATION_PATH = os.path.join( + DATA_SET_PATH, "unit", "one_frame_video_annotation.json" + ) + + def test_polygon_polyline_convertor(self): + payload = json.load(open(self.ANNOTATION_PATH)) + generator = VideoFrameGenerator(payload, fps=10) + data = [i for i in generator] + + from collections import defaultdict + + point_frame_map = defaultdict(list) + frame_point_map = { + i["frame"]: j["points"] for i in data for j in i["annotations"] + } + for frame, points in frame_point_map.items(): + point_frame_map[tuple(points)].append(frame) + + point_frame_count_pairs = [ + ( + ( + 226.36, + 240.15, + 191.47, + 456.31, + 240.71, + 648.53, + 585.2, + 808.18, + 1342.69, + 779.37, + 957.41, + 235.89, + 596.02, + 186.13, + ), + 5, + ), + ( + ( + 226.36, + 240.15, + 191.47, + 456.31, + 437.49, + 569.12, + 585.2, + 808.18, + 906.83, + 933.12, + 801.66, + 625.14, + 957.41, + 235.89, + 606.13, + 563.07, + ), + 6, + ), + ((1237.61, 194.59, 953.95, 509.98, 1110.52, 232.45), 5), + ((1237.61, 194.59, 1270.38, 587.25, 953.95, 509.98, 1110.52, 232.45), 5), + ( + ( + 1237.61, + 194.59, + 1270.38, + 587.25, + 995.77, + 913.51, + 953.95, + 509.98, + 1234.05, + 191.96, + ), + 8, + ), + ] + for points, frame_count in point_frame_count_pairs: + assert len(point_frame_map.pop(points)) == frame_count + assert all([len(frames) == 1 for frames in point_frame_map.values()]) + + # TODO write tests for one frame annotation + # def test_one_frame_annotation(self): + # payload = json.load(open(self.ONE_FRAME_ANNOTATION_PATH)) + # generator = VideoFrameGenerator(payload, fps=10) + # data = [i for i in generator] + # assert data