vidformer 0.5.3__py3-none-any.whl → 0.6.0__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.0"
4
+
5
+ from .vf import *
@@ -0,0 +1 @@
1
+ from .vf_cv2 import *
@@ -0,0 +1,236 @@
1
+ from .. import vf
2
+
3
+ import uuid
4
+ from fractions import Fraction
5
+ from bisect import bisect_right
6
+
7
+ CAP_PROP_POS_MSEC = 0
8
+ CAP_PROP_POS_FRAMES = 1
9
+ CAP_PROP_FRAME_WIDTH = 3
10
+ CAP_PROP_FRAME_HEIGHT = 4
11
+ CAP_PROP_FPS = 5
12
+
13
+ FONT_HERSHEY_SIMPLEX = 0
14
+ FONT_HERSHEY_PLAIN = 1
15
+ FONT_HERSHEY_DUPLEX = 2
16
+ FONT_HERSHEY_COMPLEX = 3
17
+ FONT_HERSHEY_TRIPLEX = 4
18
+ FONT_HERSHEY_COMPLEX_SMALL = 5
19
+ FONT_HERSHEY_SCRIPT_SIMPLEX = 6
20
+ FONT_HERSHEY_SCRIPT_COMPLEX = 7
21
+ FONT_ITALIC = 16
22
+
23
+ FILLED = -1
24
+ LINE_4 = 4
25
+ LINE_8 = 8
26
+ LINE_AA = 16
27
+
28
+ _filter_scale = vf.Filter("Scale")
29
+ _filter_rectangle = vf.Filter("cv2.rectangle")
30
+ _filter_putText = vf.Filter("cv2.putText")
31
+
32
+
33
+ def _ts_to_fps(timestamps):
34
+ return int(1 / (timestamps[1] - timestamps[0])) # TODO: Fix for non-integer fps
35
+
36
+
37
+ def _fps_to_ts(fps, n_frames):
38
+ assert type(fps) == int
39
+ return [Fraction(i, fps) for i in range(n_frames)]
40
+
41
+
42
+ _global_cv2_server = None
43
+
44
+
45
+ def _server():
46
+ global _global_cv2_server
47
+ if _global_cv2_server is None:
48
+ _global_cv2_server = vf.YrdenServer()
49
+ return _global_cv2_server
50
+
51
+
52
+ def set_cv2_server(server):
53
+ """Set the server to use for the cv2 frontend."""
54
+ global _global_cv2_server
55
+ assert isinstance(server, vf.YrdenServer)
56
+ _global_cv2_server = server
57
+
58
+
59
+ class _Frame:
60
+ def __init__(self, f):
61
+ self._f = f
62
+
63
+ # denotes that the frame has not yet been modified
64
+ # when a frame is modified, it is converted to rgb24 first
65
+ self._modified = False
66
+
67
+ def _mut(self):
68
+ self._modified = True
69
+ self._f = _filter_scale(self._f, pix_fmt="rgb24")
70
+
71
+
72
+ class VideoCapture:
73
+ def __init__(self, path):
74
+ self._path = path
75
+ server = _server()
76
+ self._source = vf.Source(server, str(uuid.uuid4()), path, 0)
77
+ self._next_frame_idx = 0
78
+
79
+ def isOpened(self):
80
+ return True
81
+
82
+ def get(self, prop):
83
+ if prop == CAP_PROP_FPS:
84
+ return _ts_to_fps(self._source.ts())
85
+ elif prop == CAP_PROP_FRAME_WIDTH:
86
+ return self._source.fmt()["width"]
87
+ elif prop == CAP_PROP_FRAME_HEIGHT:
88
+ return self._source.fmt()["height"]
89
+
90
+ raise Exception(f"Unknown property {prop}")
91
+
92
+ def set(self, prop, value):
93
+ if prop == CAP_PROP_POS_FRAMES:
94
+ assert value >= 0 and value < len(self._source.ts())
95
+ self._next_frame_idx = value
96
+ elif prop == CAP_PROP_POS_MSEC:
97
+ t = Fraction(value, 1000)
98
+ ts = self._source.ts()
99
+ next_frame_idx = bisect_right(ts, t)
100
+ self._next_frame_idx = next_frame_idx
101
+ else:
102
+ raise Exception(f"Unsupported property {prop}")
103
+
104
+ def read(self):
105
+ if self._next_frame_idx >= len(self._source.ts()):
106
+ return False, None
107
+ frame = self._source.iloc[self._next_frame_idx]
108
+ self._next_frame_idx += 1
109
+ frame = _Frame(frame)
110
+ return True, frame
111
+
112
+ def release(self):
113
+ pass
114
+
115
+
116
+ class VideoWriter:
117
+ def __init__(self, path, fourcc, fps, size):
118
+ assert isinstance(fourcc, VideoWriter_fourcc)
119
+ self._path = path
120
+ self._fourcc = fourcc
121
+ self._fps = fps
122
+ self._size = size
123
+
124
+ self._frames = []
125
+ self._pix_fmt = "yuv420p"
126
+
127
+ def write(self, frame):
128
+ if not isinstance(frame, _Frame):
129
+ raise Exception("frame must be a _Frame object")
130
+ if frame._modified:
131
+ f_obj = _filter_scale(frame._f, pix_fmt=self._pix_fmt)
132
+ self._frames.append(f_obj)
133
+ else:
134
+ self._frames.append(frame._f)
135
+
136
+ def release(self):
137
+ spec = self.vf_spec()
138
+ server = _server()
139
+ spec.save(server, self._path)
140
+
141
+ def vf_spec(self):
142
+ fmt = {
143
+ "width": self._size[0],
144
+ "height": self._size[1],
145
+ "pix_fmt": self._pix_fmt,
146
+ }
147
+ domain = _fps_to_ts(self._fps, len(self._frames))
148
+ spec = vf.Spec(domain, lambda t, i: self._frames[i], fmt)
149
+ return spec
150
+
151
+
152
+ class VideoWriter_fourcc:
153
+ def __init__(self, *args):
154
+ self._args = args
155
+
156
+
157
+ def rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
158
+ """
159
+ cv.rectangle( img, pt1, pt2, color[, thickness[, lineType[, shift]]] )
160
+ """
161
+
162
+ assert isinstance(img, _Frame)
163
+ img._mut()
164
+
165
+ assert len(pt1) == 2
166
+ assert len(pt2) == 2
167
+ assert all(isinstance(x, int) for x in pt1)
168
+ assert all(isinstance(x, int) for x in pt2)
169
+
170
+ assert len(color) == 3 or len(color) == 4
171
+ color = [float(x) for x in color]
172
+ if len(color) == 3:
173
+ color.append(255.0)
174
+
175
+ args = []
176
+ if thickness is not None:
177
+ assert isinstance(thickness, int)
178
+ args.append(thickness)
179
+ if lineType is not None:
180
+ assert isinstance(lineType, int)
181
+ assert thickness is not None
182
+ args.append(lineType)
183
+ if shift is not None:
184
+ assert isinstance(shift, int)
185
+ assert shift is not None
186
+ args.append(shift)
187
+
188
+ img._f = _filter_rectangle(img._f, pt1, pt2, color, *args)
189
+
190
+
191
+ def putText(
192
+ img,
193
+ text,
194
+ org,
195
+ fontFace,
196
+ fontScale,
197
+ color,
198
+ thickness=None,
199
+ lineType=None,
200
+ bottomLeftOrigin=None,
201
+ ):
202
+ """
203
+ cv.putText( img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]] )
204
+ """
205
+
206
+ assert isinstance(img, _Frame)
207
+ img._mut()
208
+
209
+ assert isinstance(text, str)
210
+
211
+ assert len(org) == 2
212
+ assert all(isinstance(x, int) for x in org)
213
+
214
+ assert isinstance(fontFace, int)
215
+ assert isinstance(fontScale, float) or isinstance(fontScale, int)
216
+ fontScale = float(fontScale)
217
+
218
+ assert len(color) == 3 or len(color) == 4
219
+ color = [float(x) for x in color]
220
+ if len(color) == 3:
221
+ color.append(255.0)
222
+
223
+ args = []
224
+ if thickness is not None:
225
+ assert isinstance(thickness, int)
226
+ args.append(thickness)
227
+ if lineType is not None:
228
+ assert isinstance(lineType, int)
229
+ assert thickness is not None
230
+ args.append(lineType)
231
+ if bottomLeftOrigin is not None:
232
+ assert isinstance(bottomLeftOrigin, bool)
233
+ assert lineType is not None
234
+ args.append(bottomLeftOrigin)
235
+
236
+ img._f = _filter_putText(img._f, text, org, fontFace, fontScale, color, *args)
@@ -1,7 +1,3 @@
1
- """A Python library for creating and viewing videos with vidformer."""
2
-
3
- __version__ = "0.5.3"
4
-
5
1
  import subprocess
