vidformer 0.5.4__py3-none-any.whl → 0.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vidformer/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """A Python library for creating and viewing videos with vidformer."""
2
+
3
+ __version__ = "0.6.1"
4
+
5
+ from .vf import *
@@ -0,0 +1 @@
1
+ from .vf_cv2 import *
@@ -0,0 +1,428 @@
1
+ from .. import vf
2
+
3
+ try:
4
+ import cv2 as _opencv2
5
+ except:
6
+ _opencv2 = None
7
+
8
+ import uuid
9
+ from fractions import Fraction
10
+ from bisect import bisect_right
11
+
12
+ CAP_PROP_POS_MSEC = 0
13
+ CAP_PROP_POS_FRAMES = 1
14
+ CAP_PROP_FRAME_WIDTH = 3
15
+ CAP_PROP_FRAME_HEIGHT = 4
16
+ CAP_PROP_FPS = 5
17
+
18
+ FONT_HERSHEY_SIMPLEX = 0
19
+ FONT_HERSHEY_PLAIN = 1
20
+ FONT_HERSHEY_DUPLEX = 2
21
+ FONT_HERSHEY_COMPLEX = 3
22
+ FONT_HERSHEY_TRIPLEX = 4
23
+ FONT_HERSHEY_COMPLEX_SMALL = 5
24
+ FONT_HERSHEY_SCRIPT_SIMPLEX = 6
25
+ FONT_HERSHEY_SCRIPT_COMPLEX = 7
26
+ FONT_ITALIC = 16
27
+
28
+ FILLED = -1
29
+ LINE_4 = 4
30
+ LINE_8 = 8
31
+ LINE_AA = 16
32
+
33
+ _filter_scale = vf.Filter("Scale")
34
+ _filter_rectangle = vf.Filter("cv2.rectangle")
35
+ _filter_putText = vf.Filter("cv2.putText")
36
+ _filter_arrowedLine = vf.Filter("cv2.arrowedLine")
37
+ _filter_line = vf.Filter("cv2.line")
38
+ _filter_circle = vf.Filter("cv2.circle")
39
+ _filter_addWeighted = vf.Filter("cv2.addWeighted")
40
+
41
+
42
+ def _ts_to_fps(timestamps):
43
+ return int(1 / (timestamps[1] - timestamps[0])) # TODO: Fix for non-integer fps
44
+
45
+
46
+ def _fps_to_ts(fps, n_frames):
47
+ assert type(fps) == int
48
+ return [Fraction(i, fps) for i in range(n_frames)]
49
+
50
+
51
+ _global_cv2_server = None
52
+
53
+
54
+ def _server():
55
+ global _global_cv2_server
56
+ if _global_cv2_server is None:
57
+ _global_cv2_server = vf.YrdenServer()
58
+ return _global_cv2_server
59
+
60
+
61
+ def set_cv2_server(server):
62
+ """Set the server to use for the cv2 frontend."""
63
+ global _global_cv2_server
64
+ assert isinstance(server, vf.YrdenServer)
65
+ _global_cv2_server = server
66
+
67
+
68
+ class _Frame:
69
+ def __init__(self, f):
70
+ self._f = f
71
+
72
+ # denotes that the frame has not yet been modified
73
+ # when a frame is modified, it is converted to rgb24 first
74
+ self._modified = False
75
+
76
+ def _mut(self):
77
+ self._modified = True
78
+ self._f = _filter_scale(self._f, pix_fmt="rgb24")
79
+
80
+
81
+ class VideoCapture:
82
+ def __init__(self, path):
83
+ self._path = path
84
+ server = _server()
85
+ self._source = vf.Source(server, str(uuid.uuid4()), path, 0)
86
+ self._next_frame_idx = 0
87
+
88
+ def isOpened(self):
89
+ return True
90
+
91
+ def get(self, prop):
92
+ if prop == CAP_PROP_FPS:
93
+ return _ts_to_fps(self._source.ts())
94
+ elif prop == CAP_PROP_FRAME_WIDTH:
95
+ return self._source.fmt()["width"]
96
+ elif prop == CAP_PROP_FRAME_HEIGHT:
97
+ return self._source.fmt()["height"]
98
+
99
+ raise Exception(f"Unknown property {prop}")
100
+
101
+ def set(self, prop, value):
102
+ if prop == CAP_PROP_POS_FRAMES:
103
+ assert value >= 0 and value < len(self._source.ts())
104
+ self._next_frame_idx = value
105
+ elif prop == CAP_PROP_POS_MSEC:
106
+ t = Fraction(value, 1000)
107
+ ts = self._source.ts()
108
+ next_frame_idx = bisect_right(ts, t)
109
+ self._next_frame_idx = next_frame_idx
110
+ else:
111
+ raise Exception(f"Unsupported property {prop}")
112
+
113
+ def read(self):
114
+ if self._next_frame_idx >= len(self._source.ts()):
115
+ return False, None
116
+ frame = self._source.iloc[self._next_frame_idx]
117
+ self._next_frame_idx += 1
118
+ frame = _Frame(frame)
119
+ return True, frame
120
+
121
+ def release(self):
122
+ pass
123
+
124
+
125
+ class VideoWriter:
126
+ def __init__(self, path, fourcc, fps, size):
127
+ assert isinstance(fourcc, VideoWriter_fourcc)
128
+ self._path = path
129
+ self._fourcc = fourcc
130
+ self._fps = fps
131
+ self._size = size
132
+
133
+ self._frames = []
134
+ self._pix_fmt = "yuv420p"
135
+
136
+ def write(self, frame):
137
+ if not isinstance(frame, _Frame):
138
+ raise Exception("frame must be a _Frame object")
139
+ if frame._modified:
140
+ f_obj = _filter_scale(frame._f, pix_fmt=self._pix_fmt)
141
+ self._frames.append(f_obj)
142
+ else:
143
+ self._frames.append(frame._f)
144
+
145
+ def release(self):
146
+ spec = self.vf_spec()
147
+ server = _server()
148
+ spec.save(server, self._path)
149
+
150
+ def vf_spec(self):
151
+ fmt = {
152
+ "width": self._size[0],
153
+ "height": self._size[1],
154
+ "pix_fmt": self._pix_fmt,
155
+ }
156
+ domain = _fps_to_ts(self._fps, len(self._frames))
157
+ spec = vf.Spec(domain, lambda t, i: self._frames[i], fmt)
158
+ return spec
159
+
160
+
161
+ class VideoWriter_fourcc:
162
+ def __init__(self, *args):
163
+ self._args = args
164
+
165
+
166
+ def rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
167
+ """
168
+ cv.rectangle( img, pt1, pt2, color[, thickness[, lineType[, shift]]] )
169
+ """
170
+
171
+ assert isinstance(img, _Frame)
172
+ img._mut()
173
+
174
+ assert len(pt1) == 2
175
+ assert len(pt2) == 2
176
+ assert all(isinstance(x, int) for x in pt1)
177
+ assert all(isinstance(x, int) for x in pt2)
178
+
179
+ assert len(color) == 3 or len(color) == 4
180
+ color = [float(x) for x in color]
181
+ if len(color) == 3:
182
+ color.append(255.0)
183
+
184
+ args = []
185
+ if thickness is not None:
186
+ assert isinstance(thickness, int)
187
+ args.append(thickness)
188
+ if lineType is not None:
189
+ assert isinstance(lineType, int)
190
+ assert thickness is not None
191
+ args.append(lineType)
192
+ if shift is not None:
193
+ assert isinstance(shift, int)
194
+ assert shift is not None
195
+ args.append(shift)
196
+
197
+ img._f = _filter_rectangle(img._f, pt1, pt2, color, *args)
198
+
199
+
200
+ def putText(
201
+ img,
202
+ text,
203
+ org,
204
+ fontFace,
205
+ fontScale,
206
+ color,
207
+ thickness=None,
208
+ lineType=None,
209
+ bottomLeftOrigin=None,
210
+ ):
211
+ """
212
+ cv.putText( img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]] )
213
+ """
214
+
215
+ assert isinstance(img, _Frame)
216
+ img._mut()
217
+
218
+ assert isinstance(text, str)
219
+
220
+ assert len(org) == 2
221
+ assert all(isinstance(x, int) for x in org)
222
+
223
+ assert isinstance(fontFace, int)
224
+ assert isinstance(fontScale, float) or isinstance(fontScale, int)
225
+ fontScale = float(fontScale)
226
+
227
+ assert len(color) == 3 or len(color) == 4
228
+ color = [float(x) for x in color]
229
+ if len(color) == 3:
230
+ color.append(255.0)
231
+
232
+ args = []
233
+ if thickness is not None:
234
+ assert isinstance(thickness, int)
235
+ args.append(thickness)
236
+ if lineType is not None:
237
+ assert isinstance(lineType, int)
238
+ assert thickness is not None
239
+ args.append(lineType)
240
+ if bottomLeftOrigin is not None:
241
+ assert isinstance(bottomLeftOrigin, bool)
242
+ assert lineType is not None
243
+ args.append(bottomLeftOrigin)
244
+
245
+ img._f = _filter_putText(img._f, text, org, fontFace, fontScale, color, *args)
246
+
247
+
248
+ def arrowedLine(
249
+ img, pt1, pt2, color, thickness=None, line_type=None, shift=None, tipLength=None
250
+ ):
251
+ """
252
+ cv.arrowedLine( img, pt1, pt2, color[, thickness[, line_type[, shift[, tipLength]]]] )
253
+ """
254
+ assert isinstance(img, _Frame)
255
+ img._mut()
256
+
257
+ assert len(pt1) == 2
258
+ assert len(pt2) == 2
259
+ assert all(isinstance(x, int) for x in pt1)
260
+ assert all(isinstance(x, int) for x in pt2)
261
+
262
+ assert len(color) == 3 or len(color) == 4
263
+ color = [float(x) for x in color]
264
+ if len(color) == 3:
265
+ color.append(255.0)
266
+
267
+ args = []
268
+ if thickness is not None:
269
+ assert isinstance(thickness, int)
270
+ args.append(thickness)
271
+ if line_type is not None:
272
+ assert isinstance(line_type, int)
273
+ assert thickness is not None
274
+ args.append(line_type)
275
+ if shift is not None:
276
+ assert isinstance(shift, int)
277
+ assert shift is not None
278
+ args.append(shift)
279
+ if tipLength is not None:
280
+ assert isinstance(tipLength, float)
281
+ assert shift is not None
282
+ args.append(tipLength)
283
+
284
+ img._f = _filter_arrowedLine(img._f, pt1, pt2, color, *args)
285
+
286
+
287
+ def line(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
288
+ assert isinstance(img, _Frame)
289
+ img._mut()
290
+
291
+ assert len(pt1) == 2
292
+ assert len(pt2) == 2
293
+ assert all(isinstance(x, int) for x in pt1)
294
+ assert all(isinstance(x, int) for x in pt2)
295
+
296
+ assert len(color) == 3 or len(color) == 4
297
+ color = [float(x) for x in color]
298
+ if len(color) == 3:
299
+ color.append(255.0)
300
+
301
+ args = []
302
+ if thickness is not None:
303
+ assert isinstance(thickness, int)
304
+ args.append(thickness)
305
+ if lineType is not None:
306
+ assert isinstance(lineType, int)
307
+ assert thickness is not None
308
+ args.append(lineType)
309
+ if shift is not None:
310
+ assert isinstance(shift, int)
311
+ assert shift is not None
312
+ args.append(shift)
313
+
314
+ img._f = _filter_line(img._f, pt1, pt2, color, *args)
315
+
316
+
317
+ def circle(img, center, radius, color, thickness=None, lineType=None, shift=None):
318
+ assert isinstance(img, _Frame)
319
+ img._mut()
320
+
321
+ assert len(center) == 2
322
+ assert all(isinstance(x, int) for x in center)
323
+
324
+ assert isinstance(radius, int)
325
+
326
+ assert len(color) == 3 or len(color) == 4
327
+ color = [float(x) for x in color]
328
+ if len(color) == 3:
329
+ color.append(255.0)
330
+
331
+ args = []
332
+ if thickness is not None:
333
+ assert isinstance(thickness, int)
334
+ args.append(thickness)
335
+ if lineType is not None:
336
+ assert isinstance(lineType, int)
337
+ assert thickness is not None
338
+ args.append(lineType)
339
+ if shift is not None:
340
+ assert isinstance(shift, int)
341
+ assert shift is not None
342
+ args.append(shift)
343
+
344
+ img._f = _filter_circle(img._f, center, radius, color, *args)
345
+
346
+
347
+ def getFontScaleFromHeight(*args, **kwargs):
348
+ """
349
+ cv.getFontScaleFromHeight( fontFace, pixelHeight[, thickness] )
350
+ """
351
+ if _opencv2 is None:
352
+ raise NotImplementedError("getFontScaleFromHeight requires the cv2 module")
353
+ return _opencv2.getFontScaleFromHeight(*args, **kwargs)
354
+
355
+
356
+ def getTextSize(*args, **kwargs):
357
+ """
358
+ cv.getTextSize( text, fontFace, fontScale, thickness )
359
+ """
360
+ if _opencv2 is None:
361
+ raise NotImplementedError("getTextSize requires the cv2 module")
362
+ return _opencv2.getTextSize(*args, **kwargs)
363
+
364
+
365
+ def addWeighted(src1, alpha, src2, beta, gamma, dst=None, dtype=-1):
366
+ """
367
+ cv.addWeighted( src1, alpha, src2, beta, gamma[, dst[, dtype]] ) -> dst
368
+ """
369
+ assert isinstance(src1, _Frame)
370
+ assert isinstance(src2, _Frame)
371
+ src1._mut()
372
+ src2._mut()
373
+
374
+ if dst is None:
375
+ dst = _Frame(src1._f)
376
+ else:
377
+ assert isinstance(dst, _Frame)
378
+ dst._mut()
379
+
380
+ assert isinstance(alpha, float) or isinstance(alpha, int)
381
+ assert isinstance(beta, float) or isinstance(beta, int)
382
+ assert isinstance(gamma, float) or isinstance(gamma, int)
383
+ alpha = float(alpha)
384
+ beta = float(beta)
385
+ gamma = float(gamma)
386
+
387
+ if dtype != -1:
388
+ raise Exception("addWeighted does not support the dtype argument")
389
+
390
+ dst._f = _filter_addWeighted(src1._f, alpha, src2._f, beta, gamma)
391
+ return dst
392
+
393
+
394
+ # Stubs for unimplemented functions
395
+
396
+
397
+ def clipLine(*args, **kwargs):
398
+ raise NotImplementedError("clipLine is not yet implemented in the cv2 frontend")
399
+
400
+
401
+ def drawContours(*args, **kwargs):
402
+ raise NotImplementedError("drawContours is not yet implemented in the cv2 frontend")
403
+
404
+
405
+ def drawMarker(*args, **kwargs):
406
+ raise NotImplementedError("drawMarker is not yet implemented in the cv2 frontend")
407
+
408
+
409
+ def ellipse(*args, **kwargs):
410
+ raise NotImplementedError("ellipse is not yet implemented in the cv2 frontend")
411
+
412
+
413
+ def ellipse2Poly(*args, **kwargs):
414
+ raise NotImplementedError("ellipse2Poly is not yet implemented in the cv2 frontend")
415
+
416
+
417
+ def fillConvexPoly(*args, **kwargs):
418
+ raise NotImplementedError(
419
+ "fillConvexPoly is not yet implemented in the cv2 frontend"
420
+ )
421
+
422
+
423
+ def fillPoly(*args, **kwargs):
424
+ raise NotImplementedError("fillPoly is not yet implemented in the cv2 frontend")
425
+
426
+
427
+ def polylines(*args, **kwargs):
428
+ raise NotImplementedError("polylines is not yet implemented in the cv2 frontend")
@@ -1,7 +1,3 @@
1
- """A Python library for creating and viewing videos with vidformer."""
2
-
3
- __version__ = "0.5.4"
4
-
5
1
  import subprocess
6
2
  from fractions import Fraction
7
3
  import random
@@ -21,6 +17,8 @@ import requests
21
17
  import msgpack
22
18
  import numpy as np
23
19
 
20
+ from . import __version__
21
+
24
22
  _in_notebook = False
25
23
  try:
26
24
  from IPython import get_ipython
@@ -355,7 +353,7 @@ class Loader:
355
353
  class YrdenServer:
356
354
  """A connection to a Yrden server"""
357
355
 
358
- def __init__(self, domain=None, port=None, bin="vidformer-cli"):
356
+ def __init__(self, domain=None, port=None, bin=None):
359
357
  """Connect to a Yrden server
360
358
 
361
359
  Can either connect to an existing server, if domain and port are provided, or start a new server using the provided binary
@@ -365,7 +363,12 @@ class YrdenServer:
365
363
  self._port = port
366
364
  self._proc = None
367
365
  if self._port is None:
368
- assert bin is not None
366
+ if bin is None:
367
+ if os.getenv("VIDFORMER_BIN") is not None:
368
+ bin = os.getenv("VIDFORMER_BIN")
369
+ else:
370
+ bin = "vidformer-cli"
371
+
369
372
  self._domain = "localhost"
370
373
  self._port = random.randint(49152, 65535)
371
374
  cmd = [bin, "yrden", "--port", str(self._port)]
@@ -579,17 +582,31 @@ class StorageService:
579
582
  return f"{self._service}(config={self._config})"
580
583
 
581
584
 
582
- def _json_arg(arg):
585
+ def _json_arg(arg, skip_data_anot=False):
583
586
  if type(arg) == FilterExpr or type(arg) == SourceExpr:
584
587
  return {"Frame": arg._to_json_spec()}
585
588
  elif type(arg) == int:
589
+ if skip_data_anot:
590
+ return {"Int": arg}
586
591
  return {"Data": {"Int": arg}}
587
592
  elif type(arg) == str:
593
+ if skip_data_anot:
594
+ return {"String": arg}
588
595
  return {"Data": {"String": arg}}
596
+ elif type(arg) == float:
597
+ if skip_data_anot:
598
+ return {"Float": arg}
599
+ return {"Data": {"Float": arg}}
589
600
  elif type(arg) == bool:
601
+ if skip_data_anot:
602
+ return {"Bool": arg}
590
603
  return {"Data": {"Bool": arg}}
604
+ elif type(arg) == tuple or type(arg) == list:
605
+ if skip_data_anot:
606
+ return {"List": [_json_arg(x, True) for x in list(arg)]}
607
+ return {"Data": {"List": [_json_arg(x, True) for x in list(arg)]}}
591
608
  else:
592
- assert False
609
+ raise Exception(f"Unknown arg type: {type(arg)}")
593
610
 
594
611
 
595
612
  class Filter:
@@ -751,9 +768,9 @@ class UDF:
751
768
  keys = list(obj.keys())
752
769
  assert len(keys) == 1
753
770
  type_key = keys[0]
754
- assert type_key in ["Frame", "String", "Int", "Bool"]
771
+ assert type_key in ["FrameType", "String", "Int", "Bool"]
755
772
 
756
- if type_key == "Frame":
773
+ if type_key == "FrameType":
757
774
  frame = obj[type_key]
758
775
  assert type(frame) == dict
759
776
  assert "width" in frame
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: vidformer
3
+ Version: 0.6.1
4
+ Summary: A Python library for creating and viewing videos with vidformer.
5
+ Author-email: Dominik Winecki <dominikwinecki@gmail.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Dist: requests
11
+ Requires-Dist: msgpack
12
+ Requires-Dist: numpy
13
+ Project-URL: Documentation, https://ixlab.github.io/vidformer/vidformer-py/
14
+ Project-URL: Homepage, https://ixlab.github.io/vidformer/
15
+ Project-URL: Issues, https://github.com/ixlab/vidformer/issues
16
+
17
+ # vidformer-py
18
+
19
+ [![PyPI version](https://img.shields.io/pypi/v/vidformer.svg)](https://pypi.org/project/vidformer/)
20
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/ixlab/vidformer/blob/main/LICENSE)
21
+
22
+
23
+ vidformer-py is our Python 🐍 interface for [vidformer](https://github.com/ixlab/vidformer).
24
+ Our [getting started guide](https://ixlab.github.io/vidformer/getting-started.html) explains how to use it.
25
+
26
+ **Quick links:**
27
+ * [📦 PyPI](https://pypi.org/project/vidformer/)
28
+ * [📘 Documentation](https://ixlab.github.io/vidformer/vidformer-py/)
29
+ * [🧑‍💻 Source Code](https://github.com/ixlab/vidformer/tree/main/vidformer-py/)
30
+
31
+ **Publish:**
32
+ ```bash
33
+ export FLIT_USERNAME='__token__' FLIT_PASSWORD='<token>'
34
+ flit publish
35
+ ```
36
+
@@ -0,0 +1,7 @@
1
+ vidformer/__init__.py,sha256=C-kb3m7hPUh0Sw09vty3JKJHMbJTQcdqSmt8m2V9HIM,113
2
+ vidformer/vf.py,sha256=gexrp0PQ8cbkixCPLY9BCquHeHWfD6iUcA_wbSxGmFQ,29511
3
+ vidformer/cv2/__init__.py,sha256=wOjDsYyUKlP_Hye8-tyz-msu9xwaPMpN2sGMu3Lh3-w,22
4
+ vidformer/cv2/vf_cv2.py,sha256=m05MNPklhaaAfFQfTWetJ_FqWq0u_s77INfABUnHVoA,11664
5
+ vidformer-0.6.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
6
+ vidformer-0.6.1.dist-info/METADATA,sha256=gbvJy_0NGLbnq-EvDsDSoUm7EmHhlfUQL5au6pKCsXk,1383
7
+ vidformer-0.6.1.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: vidformer
3
- Version: 0.5.4
4
- Summary: A Python library for creating and viewing videos with vidformer.
5
- Author-email: Dominik Winecki <dominikwinecki@gmail.com>
6
- Requires-Python: >=3.8
7
- Description-Content-Type: text/markdown
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Operating System :: OS Independent
10
- Requires-Dist: requests
11
- Requires-Dist: msgpack
12
- Requires-Dist: numpy
13
- Project-URL: Homepage, https://ixlab.github.io/vidformer/
14
- Project-URL: Issues, https://ixlab.github.io/vidformer/issues
15
-
16
- # vidformer-py
17
-
18
- ## Publish
19
-
20
- ```bash
21
- export FLIT_USERNAME='__token__' FLIT_PASSWORD='<token>'
22
- flit publish
23
- ```
24
-
@@ -1,4 +0,0 @@
1
- vidformer.py,sha256=11H5DONF82OWWdd4DxE30u9puuT_Y3sy3bFEhvKZs54,28821
2
- vidformer-0.5.4.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
3
- vidformer-0.5.4.dist-info/METADATA,sha256=Sn-s_MobObWQkbOiqDqeKNB69Td5kjM79TdXN-YXgHs,643
4
- vidformer-0.5.4.dist-info/RECORD,,