@@ -101,7 +101,12 @@ def extract_frames_as_images(video_document: Document, framenums: List[int], as_
101
101
return frames
102
102
103
103
104
- def get_mid_framenum (mmif : Mmif , time_frame : Annotation ):
104
+ def get_mid_framenum (mmif : Mmif , time_frame : Annotation ) -> int :
105
+ warnings .warn ('This function is deprecated. Use ``get_representative_framenums()`` instead.' , DeprecationWarning , stacklevel = 2 )
106
+ return _get_mid_framenum (mmif , time_frame )
107
+
108
+
109
+ def _get_mid_framenum (mmif : Mmif , time_frame : Annotation ) -> int :
105
110
"""
106
111
Calculates the middle frame number of a time interval annotation.
107
112
@@ -112,7 +117,7 @@ def get_mid_framenum(mmif: Mmif, time_frame: Annotation):
112
117
timeunit = time_frame .get_property ('timeUnit' )
113
118
video_document = mmif [time_frame .get_property ('document' )]
114
119
fps = get_framerate (video_document )
115
- return convert (time_frame .get_property ('start' ) + time_frame .get_property ('end' ), timeunit , 'frame' , fps ) // 2
120
+ return int ( convert (time_frame .get_property ('start' ) + time_frame .get_property ('end' ), timeunit , 'frame' , fps ) // 2 )
116
121
117
122
118
123
def extract_mid_frame (mmif : Mmif , time_frame : Annotation , as_PIL : bool = False ):
@@ -124,44 +129,65 @@ def extract_mid_frame(mmif: Mmif, time_frame: Annotation, as_PIL: bool = False):
124
129
:param as_PIL: return :py:class:`~PIL.Image.Image` instead of :py:class:`~numpy.ndarray`
125
130
:return: frame as a :py:class:`numpy.ndarray` or :py:class:`PIL.Image.Image`
126
131
"""
132
+ warnings .warn ('This function is deprecated. Use ``extract_representative_frames()`` instead.' , DeprecationWarning , stacklevel = 2 )
127
133
vd = mmif [time_frame .get_property ('document' )]
128
134
return extract_frames_as_images (vd , [get_mid_framenum (mmif , time_frame )], as_PIL = as_PIL )[0 ]
129
135
130
136
131
- def get_representative_framenum (mmif : Mmif , time_frame : Annotation ):
137
+ def get_representative_framenums (mmif : Mmif , time_frame : Annotation ) -> List [ int ] :
132
138
"""
133
- Calculates the representative frame number from an annotation.
139
+ Calculates the representative frame numbers from an annotation. To pick the representative frames, it first looks
140
+ up the ``representatives`` property of the ``TimeFrame`` annotation. If it is not found, it will calculate the
141
+ number of the middle frame.
134
142
135
143
:param mmif: :py:class:`~mmif.serialize.mmif.Mmif` instance
136
144
:param time_frame: :py:class:`~mmif.serialize.annotation.Annotation` instance that holds a time interval annotation containing a `representatives` property (``"@type": ".../TimeFrame/..."``)
137
145
:return: representative frame number as an integer
138
146
"""
139
147
if 'representatives' not in time_frame .properties :
140
- raise ValueError ( f'The time frame { time_frame . id } does not have a representative.' )
148
+ return [ _get_mid_framenum ( mmif , time_frame )]
141
149
timeunit = time_frame .get_property ('timeUnit' )
142
150
video_document = mmif [time_frame .get_property ('document' )]
143
151
fps = get_framerate (video_document )
144
152
representatives = time_frame .get_property ('representatives' )
145
- top_representative_id = representatives [0 ]
153
+ ref_frams = []
154
+ for rep in representatives :
155
+ if Mmif .id_delimiter in rep :
156
+ rep_long_id = rep
157
+ else :
158
+ rep_long_id = time_frame ._parent_view_id + time_frame .id_delimiter + rep
159
+ try :
160
+ rep_anno = mmif [rep_long_id ]
161
+ except KeyError :
162
+ raise ValueError (f'Representative timepoint { rep_long_id } not found in any view.' )
163
+ ref_frams .append (int (convert (rep_anno .get_property ('timePoint' ), timeunit , 'frame' , fps )))
164
+ return ref_frams
165
+
166
+
167
+ def get_representative_framenum (mmif : Mmif , time_frame : Annotation ) -> int :
168
+ """
169
+ A thin wrapper around :py:func:`get_representative_framenums` to return a single representative frame number. Always
170
+ return the first frame number found.
171
+ """
146
172
try :
147
- representative_timepoint_anno = mmif [time_frame ._parent_view_id + time_frame .id_delimiter + top_representative_id ]
148
- except KeyError :
149
- raise ValueError (f'Representative timepoint { top_representative_id } not found in any view.' )
150
- return convert (representative_timepoint_anno .get_property ('timePoint' ), timeunit , 'frame' , fps )
173
+ return get_representative_framenums (mmif , time_frame )[0 ]
174
+ except IndexError :
175
+ raise ValueError (f'No representative frame found in the TimeFrame annotation { time_frame .id } .' )
151
176
152
177
153
- def extract_representative_frame (mmif : Mmif , time_frame : Annotation , as_PIL : bool = False ):
178
+ def extract_representative_frame (mmif : Mmif , time_frame : Annotation , as_PIL : bool = False , first_only : bool = True ):
154
179
"""
155
180
Extracts the representative frame of an annotation as a numpy ndarray or PIL Image.
156
181
157
182
:param mmif: :py:class:`~mmif.serialize.mmif.Mmif` instance
158
183
:param time_frame: :py:class:`~mmif.serialize.annotation.Annotation` instance that holds a time interval annotation (``"@type": ".../TimeFrame/..."``)
159
184
:param as_PIL: return :py:class:`~PIL.Image.Image` instead of :py:class:`~numpy.ndarray`
185
+ :param first_only: return the first representative frame only
160
186
:return: frame as a :py:class:`numpy.ndarray` or :py:class:`PIL.Image.Image`
161
187
"""
162
188
video_document = mmif [time_frame .get_property ('document' )]
163
- rep_frame_num = get_representative_framenum (mmif , time_frame )
164
- return extract_frames_as_images (video_document , [ rep_frame_num ] , as_PIL = as_PIL )[0 ]
189
+ rep_frame_num = [ get_representative_framenum ( mmif , time_frame )] if first_only else get_representative_framenums (mmif , time_frame )
190
+ return extract_frames_as_images (video_document , rep_frame_num , as_PIL = as_PIL )[0 ]
165
191
166
192
167
193
def sample_frames (start_frame : int , end_frame : int , sample_rate : float = 1 ) -> List [int ]:
0 commit comments