6
2
  from fractions import Fraction
7
3
  import random
@@ -15,11 +11,14 @@ import uuid
15
11
  import threading
16
12
  import gzip
17
13
  import base64
14
+ import re
18
15
 
19
16
  import requests
20
17
  import msgpack
21
18
  import numpy as np
22
19
 
20
+ from . import __version__
21
+
23
22
  _in_notebook = False
24
23
  try:
25
24
  from IPython import get_ipython
@@ -354,7 +353,7 @@ class Loader:
354
353
  class YrdenServer:
355
354
  """A connection to a Yrden server"""
356
355
 
357
- def __init__(self, domain=None, port=None, bin="vidformer-cli"):
356
+ def __init__(self, domain=None, port=None, bin=None):
358
357
  """Connect to a Yrden server
359
358
 
360
359
  Can either connect to an existing server, if domain and port are provided, or start a new server using the provided binary
@@ -364,7 +363,12 @@ class YrdenServer:
364
363
  self._port = port
365
364
  self._proc = None
366
365
  if self._port is None:
367
- 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
+
368
372
  self._domain = "localhost"
369
373
  self._port = random.randint(49152, 65535)
370
374
  cmd = [bin, "yrden", "--port", str(self._port)]
@@ -507,6 +511,21 @@ class Source:
507
511
  def __init__(
508
512
  self, server: YrdenServer, name: str, path: str, stream: int, service=None
509
513
  ):
514
+ if service is None:
515
+ # check if path is a http URL and, if so, automatically set the service
516
+ # for example, the following code should work with just vf.Source(server, "tos_720p", "https://f.dominik.win/data/dve2/tos_720p.mp4")
517
+ # this creates a storage service with endpoint "https://f.dominik.win/" and path "data/dve2/tos_720p.mp4"
518
+ # don't use the root parameter in this case
519
+
520
+ match = re.match(r"(http|https)://([^/]+)(.*)", path)
521
+ if match is not None:
522
+ endpoint = f"{match.group(1)}://{match.group(2)}"
523
+ path = match.group(3)
524
+ # remove leading slash
525
+ if path.startswith("/"):
526
+ path = path[1:]
527
+ service = StorageService("http", endpoint=endpoint)
528
+
510
529
  self._server = server
511
530
  self._name = name
512
531
  self._path = path
@@ -563,17 +582,31 @@ class StorageService:
563
582
  return f"{self._service}(config={self._config})"
564
583
 
565
584
 
566
- def _json_arg(arg):
585
+ def _json_arg(arg, skip_data_anot=False):
567
586
  if type(arg) == FilterExpr or type(arg) == SourceExpr:
568
587
  return {"Frame": arg._to_json_spec()}
569
588
  elif type(arg) == int:
589
+ if skip_data_anot:
590
+ return {"Int": arg}
570
591
  return {"Data": {"Int": arg}}
571
592
  elif type(arg) == str:
593
+ if skip_data_anot:
594
+ return {"String": arg}
572
595
  return {"Data": {"String": arg}}
596
+ elif type(arg) == float:
597
+ if skip_data_anot:
598
+ return {"Float": arg}
599
+ return {"Data": {"Float": arg}}
573
600
  elif type(arg) == bool:
601
+ if skip_data_anot:
602
+ return {"Bool": arg}
574
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)]}}
575
608
  else:
576
- assert False
609
+ raise Exception(f"Unknown arg type: {type(arg)}")
577
610
 
578
611
 
579
612
  class Filter:
@@ -735,9 +768,9 @@ class UDF:
735
768
  keys = list(obj.keys())
736
769
  assert len(keys) == 1
737
770
  type_key = keys[0]
738
- assert type_key in ["Frame", "String", "Int", "Bool"]
771
+ assert type_key in ["FrameType", "String", "Int", "Bool"]
739
772
 
740
- if type_key == "Frame":
773
+ if type_key == "FrameType":
741
774
  frame = obj[type_key]
742
775
  assert type(frame) == dict
743
776
  assert "width" in frame
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vidformer
3
- Version: 0.5.3
3
+ Version: 0.6.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
@@ -0,0 +1,7 @@
1
+ vidformer/__init__.py,sha256=MgZOCVL7wOxSseM5LArXHtW3FzPKLcmGzUaosUhSn6A,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=C3b8OEGSHh7AFbtddybP_DwR7rvy34lCewt9JWcLTaM,6196
5
+ vidformer-0.6.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
6
+ vidformer-0.6.0.dist-info/METADATA,sha256=hK3hz1DM6NQfkmMz2L5Uc098wgCvPJzaHbpkZy-DDt8,643
7
+ vidformer-0.6.0.dist-info/RECORD,,
@@ -1,4 +0,0 @@
1
- vidformer.py,sha256=nlgIXey_gOWnsQPtJ3cuhq0WqENVyQNJ5tslwFSnPR4,27989
2
- vidformer-0.5.3.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
3
- vidformer-0.5.3.dist-info/METADATA,sha256=tpzhQ8QKqulcUoSBSmb-zGJRye3lCCucqfJzOSWp2FA,643
4
- vidformer-0.5.3.dist-info/RECORD,,