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 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.11.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.kill()
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 or len(value) not in [3, 4]:
269
- raise NotImplementedError("Value should be a 3 or 4 element list")
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.ts())
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
- height, width = size
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[1],
518
- "height": self._size[0],
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
- height, width = dsize
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.3
1
+ Metadata-Version: 2.4
2
2
  Name: vidformer
3
- Version: 0.11.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,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.10.1
2
+ Generator: flit 3.11.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -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,,