vidformer 0.12.0__py3-none-any.whl → 1.0.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/cv2/__init__.py
CHANGED
@@ -19,10 +19,10 @@ except Exception:
|
|
19
19
|
_opencv2 = None
|
20
20
|
|
21
21
|
import re
|
22
|
-
import uuid
|
23
22
|
import zlib
|
24
23
|
from bisect import bisect_right
|
25
24
|
from fractions import Fraction
|
25
|
+
import os
|
26
26
|
|
27
27
|
import numpy as np
|
28
28
|
|
@@ -80,21 +80,36 @@ _global_cv2_server = None
|
|
80
80
|
def _server():
|
81
81
|
global _global_cv2_server
|
82
82
|
if _global_cv2_server is None:
|
83
|
-
|
83
|
+
if "VF_IGNI_ENDPOINT" in os.environ:
|
84
|
+
server_endpoint = os.environ["VF_IGNI_ENDPOINT"]
|
85
|
+
if "VF_IGNI_API_KEY" not in os.environ:
|
86
|
+
raise Exception("VF_IGNI_API_KEY must be set")
|
87
|
+
api_key = os.environ["VF_IGNI_API_KEY"]
|
88
|
+
_global_cv2_server = vf.Server(server_endpoint, api_key)
|
89
|
+
else:
|
90
|
+
raise Exception(
|
91
|
+
"No server set for the cv2 frontend. Set VF_IGNI_ENDPOINT and VF_IGNI_API_KEY environment variables or use cv2.set_server() before use."
|
92
|
+
)
|
84
93
|
return _global_cv2_server
|
85
94
|
|
86
95
|
|
87
96
|
def set_server(server):
|
88
97
|
"""Set the server to use for the cv2 frontend."""
|
89
98
|
global _global_cv2_server
|
90
|
-
assert isinstance(server, vf.
|
99
|
+
assert isinstance(server, vf.Server)
|
91
100
|
_global_cv2_server = server
|
92
101
|
|
93
102
|
|
103
|
+
def get_server():
|
104
|
+
"""Get the server used by the cv2 frontend."""
|
105
|
+
return _server()
|
106
|
+
|
107
|
+
|
94
108
|
_PIX_FMT_MAP = {
|
95
109
|
"rgb24": "rgb24",
|
96
110
|
"yuv420p": "rgb24",
|
97
111
|
"yuv422p": "rgb24",
|
112
|
+
"yuv422p10le": "rgb24",
|
98
113
|
"yuv444p": "rgb24",
|
99
114
|
"yuvj420p": "rgb24",
|
100
115
|
"yuvj422p": "rgb24",
|
@@ -149,28 +164,16 @@ class Frame:
|
|
149
164
|
|
150
165
|
self._mut()
|
151
166
|
server = _server()
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
frame = raw_data_array.reshape(self.shape)
|
167
|
+
frame = server.frame(
|
168
|
+
self.shape[1], self.shape[0], self._fmt["pix_fmt"], self._f
|
169
|
+
)
|
170
|
+
assert type(frame) is bytes
|
171
|
+
assert len(frame) == self.shape[0] * self.shape[1] * self.shape[2]
|
172
|
+
raw_data_array = np.frombuffer(frame, dtype=np.uint8)
|
173
|
+
frame = raw_data_array.reshape(self.shape)
|
174
|
+
if self.shape[2] == 3:
|
161
175
|
frame = frame[:, :, ::-1] # convert RGB to BGR
|
162
|
-
|
163
|
-
else:
|
164
|
-
frame = server.frame(
|
165
|
-
self.shape[1], self.shape[0], self._fmt["pix_fmt"], self._f
|
166
|
-
)
|
167
|
-
assert type(frame) is bytes
|
168
|
-
assert len(frame) == self.shape[0] * self.shape[1] * self.shape[2]
|
169
|
-
raw_data_array = np.frombuffer(frame, dtype=np.uint8)
|
170
|
-
frame = raw_data_array.reshape(self.shape)
|
171
|
-
if self.shape[2] == 3:
|
172
|
-
frame = frame[:, :, ::-1] # convert RGB to BGR
|
173
|
-
return frame
|
176
|
+
return frame
|
174
177
|
|
175
178
|
def __getitem__(self, key):
|
176
179
|
if not isinstance(key, tuple):
|
@@ -316,27 +319,19 @@ class VideoCapture:
|
|
316
319
|
def __init__(self, path: str):
|
317
320
|
server = _server()
|
318
321
|
if type(path) is str:
|
319
|
-
|
322
|
+
match = re.match(r"(http|https)://([^/]+)(.*)", path)
|
323
|
+
if match is not None:
|
324
|
+
endpoint = f"{match.group(1)}://{match.group(2)}"
|
325
|
+
path = match.group(3)
|
326
|
+
if path.startswith("/"):
|
327
|
+
path = path[1:]
|
320
328
|
self._path = path
|
321
|
-
self._source =
|
329
|
+
self._source = server.source(path, 0, "http", {"endpoint": endpoint})
|
322
330
|
else:
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
path = match.group(3)
|
328
|
-
if path.startswith("/"):
|
329
|
-
path = path[1:]
|
330
|
-
self._path = path
|
331
|
-
self._source = server.source(
|
332
|
-
path, 0, "http", {"endpoint": endpoint}
|
333
|
-
)
|
334
|
-
else:
|
335
|
-
raise Exception(
|
336
|
-
"Using a VideoCapture source by name only works with http(s) URLs. You need to pass an IgniSource instead."
|
337
|
-
)
|
338
|
-
elif isinstance(path, vf.IgniSource):
|
339
|
-
assert isinstance(server, vf.IgniServer)
|
331
|
+
self._path = path
|
332
|
+
self._source = server.source(path, 0, "fs", {"root": "."})
|
333
|
+
elif isinstance(path, vf.Source):
|
334
|
+
assert isinstance(server, vf.Server)
|
340
335
|
self._path = path._name
|
341
336
|
self._source = path
|
342
337
|
self._next_frame_idx = 0
|
@@ -397,26 +392,6 @@ class VideoCapture:
|
|
397
392
|
|
398
393
|
|
399
394
|
class VideoWriter:
|
400
|
-
def __init__(self, *args, **kwargs):
|
401
|
-
server = _server()
|
402
|
-
if isinstance(server, vf.YrdenServer):
|
403
|
-
self._writer = _YrdenVideoWriter(*args, **kwargs)
|
404
|
-
elif isinstance(server, vf.IgniServer):
|
405
|
-
self._writer = _IgniVideoWriter(*args, **kwargs)
|
406
|
-
else:
|
407
|
-
raise Exception("Unsupported server type")
|
408
|
-
|
409
|
-
def write(self, *args, **kwargs):
|
410
|
-
return self._writer.write(*args, **kwargs)
|
411
|
-
|
412
|
-
def release(self, *args, **kwargs):
|
413
|
-
return self._writer.release(*args, **kwargs)
|
414
|
-
|
415
|
-
def spec(self, *args, **kwargs):
|
416
|
-
return self._writer.spec(*args, **kwargs)
|
417
|
-
|
418
|
-
|
419
|
-
class _IgniVideoWriter:
|
420
395
|
def __init__(
|
421
396
|
self,
|
422
397
|
path,
|
@@ -426,14 +401,13 @@ class _IgniVideoWriter:
|
|
426
401
|
batch_size=1024,
|
427
402
|
compression="gzip",
|
428
403
|
ttl=3600,
|
404
|
+
pix_fmt="yuv420p",
|
429
405
|
vod_segment_length=Fraction(2, 1),
|
430
406
|
):
|
431
407
|
server = _server()
|
432
|
-
assert isinstance(server, vf.
|
433
|
-
|
434
|
-
|
435
|
-
"Igni does not support writing to a file. VideoWriter path must be None"
|
436
|
-
)
|
408
|
+
assert isinstance(server, vf.Server)
|
409
|
+
assert path is None or type(path) is str
|
410
|
+
self._path = path
|
437
411
|
if isinstance(fps, int):
|
438
412
|
self._f_time = Fraction(1, fps)
|
439
413
|
elif isinstance(fps, Fraction):
|
@@ -446,7 +420,7 @@ class _IgniVideoWriter:
|
|
446
420
|
width, height = size
|
447
421
|
assert ttl is None or isinstance(ttl, int)
|
448
422
|
self._spec = server.create_spec(
|
449
|
-
width, height,
|
423
|
+
width, height, pix_fmt, vod_segment_length, 1 / self._f_time, ttl=ttl
|
450
424
|
)
|
451
425
|
self._batch_size = batch_size
|
452
426
|
assert compression is None or compression in ["gzip"]
|
@@ -498,47 +472,9 @@ class _IgniVideoWriter:
|
|
498
472
|
|
499
473
|
def release(self):
|
500
474
|
self._flush(True)
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
def __init__(self, path, fourcc, fps, size):
|
505
|
-
assert isinstance(fourcc, VideoWriter_fourcc)
|
506
|
-
if path is not None and not isinstance(path, str):
|
507
|
-
raise Exception("path must be a string or None")
|
508
|
-
self._path = path
|
509
|
-
self._fourcc = fourcc
|
510
|
-
self._fps = fps
|
511
|
-
self._size = size
|
512
|
-
|
513
|
-
self._frames = []
|
514
|
-
self._pix_fmt = "yuv420p"
|
515
|
-
|
516
|
-
def write(self, frame):
|
517
|
-
frame = frameify(frame, "frame")
|
518
|
-
|
519
|
-
if frame._fmt["pix_fmt"] != self._pix_fmt:
|
520
|
-
f_obj = _filter_scale(frame._f, pix_fmt=self._pix_fmt)
|
521
|
-
self._frames.append(f_obj)
|
522
|
-
else:
|
523
|
-
self._frames.append(frame._f)
|
524
|
-
|
525
|
-
def release(self):
|
526
|
-
if self._path is None:
|
527
|
-
return
|
528
|
-
|
529
|
-
spec = self.spec()
|
530
|
-
server = _server()
|
531
|
-
spec.save(server, self._path)
|
532
|
-
|
533
|
-
def spec(self) -> vf.YrdenSpec:
|
534
|
-
fmt = {
|
535
|
-
"width": self._size[0],
|
536
|
-
"height": self._size[1],
|
537
|
-
"pix_fmt": self._pix_fmt,
|
538
|
-
}
|
539
|
-
domain = _fps_to_ts(self._fps, len(self._frames))
|
540
|
-
spec = vf.YrdenSpec(domain, lambda t, i: self._frames[i], fmt)
|
541
|
-
return spec
|
475
|
+
if self._path is not None:
|
476
|
+
server = _server()
|
477
|
+
server.export_spec(self._spec.id(), self._path)
|
542
478
|
|
543
479
|
|
544
480
|
class VideoWriter_fourcc:
|
@@ -570,82 +506,57 @@ def imread(path, *args):
|
|
570
506
|
assert path.lower().endswith((".jpg", ".jpeg", ".png"))
|
571
507
|
server = _server()
|
572
508
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
assert len(cap._source) == 1
|
581
|
-
ret, frame = cap.read()
|
582
|
-
assert ret
|
583
|
-
cap.release()
|
584
|
-
return frame
|
509
|
+
cap = VideoCapture(path)
|
510
|
+
assert cap.isOpened()
|
511
|
+
assert len(cap._source) == 1
|
512
|
+
ret, frame = cap.read()
|
513
|
+
assert ret
|
514
|
+
cap.release()
|
515
|
+
return frame
|
585
516
|
|
586
517
|
|
587
518
|
def imwrite(path, img, *args):
|
588
519
|
if len(args) > 0:
|
589
520
|
raise NotImplementedError("imwrite does not support additional arguments")
|
590
521
|
|
591
|
-
server = _server()
|
592
|
-
if type(server) is vf.IgniServer:
|
593
|
-
raise NotImplementedError(
|
594
|
-
"imwrite is only supported with YrdenServer, not IgniServer"
|
595
|
-
)
|
596
|
-
|
597
522
|
img = frameify(img)
|
598
|
-
|
599
523
|
fmt = img._fmt.copy()
|
600
524
|
width = fmt["width"]
|
601
525
|
height = fmt["height"]
|
602
|
-
f = img._f
|
603
|
-
|
604
|
-
domain = [Fraction(0, 1)]
|
605
526
|
|
606
527
|
if path.lower().endswith(".png"):
|
607
|
-
|
608
|
-
|
609
|
-
domain,
|
610
|
-
lambda t, i: img._f,
|
611
|
-
{"width": width, "height": height, "pix_fmt": "rgb24"},
|
612
|
-
)
|
613
|
-
spec.save(_server(), path, encoder="png")
|
528
|
+
out_pix_fmt = "rgb24"
|
529
|
+
encoder = "png"
|
614
530
|
elif path.lower().endswith((".jpg", ".jpeg")):
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
fmt["pix_fmt"] = "yuv420p"
|
531
|
+
encoder = "mjpeg"
|
532
|
+
if img._fmt["pix_fmt"] not in ["yuvj420p", "yuvj422p", "yuvj444p"]:
|
533
|
+
out_pix_fmt = "yuvj420p"
|
619
534
|
else:
|
620
|
-
|
621
|
-
f = _filter_scale(img._f, pix_fmt="yuvj420p")
|
622
|
-
fmt["pix_fmt"] = "yuvj420p"
|
623
|
-
|
624
|
-
spec = vf.YrdenSpec(domain, lambda t, i: f, fmt)
|
625
|
-
spec.save(server, path, encoder="mjpeg")
|
535
|
+
out_pix_fmt = img._fmt["pix_fmt"]
|
626
536
|
else:
|
627
537
|
raise Exception("Unsupported image format")
|
628
538
|
|
539
|
+
if img._fmt["pix_fmt"] != out_pix_fmt:
|
540
|
+
f = _filter_scale(img._f, pix_fmt=out_pix_fmt)
|
541
|
+
img = Frame(f, {"width": width, "height": height, "pix_fmt": out_pix_fmt})
|
542
|
+
|
543
|
+
writer = VideoWriter(None, None, 1, (width, height), pix_fmt=out_pix_fmt)
|
544
|
+
writer.write(img)
|
545
|
+
writer.release()
|
546
|
+
|
547
|
+
spec = writer.spec()
|
548
|
+
server = _server()
|
549
|
+
server.export_spec(spec.id(), path, encoder=encoder)
|
550
|
+
|
629
551
|
|
630
|
-
def vidplay(video,
|
552
|
+
def vidplay(video, method="html"):
|
631
553
|
"""
|
632
554
|
Play a vidformer video specification.
|
633
|
-
|
634
|
-
Args:
|
635
|
-
video: one of [vidformer.Spec, vidformer.Source, vidformer.cv2.VideoWriter]
|
636
555
|
"""
|
637
|
-
if isinstance(video,
|
638
|
-
return video.play(
|
639
|
-
elif isinstance(video, vf.
|
640
|
-
return video.play(
|
641
|
-
elif isinstance(video, VideoWriter):
|
642
|
-
return vidplay(video._writer, *args, **kwargs)
|
643
|
-
elif isinstance(video, _YrdenVideoWriter):
|
644
|
-
return video.spec().play(_server(), *args, **kwargs)
|
645
|
-
elif isinstance(video, _IgniVideoWriter):
|
646
|
-
return video._spec.play(*args, **kwargs)
|
647
|
-
elif isinstance(video, vf.IgniSpec):
|
648
|
-
return video.play(*args, **kwargs)
|
556
|
+
if isinstance(video, VideoWriter):
|
557
|
+
return video.spec().play(method=method)
|
558
|
+
elif isinstance(video, vf.Spec):
|
559
|
+
return video.play(method=method)
|
649
560
|
else:
|
650
561
|
raise Exception("Unsupported video type to vidplay")
|
651
562
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: vidformer
|
3
|
-
Version: 0.
|
3
|
+
Version: 1.0.1
|
4
4
|
Summary: vidformer-py is a Python 🐍 interface for [vidformer](https://github.com/ixlab/vidformer).
|
5
5
|
Author-email: Dominik Winecki <dominikwinecki@gmail.com>
|
6
6
|
Requires-Python: >=3.8
|
@@ -8,7 +8,6 @@ Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: Operating System :: OS Independent
|
10
10
|
Requires-Dist: requests
|
11
|
-
Requires-Dist: msgpack
|
12
11
|
Requires-Dist: numpy
|
13
12
|
Project-URL: Documentation, https://ixlab.github.io/vidformer/vidformer-py/
|
14
13
|
Project-URL: Homepage, https://ixlab.github.io/vidformer/
|
@@ -0,0 +1,6 @@
|
|
1
|
+
vidformer/__init__.py,sha256=lTnRzfqleuEwb_mG4E0OZ1PgQ-8hUHiASERi_eXPiJc,29972
|
2
|
+
vidformer/cv2/__init__.py,sha256=cgnomW6xV7Ho7cCgbuw5EfS_2JJBmAwIyt-_gmtUTyY,26829
|
3
|
+
vidformer/supervision/__init__.py,sha256=dRHAcHiZN68gUH_2m3o7Ohsv3NBGxF4XGPeI0pn2_K4,20346
|
4
|
+
vidformer-1.0.1.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
|
5
|
+
vidformer-1.0.1.dist-info/METADATA,sha256=bw_C4C9EeVhsHDVywkyTwoTHPCiHJe6w3BJsWgvyAqI,1776
|
6
|
+
vidformer-1.0.1.dist-info/RECORD,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
vidformer/__init__.py,sha256=2_IA8eCF8xIWqgdcpC06CSEsX_b2DPpOww3tuQlY3rg,55692
|
2
|
-
vidformer/cv2/__init__.py,sha256=cp1qJPpxpRGCE3elmoHDxhzafZbopZ9wIkKcZJJI8HM,30105
|
3
|
-
vidformer/supervision/__init__.py,sha256=dRHAcHiZN68gUH_2m3o7Ohsv3NBGxF4XGPeI0pn2_K4,20346
|
4
|
-
vidformer-0.12.0.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
|
5
|
-
vidformer-0.12.0.dist-info/METADATA,sha256=C3OsKiJjYPCgiUblJUan2-aQG5TOprdCn2cduqJBow0,1800
|
6
|
-
vidformer-0.12.0.dist-info/RECORD,,
|
File without changes
|