vidformer 0.11.0__py3-none-any.whl → 0.12.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 +11 -6
- vidformer/cv2/__init__.py +25 -7
- vidformer/supervision/__init__.py +91 -1
- {vidformer-0.11.0.dist-info → vidformer-0.12.0.dist-info}/METADATA +2 -2
- vidformer-0.12.0.dist-info/RECORD +6 -0
- {vidformer-0.11.0.dist-info → vidformer-0.12.0.dist-info}/WHEEL +1 -1
- vidformer-0.11.0.dist-info/RECORD +0 -6
vidformer/__init__.py
CHANGED
@@ -9,7 +9,7 @@ vidformer-py is a Python 🐍 interface for [vidformer](https://github.com/ixlab
|
|
9
9
|
* [🧑💻 Source Code](https://github.com/ixlab/vidformer/tree/main/vidformer-py/)
|
10
10
|
"""
|
11
11
|
|
12
|
-
__version__ = "0.
|
12
|
+
__version__ = "0.12.0"
|
13
13
|
|
14
14
|
|
15
15
|
import base64
|
@@ -105,14 +105,14 @@ def _play(namespace, hls_video_url, hls_js_url, method="html", status_url=None):
|
|
105
105
|
<script src="{hls_js_url}"></script>
|
106
106
|
</head>
|
107
107
|
<body>
|
108
|
-
<div id="container"></div>
|
108
|
+
<div id="container-{namespace}"></div>
|
109
109
|
<script>
|
110
110
|
var statusUrl = '{status_url}';
|
111
111
|
var videoSrc = '{hls_video_url}';
|
112
112
|
var videoNamespace = '{namespace}';
|
113
113
|
|
114
114
|
function showWaiting() {{
|
115
|
-
document.getElementById('container').textContent = 'Waiting...';
|
115
|
+
document.getElementById('container-{namespace}').textContent = 'Waiting...';
|
116
116
|
pollStatus();
|
117
117
|
}}
|
118
118
|
|
@@ -122,7 +122,7 @@ def _play(namespace, hls_video_url, hls_js_url, method="html", status_url=None):
|
|
122
122
|
.then(r => r.json())
|
123
123
|
.then(res => {{
|
124
124
|
if (res.ready) {{
|
125
|
-
document.getElementById('container').textContent = '';
|
125
|
+
document.getElementById('container-{namespace}').textContent = '';
|
126
126
|
attachHls();
|
127
127
|
}} else {{
|
128
128
|
pollStatus();
|
@@ -136,7 +136,7 @@ def _play(namespace, hls_video_url, hls_js_url, method="html", status_url=None):
|
|
136
136
|
}}
|
137
137
|
|
138
138
|
function attachHls() {{
|
139
|
-
var container = document.getElementById('container');
|
139
|
+
var container = document.getElementById('container-{namespace}');
|
140
140
|
container.textContent = '';
|
141
141
|
var video = document.createElement('video');
|
142
142
|
video.id = 'video-' + videoNamespace;
|
@@ -1196,7 +1196,12 @@ class YrdenServer:
|
|
1196
1196
|
|
1197
1197
|
def __del__(self):
|
1198
1198
|
if self._proc is not None:
|
1199
|
-
self._proc.
|
1199
|
+
self._proc.terminate()
|
1200
|
+
try:
|
1201
|
+
self._proc.wait(timeout=1)
|
1202
|
+
except subprocess.TimeoutExpired:
|
1203
|
+
self._proc.kill()
|
1204
|
+
self._proc.wait()
|
1200
1205
|
|
1201
1206
|
|
1202
1207
|
class YrdenSource:
|
vidformer/cv2/__init__.py
CHANGED
@@ -265,8 +265,12 @@ class Frame:
|
|
265
265
|
raise NotImplementedError("Only 1-channel mask frames are supported")
|
266
266
|
|
267
267
|
# Value should be a bgr or bgra color
|
268
|
-
if type(value) is not list
|
269
|
-
|
268
|
+
if (type(value) is not list and type(value) is not tuple) or len(
|
269
|
+
value
|
270
|
+
) not in [3, 4]:
|
271
|
+
raise NotImplementedError(
|
272
|
+
"Value should be a 3 or 4 element list or tuple"
|
273
|
+
)
|
270
274
|
value = [float(x) for x in value]
|
271
275
|
if len(value) == 3:
|
272
276
|
value.append(255.0)
|
@@ -348,7 +352,7 @@ class VideoCapture:
|
|
348
352
|
elif prop == CAP_PROP_FRAME_HEIGHT:
|
349
353
|
return self._source.fmt()["height"]
|
350
354
|
elif prop == CAP_PROP_FRAME_COUNT:
|
351
|
-
return len(self._source
|
355
|
+
return len(self._source)
|
352
356
|
elif prop == CAP_PROP_POS_FRAMES:
|
353
357
|
return self._next_frame_idx
|
354
358
|
|
@@ -374,6 +378,20 @@ class VideoCapture:
|
|
374
378
|
frame = Frame(frame, self._source.fmt())
|
375
379
|
return True, frame
|
376
380
|
|
381
|
+
def __getitem__(self, key):
|
382
|
+
if not isinstance(key, int):
|
383
|
+
raise NotImplementedError("Only integer indexing is supported")
|
384
|
+
if key < 0:
|
385
|
+
key = len(self._source) + key
|
386
|
+
if key < 0 or key >= len(self._source):
|
387
|
+
raise IndexError("Index out of bounds")
|
388
|
+
frame = self._source.iloc[key]
|
389
|
+
frame = Frame(frame, self._source.fmt())
|
390
|
+
return frame
|
391
|
+
|
392
|
+
def __len__(self):
|
393
|
+
return len(self._source)
|
394
|
+
|
377
395
|
def release(self):
|
378
396
|
pass
|
379
397
|
|
@@ -425,7 +443,7 @@ class _IgniVideoWriter:
|
|
425
443
|
|
426
444
|
assert isinstance(size, tuple) or isinstance(size, list)
|
427
445
|
assert len(size) == 2
|
428
|
-
|
446
|
+
width, height = size
|
429
447
|
assert ttl is None or isinstance(ttl, int)
|
430
448
|
self._spec = server.create_spec(
|
431
449
|
width, height, "yuv420p", vod_segment_length, 1 / self._f_time, ttl=ttl
|
@@ -514,8 +532,8 @@ class _YrdenVideoWriter:
|
|
514
532
|
|
515
533
|
def spec(self) -> vf.YrdenSpec:
|
516
534
|
fmt = {
|
517
|
-
"width": self._size[
|
518
|
-
"height": self._size[
|
535
|
+
"width": self._size[0],
|
536
|
+
"height": self._size[1],
|
519
537
|
"pix_fmt": self._pix_fmt,
|
520
538
|
}
|
521
539
|
domain = _fps_to_ts(self._fps, len(self._frames))
|
@@ -658,7 +676,7 @@ def resize(src, dsize):
|
|
658
676
|
|
659
677
|
assert isinstance(dsize, tuple) or isinstance(dsize, list)
|
660
678
|
assert len(dsize) == 2
|
661
|
-
|
679
|
+
width, height = dsize
|
662
680
|
|
663
681
|
f = _filter_scale(src._f, width=width, height=height)
|
664
682
|
fmt = {"width": width, "height": height, "pix_fmt": src._fmt["pix_fmt"]}
|
@@ -14,6 +14,11 @@ from supervision.geometry.core import Position
|
|
14
14
|
|
15
15
|
import vidformer.cv2 as vf_cv2
|
16
16
|
|
17
|
+
try:
|
18
|
+
import cv2 as ocv_cv2
|
19
|
+
except ImportError:
|
20
|
+
ocv_cv2 = None
|
21
|
+
|
17
22
|
CV2_FONT = vf_cv2.FONT_HERSHEY_SIMPLEX
|
18
23
|
|
19
24
|
|
@@ -272,7 +277,6 @@ class DotAnnotator:
|
|
272
277
|
outline_thickness: int = 0,
|
273
278
|
outline_color=Color.BLACK,
|
274
279
|
):
|
275
|
-
|
276
280
|
self.color = color
|
277
281
|
self.radius: int = radius
|
278
282
|
self.position: Position = position
|
@@ -537,3 +541,89 @@ class LabelAnnotator:
|
|
537
541
|
thickness=-1,
|
538
542
|
)
|
539
543
|
return scene
|
544
|
+
|
545
|
+
|
546
|
+
class MaskAnnotator:
|
547
|
+
def __init__(
|
548
|
+
self,
|
549
|
+
color=ColorPalette.DEFAULT,
|
550
|
+
opacity: float = 0.5,
|
551
|
+
color_lookup: ColorLookup = ColorLookup.CLASS,
|
552
|
+
):
|
553
|
+
self.color = color
|
554
|
+
self.opacity = opacity
|
555
|
+
self.color_lookup: ColorLookup = color_lookup
|
556
|
+
|
557
|
+
def annotate(
|
558
|
+
self,
|
559
|
+
scene,
|
560
|
+
detections: Detections,
|
561
|
+
custom_color_lookup=None,
|
562
|
+
):
|
563
|
+
if detections.mask is None:
|
564
|
+
return scene
|
565
|
+
|
566
|
+
colored_mask = scene.copy()
|
567
|
+
|
568
|
+
for detection_idx in np.flip(np.argsort(detections.box_area)):
|
569
|
+
color = resolve_color(
|
570
|
+
color=self.color,
|
571
|
+
detections=detections,
|
572
|
+
detection_idx=detection_idx,
|
573
|
+
color_lookup=(
|
574
|
+
self.color_lookup
|
575
|
+
if custom_color_lookup is None
|
576
|
+
else custom_color_lookup
|
577
|
+
),
|
578
|
+
)
|
579
|
+
mask = detections.mask[detection_idx]
|
580
|
+
colored_mask[mask] = color.as_bgr()
|
581
|
+
|
582
|
+
vf_cv2.addWeighted(
|
583
|
+
colored_mask, self.opacity, scene, 1 - self.opacity, 0, dst=scene
|
584
|
+
)
|
585
|
+
return scene
|
586
|
+
|
587
|
+
|
588
|
+
class MaskStreamWriter:
|
589
|
+
def __init__(self, path: str, shape: tuple):
|
590
|
+
# Shape should be (width, height)
|
591
|
+
assert ocv_cv2 is not None, "OpenCV cv2 is required for ExternDetectionsBuilder"
|
592
|
+
assert type(shape) is tuple, "shape must be a tuple"
|
593
|
+
assert len(shape) == 2, "shape must be a tuple of length 2"
|
594
|
+
self._shape = (shape[1], shape[0])
|
595
|
+
self._writer = ocv_cv2.VideoWriter(
|
596
|
+
path, ocv_cv2.VideoWriter_fourcc(*"FFV1"), 1, shape, isColor=False
|
597
|
+
)
|
598
|
+
assert self._writer.isOpened(), f"Failed to open video writer at {path}"
|
599
|
+
self._i = 0
|
600
|
+
|
601
|
+
def write_detections(self, detections: Detections):
|
602
|
+
if len(detections) == 0:
|
603
|
+
return self._i
|
604
|
+
|
605
|
+
mask = detections.mask
|
606
|
+
assert (
|
607
|
+
mask.shape[1:] == self._shape
|
608
|
+
), f"mask shape ({mask.shape[:1]}) must match the shape of the video ({self._shape})"
|
609
|
+
for i in range(mask.shape[0]):
|
610
|
+
frame_uint8 = detections.mask[i].astype(np.uint8)
|
611
|
+
self._writer.write(frame_uint8)
|
612
|
+
self._i += 1
|
613
|
+
return self._i
|
614
|
+
|
615
|
+
def release(self):
|
616
|
+
self._writer.release()
|
617
|
+
|
618
|
+
|
619
|
+
def populate_mask(
|
620
|
+
detections: Detections, mask_stream: vf_cv2.VideoCapture, frame_idx: int
|
621
|
+
):
|
622
|
+
assert type(detections) is Detections
|
623
|
+
assert detections.mask is None
|
624
|
+
detections.mask = []
|
625
|
+
assert len(detections) + frame_idx <= len(mask_stream)
|
626
|
+
for i in range(len(detections)):
|
627
|
+
mask = mask_stream[frame_idx + i]
|
628
|
+
assert mask.shape[2] == 1, "mask must be a single channel image"
|
629
|
+
detections.mask.append(mask)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: vidformer
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.12.0
|
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
|
@@ -0,0 +1,6 @@
|
|
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,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
vidformer/__init__.py,sha256=lbbyaiV57QsaXmvHfrz_RXLaRnFMfm5ulK2dN701X-E,55465
|
2
|
-
vidformer/cv2/__init__.py,sha256=9J_PV306rHYlf4FgBeQqJnlJJ6d2Mcb9s0TfiH8fASA,29528
|
3
|
-
vidformer/supervision/__init__.py,sha256=KR-keBgDG29TSyIFU4Czgd8Yc5qckJKlSaMcPj_z-Zc,17490
|
4
|
-
vidformer-0.11.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
5
|
-
vidformer-0.11.0.dist-info/METADATA,sha256=K3-g51c1iXRrkmqRwoYLUN8uJThtSCkjMs7kzr2SvNw,1800
|
6
|
-
vidformer-0.11.0.dist-info/RECORD,,
|