vidformer 0.5.3__py3-none-any.whl → 0.6.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,