vidformer 0.6.5__tar.gz → 0.8.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: vidformer
3
- Version: 0.6.5
3
+ Version: 0.8.0
4
4
  Summary: A Python library for creating and viewing videos with vidformer.
5
5
  Author-email: Dominik Winecki <dominikwinecki@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -1,5 +1,5 @@
1
1
  """A Python library for creating and viewing videos with vidformer."""
2
2
 
3
- __version__ = "0.6.5"
3
+ __version__ = "0.8.0"
4
4
 
5
5
  from .vf import *
@@ -45,6 +45,11 @@ LINE_4 = 4
45
45
  LINE_8 = 8
46
46
  LINE_AA = 16
47
47
 
48
+ _inline_mat = vf.Filter("_inline_mat")
49
+ _slice_mat = vf.Filter("_slice_mat")
50
+ _slice_write_mat = vf.Filter("_slice_write_mat")
51
+
52
+
48
53
  _filter_scale = vf.Filter("Scale")
49
54
  _filter_rectangle = vf.Filter("cv2.rectangle")
50
55
  _filter_putText = vf.Filter("cv2.putText")
@@ -96,8 +101,9 @@ class Frame:
96
101
  return
97
102
 
98
103
  self._modified = True
99
- self._f = _filter_scale(self._f, pix_fmt="rgb24")
100
- self._fmt["pix_fmt"] = "rgb24"
104
+ if self._fmt["pix_fmt"] != "rgb24":
105
+ self._f = _filter_scale(self._f, pix_fmt="rgb24")
106
+ self._fmt["pix_fmt"] = "rgb24"
101
107
 
102
108
  def numpy(self):
