Introduction¶
Overview¶
The goal of this format is to facilitate interchange of annotations and notes between different review systems or provide a very simple offline review format for a simple review system.
The ORIAnnotations module provides a set of helper classes to manage media and annotations, primarily using OpenTimelineIO (OTIO) as the interchange format. This allows for easy integration with various production tracking systems without requiring specialized tools.
Requirements¶
The main requirement is on Opentimelineio.
pip install opentimelineio
However, to build the documentation, you also need:
Example Usage¶
1import sys, os
2
3project_root = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
4
5manifest_path = os.environ.get("OTIO_PLUGIN_MANIFEST_PATH", "")
6if manifest_path:
7 manifest_path += os.pathsep
8os.environ["OTIO_PLUGIN_MANIFEST_PATH"] = manifest_path + os.path.join(
9 project_root, "otio_event_plugin", "plugin_manifest.json"
10)
11sys.path.append(os.path.join(project_root, "python"))
12
13
14
15import opentimelineio as otio
16import ORIAnnotations
17import os
18import dataclasses
19
20
21reviewdata = [
22 {'media': "/Users/sam/git/Annotations/testmedia/chimera_cars_srgb-test_mov-prores_ks.mov",
23 'startframe': 0,
24 'framerate': 25,
25 'duration': 200,
26 'reviewframes': [{'note': "Hello",
27 'frame': 1,
28 'image': '/Users/sam/git/Annotations/test_export/chimera_cars_srgb-test_mov-prores_ks.00001.png'
29 },
30 {'note': "Hello",
31 'frame': 15,
32 'image': '/Users/sam/git/Annotations/test_export/chimera_cars_srgb-test_mov-prores_ks.00015.png',
33 "annotation_commands": """
34 {"OTIO_SCHEMA": "PaintStart.1", "brush": "circle","friendly_name": "defaultSequence_p_sourceGroup000000.pen:13:1:sam","rgba": [1,1,1,1],"source_index": 0,"timestamp": "2025-07-06T23:21:48.664116","type": "color","uuid": "6500d2f3-e758-4cd6-9bf2-83c7d712786d","visible": true}
35 {"OTIO_SCHEMA": "PaintPoint.1", "point": [{"OTIO_SCHEMA": "PaintVertex.1", "size": 0.00995635986328125, "x": -0.47882136702537537,"y": 0.046961307525634766},{"OTIO_SCHEMA": "PaintVertex.1","size": 0.00995635986328125,"x": -0.480663001537323,"y": 0.046961307525634766}]}
36"""
37 },
38 ]
39 },
40 {'media': "/Users/sam/git/Annotations/testmedia/chimera_coaster_srgb-test_mov-dnxhd.mov",
41 'startframe': 0,
42 'framerate': 25,
43 'duration': 200,
44 'reviewframes': [{'note': "Hello", 'frame': 1, 'image': '/Users/sam/git/Annotations/test_export/chimera_coaster_srgb-test_mov-dnxhd.00001.png'},
45 {'note': "Hello", 'frame': 25, 'image': '/Users/sam/git/Annotations/test_export/chimera_coaster_srgb-test_mov-dnxhd.00025.png'},
46 ]
47 },
48 {'media': "/Users/sam/git/Annotations/testmedia/chimera_fountains_srgb-test_mov-dnxhd.mov",
49 'startframe': 0,
50 'framerate': 25,
51 'duration': 200,
52 'reviewframes': [{'note': "Hello", 'frame': 28, 'image': '/Users/sam/git/Annotations/test_export/chimera_fountains_srgb-test_mov-dnxhd.00028.png'},
53 ]
54 }
55]
56
57
58medialist = []
59reviewitems = []
60
61for reviewmedia in reviewdata:
62 media = ORIAnnotations.Media(media_path=reviewmedia['media'],
63 name=os.path.basename(reviewmedia['media']),
64 frame_rate=reviewmedia['framerate'],
65 start_frame=reviewmedia['startframe'],
66 duration=reviewmedia['duration']
67 )
68 medialist.append(media)
69 ri = ORIAnnotations.ReviewItem(media=media)
70 reviewitems.append(ri)
71 frames = []
72 for reviewframe in reviewmedia['reviewframes']:
73 frame = ORIAnnotations.ReviewItemFrame(note=reviewframe['note'], annotation_image=reviewframe['image'], frame=reviewframe['frame'], review_item=ri)
74 if 'annotation_commands' in reviewframe:
75 # For testing we assume this is a jsonlines format string, so we are going to read it in and turn it into OTIO schema objects.
76 lines = reviewframe['annotation_commands'].splitlines()
77 events = []
78 for line in lines:
79 if len(line) == 0:
80 continue
81 print("DECODING:", line)
82 event = otio.adapters.read_from_string(line, adapter_name="otio_json")
83 events.append(event)
84 frame.annotation_commands = events
85 frames.append(frame)
86 ri.review_frames = frames
87
88review = ORIAnnotations.Review(title="Review", review_items=reviewitems)
89print("MediaList:", medialist)
90reviewgroup = ORIAnnotations.ReviewGroup(media=medialist, reviews=[review])
91timeline = reviewgroup.export_otio_timeline()
92print("About to export:")
93outputfile = "tests/test_export_unittest.otio"
94otio.adapters.write_to_file(timeline, outputfile)
95print("Exported to:", outputfile)
96
97
98# Now we read the file back in.
99
100newtimeline = otio.adapters.read_from_file(outputfile)
101rg = ORIAnnotations.ReviewGroup()
102rg.read_otio_timeline(newtimeline)
103alt_test_output_file = "tests/test_export_unittest2.otio"
104otio.adapters.write_to_file(newtimeline, alt_test_output_file)
105print("Exported to:", alt_test_output_file)
You can run this from the command line, once the libraries are installed with:
python test/testannotations.py