103
109
  """
@@ -113,8 +119,112 @@ class Frame:
113
119
  assert len(frame_raster_rgb24) == self.shape[0] * self.shape[1] * 3
114
120
  raw_data_array = np.frombuffer(frame_raster_rgb24, dtype=np.uint8)
115
121
  frame = raw_data_array.reshape(self.shape)
122
+ frame = frame[:, :, ::-1] # convert RGB to BGR
116
123
  return frame
117
124
 
125
+ def __getitem__(self, key):
126
+ if not isinstance(key, tuple):
127
+ raise NotImplementedError("Only 2D slicing is supported")
128
+
129
+ if len(key) != 2:
130
+ raise NotImplementedError("Only 2D slicing is supported")
131
+
132
+ if not all(isinstance(x, slice) for x in key):
133
+ raise NotImplementedError("Only 2D slicing is supported")
134
+
135
+ miny = key[0].start if key[0].start is not None else 0
136
+ maxy = key[0].stop if key[0].stop is not None else self.shape[0]
137
+ minx = key[1].start if key[1].start is not None else 0
138
+ maxx = key[1].stop if key[1].stop is not None else self.shape[1]
139
+
140
+ # handle negative indices
141
+ if miny < 0:
142
+ miny = self.shape[0] + miny
143
+ if maxy < 0:
144
+ maxy = self.shape[0] + maxy
145
+ if minx < 0:
146
+ minx = self.shape[1] + minx
147
+ if maxx < 0:
148
+ maxx = self.shape[1] + maxx
149
+
150
+ if (
151
+ maxy <= miny
152
+ or maxx <= minx
153
+ or miny < 0
154
+ or minx < 0
155
+ or maxy > self.shape[0]
156
+ or maxx > self.shape[1]
157
+ ):
158
+ raise NotImplementedError("Invalid slice")
159
+
160
+ f = _slice_mat(self._f, miny, maxy, minx, maxx)
161
+ fmt = self._fmt.copy()
162
+ fmt["width"] = maxx - minx
163
+ fmt["height"] = maxy - miny
164
+ return Frame(f, fmt)
165
+
166
+ def __setitem__(self, key, value):
167
+ value = frameify(value, "value")
168
+
169
+ if not isinstance(key, tuple):
170
+ raise NotImplementedError("Only 2D slicing is supported")
171
+
172
+ if len(key) != 2:
173
+ raise NotImplementedError("Only 2D slicing is supported")
174
+
175
+ if not all(isinstance(x, slice) for x in key):
176
+ raise NotImplementedError("Only 2D slicing is supported")
177
+
178
+ miny = key[0].start if key[0].start is not None else 0
179
+ maxy = key[0].stop if key[0].stop is not None else self.shape[0]
180
+ minx = key[1].start if key[1].start is not None else 0
181
+ maxx = key[1].stop if key[1].stop is not None else self.shape[1]
182
+
183
+ # handle negative indices
184
+ if miny < 0:
185
+ miny = self.shape[0] + miny
186
+ if maxy < 0:
187
+ maxy = self.shape[0] + maxy
188
+ if minx < 0:
189
+ minx = self.shape[1] + minx
190
+ if maxx < 0:
191
+ maxx = self.shape[1] + maxx
192
+
193
+ if (
194
+ maxy <= miny
195
+ or maxx <= minx
196
+ or miny < 0
197
+ or minx < 0
198
+ or maxy > self.shape[0]
199
+ or maxx > self.shape[1]
200
+ ):
201
+ raise NotImplementedError("Invalid slice")
202
+
203
+ if value.shape[0] != maxy - miny or value.shape[1] != maxx - minx:
204
+ raise NotImplementedError("Shape mismatch")
205
+
206
+ self._mut()
207
+ value._mut()
208
+
209
+ self._f = _slice_write_mat(self._f, value._f, miny, maxy, minx, maxx)
210
+
211
+
212
+ def _inline_frame(arr):
213
+ assert arr.dtype == np.uint8
214
+ assert arr.ndim == 3
215
+ assert arr.shape[2] == 3
216
+
217
+ # convert BGR to RGB
218
+ arr = arr[:, :, ::-1]
219
+
220
+ width = arr.shape[1]
221
+ height = arr.shape[0]
222
+ pix_fmt = "rgb24"
223
+
224
+ f = _inline_mat(arr.tobytes(), width=width, height=height, pix_fmt=pix_fmt)
225
+ fmt = {"width": width, "height": height, "pix_fmt": pix_fmt}
226
+ return Frame(f, fmt)
227
+
118
228
 
119
229
  class VideoCapture:
120
230
  def __init__(self, path):
@@ -178,9 +288,9 @@ class VideoWriter:
178
288
  self._pix_fmt = "yuv420p"
179
289
 
180
290
  def write(self, frame):
181
- if not isinstance(frame, Frame):
182
- raise Exception("frame must be a vidformer.cv2.Frame object")
183
- if frame._modified:
291
+ frame = frameify(frame, "frame")
292
+
293
+ if frame._fmt["pix_fmt"] != self._pix_fmt:
184
294
  f_obj = _filter_scale(frame._f, pix_fmt=self._pix_fmt)
185
295
  self._frames.append(f_obj)
186
296
  else:
@@ -210,6 +320,24 @@ class VideoWriter_fourcc:
210
320
  self._args = args
211
321
 
212
322
 
323
+ def frameify(obj, field_name=None):
324
+ """
325
+ Turn an object (e.g., ndarray) into a Frame.
326
+ """
327
+
328
+ if isinstance(obj, Frame):
329
+ return obj
330
+ elif isinstance(obj, np.ndarray):
331
+ return _inline_frame(obj)
332
+ else:
333
+ if field_name is not None:
334
+ raise Exception(
335
+ f"Unsupported type for field {field_name}, expected Frame or np.ndarray"
336
+ )
337
+ else:
338
+ raise Exception("Unsupported type, expected Frame or np.ndarray")
339
+
340
+
213
341
  def imread(path, *args):
214
342
  if len(args) > 0:
215
343
  raise NotImplementedError("imread does not support additional arguments")
@@ -225,8 +353,7 @@ def imwrite(path, img, *args):
225
353
  if len(args) > 0:
226
354
  raise NotImplementedError("imwrite does not support additional arguments")
227
355
 
228
- if not isinstance(img, Frame):
229
- raise Exception("img must be a vidformer.cv2.Frame object")
356
+ img = frameify(img)
230
357
 
231
358
  fmt = img._fmt.copy()
232
359
  width = fmt["width"]
@@ -282,7 +409,7 @@ def rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
282
409
  cv.rectangle( img, pt1, pt2, color[, thickness[, lineType[, shift]]] )
283
410
  """
284
411
 
285
- assert isinstance(img, Frame)
412
+ img = frameify(img)
286
413
  img._mut()
287
414
 
288
415
  assert len(pt1) == 2
@@ -326,7 +453,7 @@ def putText(
326
453
  cv.putText( img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]] )
327
454
  """
328
455
 
329
- assert isinstance(img, Frame)
456
+ img = frameify(img)
330
457
  img._mut()
331
458
 
332
459
  assert isinstance(text, str)
@@ -365,7 +492,7 @@ def arrowedLine(
365
492
  """
366
493
  cv.arrowedLine( img, pt1, pt2, color[, thickness[, line_type[, shift[, tipLength]]]] )
367
494
  """
368
- assert isinstance(img, Frame)
495
+ img = frameify(img)
369
496
  img._mut()
370
497
 
371
498
  assert len(pt1) == 2
@@ -399,7 +526,7 @@ def arrowedLine(
399
526
 
400
527
 
401
528
  def line(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
402
- assert isinstance(img, Frame)
529
+ img = frameify(img)
403
530
  img._mut()
404
531
 
405
532
  assert len(pt1) == 2
@@ -429,7 +556,7 @@ def line(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
429
556
 
430
557
 
431
558
  def circle(img, center, radius, color, thickness=None, lineType=None, shift=None):
432
- assert isinstance(img, Frame)
559
+ img = frameify(img)
433
560
  img._mut()
434
561
 
435
562
  assert len(center) == 2
@@ -480,15 +607,15 @@ def addWeighted(src1, alpha, src2, beta, gamma, dst=None, dtype=-1):
480
607
  """
481
608
  cv.addWeighted( src1, alpha, src2, beta, gamma[, dst[, dtype]] ) -> dst
482
609
  """
483
- assert isinstance(src1, Frame)
484
- assert isinstance(src2, Frame)
610
+ src1 = frameify(src1, "src1")
611
+ src2 = frameify(src2, "src2")
485
612
  src1._mut()
486
613
  src2._mut()
487
614
 
488
615
  if dst is None:
489
616
  dst = Frame(src1._f, src1._fmt.copy())
490
617
  else:
491
- assert isinstance(dst, Frame)
618
+ assert isinstance(dst, Frame), "dst must be a Frame"
492
619
  dst._mut()
493
620
 
494
621
  assert isinstance(alpha, float) or isinstance(alpha, int)
@@ -638,6 +638,11 @@ def _json_arg(arg, skip_data_anot=False):
638
638
  if skip_data_anot:
639
639
  return {"String": arg}
640
640
  return {"Data": {"String": arg}}
641
+ elif type(arg) == bytes:
642
+ arg = list(arg)
643
+ if skip_data_anot:
644
+ return {"Bytes": arg}
645
+ return {"Data": {"Bytes": arg}}
641
646
  elif type(arg) == float:
642
647
  if skip_data_anot:
643
648
  return {"Float": arg}
@@ -837,7 +842,7 @@ class UDF:
837
842
  assert type(obj[type_key]) == bool
838
843
  return obj[type_key]
839
844
  else:
840
- assert False
845
+ assert False, f"Unknown type: {type_key}"
841
846
 
842
847
  def _deser_filter(self, obj):
843
848
  assert type(obj) == dict
@@ -873,7 +878,7 @@ class UDF:
873
878
  assert type(obj[type_key]) == bool
874
879
  return obj[type_key]
875
880
  else:
876
- assert False
881
+ assert False, f"Unknown type: {type_key}"
877
882
 
878
883
  def _host(self, socket_path: str):
879
884
  if os.path.exists(socket_path):
File without changes
File without changes
File without changes