vidformer 0.10.0__py3-none-any.whl → 0.11.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 +321 -40
- vidformer/cv2/__init__.py +208 -89
- vidformer/supervision/__init__.py +37 -27
- {vidformer-0.10.0.dist-info → vidformer-0.11.0.dist-info}/METADATA +1 -1
- vidformer-0.11.0.dist-info/RECORD +6 -0
- vidformer-0.10.0.dist-info/RECORD +0 -6
- {vidformer-0.10.0.dist-info → vidformer-0.11.0.dist-info}/WHEEL +0 -0
vidformer/__init__.py
CHANGED
@@ -9,27 +9,28 @@ 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.11.0"
|
13
13
|
|
14
14
|
|
15
|
-
import
|
16
|
-
|
17
|
-
import random
|
18
|
-
import time
|
15
|
+
import base64
|
16
|
+
import gzip
|
19
17
|
import json
|
20
|
-
import socket
|
21
|
-
import os
|
22
18
|
import multiprocessing
|
23
|
-
import
|
24
|
-
import
|
25
|
-
import gzip
|
26
|
-
import base64
|
19
|
+
import os
|
20
|
+
import random
|
27
21
|
import re
|
22
|
+
import socket
|
23
|
+
import struct
|
24
|
+
import subprocess
|
25
|
+
import threading
|
26
|
+
import time
|
27
|
+
import uuid
|
28
|
+
from fractions import Fraction
|
28
29
|
from urllib.parse import urlparse
|
29
30
|
|
30
|
-
import requests
|
31
31
|
import msgpack
|
32
32
|
import numpy as np
|
33
|
+
import requests
|
33
34
|
|
34
35
|
_in_notebook = False
|
35
36
|
try:
|
@@ -182,6 +183,214 @@ def _play(namespace, hls_video_url, hls_js_url, method="html", status_url=None):
|
|
182
183
|
raise ValueError("Invalid method")
|
183
184
|
|
184
185
|
|
186
|
+
def _feb_expr_coded_as_scalar(expr) -> bool:
|
187
|
+
if type(expr) is tuple:
|
188
|
+
expr = list(expr)
|
189
|
+
if type(expr) is FilterExpr:
|
190
|
+
return False
|
191
|
+
if type(expr) is list:
|
192
|
+
if len(expr) > 3:
|
193
|
+
return False
|
194
|
+
else:
|
195
|
+
return all([type(x) is int and x >= -(2**15) and x < 2**15 for x in expr])
|
196
|
+
else:
|
197
|
+
assert type(expr) in [int, float, str, bytes, SourceExpr, bool, list]
|
198
|
+
return True
|
199
|
+
|
200
|
+
|
201
|
+
class _FrameExpressionBlock:
|
202
|
+
def __init__(self):
|
203
|
+
self._functions = []
|
204
|
+
self._literals = []
|
205
|
+
self._sources = []
|
206
|
+
self._kwarg_keys = []
|
207
|
+
self._source_fracs = []
|
208
|
+
self._exprs = []
|
209
|
+
self._frame_exprs = []
|
210
|
+
|
211
|
+
def __len__(self):
|
212
|
+
return len(self._frame_exprs)
|
213
|
+
|
214
|
+
def insert_expr(self, expr):
|
215
|
+
if type(expr) is SourceExpr or type(expr) is FilterExpr:
|
216
|
+
return self.insert_frame_expr(expr)
|
217
|
+
else:
|
218
|
+
return self.insert_data_expr(expr)
|
219
|
+
|
220
|
+
def insert_data_expr(self, data):
|
221
|
+
if type(data) is tuple:
|
222
|
+
data = list(data)
|
223
|
+
if type(data) is bool:
|
224
|
+
self._exprs.append(0x01000000_00000000 | int(data))
|
225
|
+
return len(self._exprs) - 1
|
226
|
+
elif type(data) is int:
|
227
|
+
if data >= -(2**31) and data < 2**31:
|
228
|
+
self._exprs.append(data & 0xFFFFFFFF)
|
229
|
+
else:
|
230
|
+
self._literals.append(_json_arg(data, skip_data_anot=True))
|
231
|
+
self._exprs.append(0x40000000_00000000 | len(self._literals) - 1)
|
232
|
+
return len(self._exprs) - 1
|
233
|
+
elif type(data) is float:
|
234
|
+
self._exprs.append(
|
235
|
+
0x02000000_00000000 | int.from_bytes(struct.pack("f", data)[::-1])
|
236
|
+
)
|
237
|
+
elif type(data) is str:
|
238
|
+
self._literals.append(_json_arg(data, skip_data_anot=True))
|
239
|
+
self._exprs.append(0x40000000_00000000 | len(self._literals) - 1)
|
240
|
+
elif type(data) is bytes:
|
241
|
+
self._literals.append(_json_arg(data, skip_data_anot=True))
|
242
|
+
self._exprs.append(0x40000000_00000000 | len(self._literals) - 1)
|
243
|
+
elif type(data) is list:
|
244
|
+
if len(data) == 0:
|
245
|
+
self._exprs.append(0x03000000_00000000)
|
246
|
+
return len(self._exprs) - 1
|
247
|
+
if (
|
248
|
+
len(data) == 1
|
249
|
+
and type(data[0]) is int
|
250
|
+
and data[0] >= -(2**15)
|
251
|
+
and data[0] < 2**15
|
252
|
+
):
|
253
|
+
self._exprs.append(0x04000000_00000000 | (data[0] & 0xFFFF))
|
254
|
+
return len(self._exprs) - 1
|
255
|
+
if (
|
256
|
+
len(data) == 2
|
257
|
+
and type(data[0]) is int
|
258
|
+
and data[0] >= -(2**15)
|
259
|
+
and data[0] < 2**15
|
260
|
+
and type(data[1]) is int
|
261
|
+
and data[1] >= -(2**15)
|
262
|
+
and data[1] < 2**15
|
263
|
+
):
|
264
|
+
self._exprs.append(
|
265
|
+
0x05000000_00000000
|
266
|
+
| ((data[0] & 0xFFFF) << 16)
|
267
|
+
| (data[1] & 0xFFFF)
|
268
|
+
)
|
269
|
+
return len(self._exprs) - 1
|
270
|
+
if (
|
271
|
+
len(data) == 3
|
272
|
+
and type(data[0]) is int
|
273
|
+
and data[0] >= -(2**15)
|
274
|
+
and data[0] < 2**15
|
275
|
+
and type(data[1]) is int
|
276
|
+
and data[1] >= -(2**15)
|
277
|
+
and data[1] < 2**15
|
278
|
+
and type(data[2]) is int
|
279
|
+
and data[2] >= -(2**15)
|
280
|
+
and data[2] < 2**15
|
281
|
+
):
|
282
|
+
self._exprs.append(
|
283
|
+
0x06000000_00000000
|
284
|
+
| ((data[0] & 0xFFFF) << 32)
|
285
|
+
| ((data[1] & 0xFFFF) << 16)
|
286
|
+
| (data[2] & 0xFFFF)
|
287
|
+
)
|
288
|
+
return len(self._exprs) - 1
|
289
|
+
out = len(self._exprs)
|
290
|
+
member_idxs = []
|
291
|
+
for member in data:
|
292
|
+
if _feb_expr_coded_as_scalar(member):
|
293
|
+
member_idxs.append(None)
|
294
|
+
else:
|
295
|
+
member_idxs.append(self.insert_data_expr(member))
|
296
|
+
|
297
|
+
self._exprs.append(0x42000000_00000000 | len(data))
|
298
|
+
|
299
|
+
for i in range(len(data)):
|
300
|
+
if member_idxs[i] is None:
|
301
|
+
self.insert_data_expr(data[i])
|
302
|
+
else:
|
303
|
+
self._exprs.append(0x45000000_00000000 | member_idxs[i])
|
304
|
+
|
305
|
+
return out
|
306
|
+
else:
|
307
|
+
raise Exception("Invalid data type")
|
308
|
+
|
309
|
+
def insert_frame_expr(self, frame):
|
310
|
+
if type(frame) is SourceExpr:
|
311
|
+
source = frame._source._name
|
312
|
+
if source in self._sources:
|
313
|
+
source_idx = self._sources.index(source)
|
314
|
+
else:
|
315
|
+
source_idx = len(self._sources)
|
316
|
+
self._sources.append(source)
|
317
|
+
if frame._is_iloc:
|
318
|
+
self._exprs.append(
|
319
|
+
0x43000000_00000000 | (source_idx << 32) | frame._idx
|
320
|
+
)
|
321
|
+
else:
|
322
|
+
idx = len(self._source_fracs) // 2
|
323
|
+
self._source_fracs.append(frame._idx.numerator)
|
324
|
+
self._source_fracs.append(frame._idx.denominator)
|
325
|
+
self._exprs.append(0x44000000_00000000 | (source_idx << 32) | idx)
|
326
|
+
return len(self._exprs) - 1
|
327
|
+
elif type(frame) is FilterExpr:
|
328
|
+
func = frame._filter._func
|
329
|
+
if func in self._functions:
|
330
|
+
func_idx = self._functions.index(func)
|
331
|
+
else:
|
332
|
+
func_idx = len(self._functions)
|
333
|
+
self._functions.append(func)
|
334
|
+
len_args = len(frame._args)
|
335
|
+
len_kwargs = len(frame._kwargs)
|
336
|
+
|
337
|
+
arg_idxs = []
|
338
|
+
for arg in frame._args:
|
339
|
+
if _feb_expr_coded_as_scalar(arg):
|
340
|
+
arg_idxs.append(None)
|
341
|
+
else:
|
342
|
+
arg_idxs.append(self.insert_expr(arg))
|
343
|
+
kwarg_idxs = {}
|
344
|
+
for k, v in frame._kwargs.items():
|
345
|
+
if _feb_expr_coded_as_scalar(v):
|
346
|
+
kwarg_idxs[k] = None
|
347
|
+
else:
|
348
|
+
kwarg_idxs[k] = self.insert_expr(v)
|
349
|
+
|
350
|
+
out_idx = len(self._exprs)
|
351
|
+
self._exprs.append(
|
352
|
+
0x41000000_00000000 | (len_args << 24) | (len_kwargs << 16) | func_idx
|
353
|
+
)
|
354
|
+
for i in range(len_args):
|
355
|
+
if arg_idxs[i] is None:
|
356
|
+
# It's a scalar
|
357
|
+
self.insert_expr(frame._args[i])
|
358
|
+
else:
|
359
|
+
# It's an expression pointer
|
360
|
+
self._exprs.append(0x45000000_00000000 | arg_idxs[i])
|
361
|
+
for k, v in frame._kwargs.items():
|
362
|
+
if k in self._kwarg_keys:
|
363
|
+
k_idx = self._kwarg_keys.index(k)
|
364
|
+
else:
|
365
|
+
k_idx = len(self._kwarg_keys)
|
366
|
+
self._kwarg_keys.append(k)
|
367
|
+
self._exprs.append(0x46000000_00000000 | k_idx)
|
368
|
+
if kwarg_idxs[k] is None:
|
369
|
+
# It's a scalar
|
370
|
+
self.insert_expr(v)
|
371
|
+
else:
|
372
|
+
# It's an expression pointer
|
373
|
+
self._exprs.append(0x45000000_00000000 | kwarg_idxs[k])
|
374
|
+
return out_idx
|
375
|
+
else:
|
376
|
+
raise Exception("Invalid frame type")
|
377
|
+
|
378
|
+
def insert_frame(self, frame):
|
379
|
+
idx = self.insert_frame_expr(frame)
|
380
|
+
self._frame_exprs.append(idx)
|
381
|
+
|
382
|
+
def as_dict(self):
|
383
|
+
return {
|
384
|
+
"functions": self._functions,
|
385
|
+
"literals": self._literals,
|
386
|
+
"sources": self._sources,
|
387
|
+
"kwarg_keys": self._kwarg_keys,
|
388
|
+
"source_fracs": self._source_fracs,
|
389
|
+
"exprs": self._exprs,
|
390
|
+
"frame_exprs": self._frame_exprs,
|
391
|
+
}
|
392
|
+
|
393
|
+
|
185
394
|
class IgniSource:
|
186
395
|
def __init__(self, id: str, src):
|
187
396
|
self._name = id
|
@@ -245,7 +454,9 @@ class IgniServer:
|
|
245
454
|
self._endpoint = endpoint
|
246
455
|
|
247
456
|
self._api_key = api_key
|
248
|
-
|
457
|
+
self._session = requests.Session()
|
458
|
+
self._session.headers.update({"Authorization": f"Bearer {self._api_key}"})
|
459
|
+
response = self._session.get(
|
249
460
|
f"{self._endpoint}/auth",
|
250
461
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
251
462
|
)
|
@@ -256,7 +467,7 @@ class IgniServer:
|
|
256
467
|
|
257
468
|
def get_source(self, id: str) -> IgniSource:
|
258
469
|
assert type(id) is str
|
259
|
-
response =
|
470
|
+
response = self._session.get(
|
260
471
|
f"{self._endpoint}/source/{id}",
|
261
472
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
262
473
|
)
|
@@ -266,7 +477,7 @@ class IgniServer:
|
|
266
477
|
return IgniSource(response["id"], response)
|
267
478
|
|
268
479
|
def list_sources(self) -> list[str]:
|
269
|
-
response =
|
480
|
+
response = self._session.get(
|
270
481
|
f"{self._endpoint}/source",
|
271
482
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
272
483
|
)
|
@@ -277,7 +488,7 @@ class IgniServer:
|
|
277
488
|
|
278
489
|
def delete_source(self, id: str):
|
279
490
|
assert type(id) is str
|
280
|
-
response =
|
491
|
+
response = self._session.delete(
|
281
492
|
f"{self._endpoint}/source/{id}",
|
282
493
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
283
494
|
)
|
@@ -302,7 +513,7 @@ class IgniServer:
|
|
302
513
|
"storage_service": storage_service,
|
303
514
|
"storage_config": storage_config,
|
304
515
|
}
|
305
|
-
response =
|
516
|
+
response = self._session.post(
|
306
517
|
f"{self._endpoint}/source/search",
|
307
518
|
json=req,
|
308
519
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
@@ -328,7 +539,7 @@ class IgniServer:
|
|
328
539
|
"storage_service": storage_service,
|
329
540
|
"storage_config": storage_config,
|
330
541
|
}
|
331
|
-
response =
|
542
|
+
response = self._session.post(
|
332
543
|
f"{self._endpoint}/source",
|
333
544
|
json=req,
|
334
545
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
@@ -354,7 +565,7 @@ class IgniServer:
|
|
354
565
|
|
355
566
|
def get_spec(self, id: str) -> IgniSpec:
|
356
567
|
assert type(id) is str
|
357
|
-
response =
|
568
|
+
response = self._session.get(
|
358
569
|
f"{self._endpoint}/spec/{id}",
|
359
570
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
360
571
|
)
|
@@ -364,7 +575,7 @@ class IgniServer:
|
|
364
575
|
return IgniSpec(response["id"], response)
|
365
576
|
|
366
577
|
def list_specs(self) -> list[str]:
|
367
|
-
response =
|
578
|
+
response = self._session.get(
|
368
579
|
f"{self._endpoint}/spec",
|
369
580
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
370
581
|
)
|
@@ -382,6 +593,7 @@ class IgniServer:
|
|
382
593
|
frame_rate,
|
383
594
|
ready_hook=None,
|
384
595
|
steer_hook=None,
|
596
|
+
ttl=None,
|
385
597
|
) -> IgniSpec:
|
386
598
|
assert type(width) is int
|
387
599
|
assert type(height) is int
|
@@ -390,6 +602,7 @@ class IgniServer:
|
|
390
602
|
assert type(frame_rate) is Fraction
|
391
603
|
assert type(ready_hook) is str or ready_hook is None
|
392
604
|
assert type(steer_hook) is str or steer_hook is None
|
605
|
+
assert ttl is None or type(ttl) is int
|
393
606
|
|
394
607
|
req = {
|
395
608
|
"width": width,
|
@@ -402,8 +615,9 @@ class IgniServer:
|
|
402
615
|
"frame_rate": [frame_rate.numerator, frame_rate.denominator],
|
403
616
|
"ready_hook": ready_hook,
|
404
617
|
"steer_hook": steer_hook,
|
618
|
+
"ttl": ttl,
|
405
619
|
}
|
406
|
-
response =
|
620
|
+
response = self._session.post(
|
407
621
|
f"{self._endpoint}/spec",
|
408
622
|
json=req,
|
409
623
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
@@ -416,7 +630,7 @@ class IgniServer:
|
|
416
630
|
|
417
631
|
def delete_spec(self, id: str):
|
418
632
|
assert type(id) is str
|
419
|
-
response =
|
633
|
+
response = self._session.delete(
|
420
634
|
f"{self._endpoint}/spec/{id}",
|
421
635
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
422
636
|
)
|
@@ -453,7 +667,7 @@ class IgniServer:
|
|
453
667
|
"frames": req_frames,
|
454
668
|
"terminal": terminal,
|
455
669
|
}
|
456
|
-
response =
|
670
|
+
response = self._session.post(
|
457
671
|
f"{self._endpoint}/spec/{spec_id}/part",
|
458
672
|
json=req,
|
459
673
|
headers={"Authorization": f"Bearer {self._api_key}"},
|
@@ -463,6 +677,84 @@ class IgniServer:
|
|
463
677
|
response = response.json()
|
464
678
|
assert response["status"] == "ok"
|
465
679
|
|
680
|
+
def push_spec_part_block(
|
681
|
+
self, spec_id: str, pos, blocks, terminal, compression="gzip"
|
682
|
+
):
|
683
|
+
if type(spec_id) is IgniSpec:
|
684
|
+
spec_id = spec_id._id
|
685
|
+
assert type(spec_id) is str
|
686
|
+
assert type(pos) is int
|
687
|
+
assert type(blocks) is list
|
688
|
+
assert type(terminal) is bool
|
689
|
+
assert compression is None or compression == "gzip"
|
690
|
+
|
691
|
+
req_blocks = []
|
692
|
+
for block in blocks:
|
693
|
+
assert type(block) is _FrameExpressionBlock
|
694
|
+
block_body = block.as_dict()
|
695
|
+
block_frames = len(block_body["frame_exprs"])
|
696
|
+
block_body = json.dumps(block_body).encode("utf-8")
|
697
|
+
if compression == "gzip":
|
698
|
+
block_body = gzip.compress(block_body, 1)
|
699
|
+
block_body = base64.b64encode(block_body).decode("utf-8")
|
700
|
+
req_blocks.append(
|
701
|
+
{
|
702
|
+
"frames": block_frames,
|
703
|
+
"compression": compression,
|
704
|
+
"body": block_body,
|
705
|
+
}
|
706
|
+
)
|
707
|
+
|
708
|
+
req = {
|
709
|
+
"pos": pos,
|
710
|
+
"terminal": terminal,
|
711
|
+
"blocks": req_blocks,
|
712
|
+
}
|
713
|
+
response = self._session.post(
|
714
|
+
f"{self._endpoint}/spec/{spec_id}/part_block",
|
715
|
+
json=req,
|
716
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
717
|
+
)
|
718
|
+
if not response.ok:
|
719
|
+
raise Exception(response.text)
|
720
|
+
response = response.json()
|
721
|
+
assert response["status"] == "ok"
|
722
|
+
|
723
|
+
def frame(self, width, height, pix_fmt, frame_expr, compression="gzip"):
|
724
|
+
assert type(frame_expr) is FilterExpr or type(frame_expr) is SourceExpr
|
725
|
+
assert compression is None or compression in ["gzip"]
|
726
|
+
feb = _FrameExpressionBlock()
|
727
|
+
feb.insert_frame(frame_expr)
|
728
|
+
feb_body = feb.as_dict()
|
729
|
+
|
730
|
+
feb_body = json.dumps(feb_body).encode("utf-8")
|
731
|
+
if compression == "gzip":
|
732
|
+
feb_body = gzip.compress(feb_body, 1)
|
733
|
+
feb_body = base64.b64encode(feb_body).decode("utf-8")
|
734
|
+
req = {
|
735
|
+
"width": width,
|
736
|
+
"height": height,
|
737
|
+
"pix_fmt": pix_fmt,
|
738
|
+
"compression": compression,
|
739
|
+
"block": {
|
740
|
+
"frames": 1,
|
741
|
+
"compression": compression,
|
742
|
+
"body": feb_body,
|
743
|
+
},
|
744
|
+
}
|
745
|
+
response = self._session.post(
|
746
|
+
f"{self._endpoint}/frame",
|
747
|
+
json=req,
|
748
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
749
|
+
)
|
750
|
+
if not response.ok:
|
751
|
+
raise Exception(response.text)
|
752
|
+
response_body = response.content
|
753
|
+
assert type(response_body) is bytes
|
754
|
+
if compression == "gzip":
|
755
|
+
response_body = gzip.decompress(response_body)
|
756
|
+
return response_body
|
757
|
+
|
466
758
|
|
467
759
|
class YrdenSpec:
|
468
760
|
"""
|
@@ -543,12 +835,11 @@ class YrdenSpec:
|
|
543
835
|
}
|
544
836
|
for k, v in filters.items()
|
545
837
|
}
|
546
|
-
arrays = []
|
547
838
|
|
548
839
|
if verbose:
|
549
840
|
print(f"Sending to server. Spec is {len(spec_obj_json_gzip_b64)} bytes")
|
550
841
|
|
551
|
-
resp = server._new(spec_obj_json_gzip_b64, sources, filters,
|
842
|
+
resp = server._new(spec_obj_json_gzip_b64, sources, filters, self._fmt)
|
552
843
|
hls_video_url = resp["stream_url"]
|
553
844
|
hls_player_url = resp["player_url"]
|
554
845
|
namespace = resp["namespace"]
|
@@ -616,9 +907,7 @@ class YrdenSpec:
|
|
616
907
|
}
|
617
908
|
for k, v in filters.items()
|
618
909
|
}
|
619
|
-
|
620
|
-
|
621
|
-
resp = server._new(spec_obj_json_gzip_b64, sources, filters, arrays, self._fmt)
|
910
|
+
resp = server._new(spec_obj_json_gzip_b64, sources, filters, self._fmt)
|
622
911
|
namespace = resp["namespace"]
|
623
912
|
return YrdenLoader(server, namespace, self._domain)
|
624
913
|
|
@@ -652,14 +941,12 @@ class YrdenSpec:
|
|
652
941
|
}
|
653
942
|
for k, v in filters.items()
|
654
943
|
}
|
655
|
-
arrays = []
|
656
944
|
|
657
945
|
resp = server._export(
|
658
946
|
pth,
|
659
947
|
spec_obj_json_gzip_b64,
|
660
948
|
sources,
|
661
949
|
filters,
|
662
|
-
arrays,
|
663
950
|
self._fmt,
|
664
951
|
encoder,
|
665
952
|
encoder_opts,
|
@@ -692,12 +979,11 @@ class YrdenSpec:
|
|
692
979
|
}
|
693
980
|
for k, v in filters.items()
|
694
981
|
}
|
695
|
-
arrays = []
|
696
982
|
end_t = time.time()
|
697
983
|
out["vrod_create_spec"] = end_t - start_t
|
698
984
|
|
699
985
|
start = time.time()
|
700
|
-
resp = server._new(pth, sources, filters,
|
986
|
+
resp = server._new(pth, sources, filters, self._fmt)
|
701
987
|
end = time.time()
|
702
988
|
out["vrod_register"] = end - start
|
703
989
|
|
@@ -735,12 +1021,11 @@ class YrdenSpec:
|
|
735
1021
|
}
|
736
1022
|
for k, v in filters.items()
|
737
1023
|
}
|
738
|
-
arrays = []
|
739
1024
|
end_t = time.time()
|
740
1025
|
out["dve2_create_spec"] = end_t - start_t
|
741
1026
|
|
742
1027
|
start = time.time()
|
743
|
-
resp = server._export(pth, sources, filters,
|
1028
|
+
resp = server._export(pth, sources, filters, self._fmt, None, None)
|
744
1029
|
resp.raise_for_status()
|
745
1030
|
end = time.time()
|
746
1031
|
out["dve2_exec"] = end - start
|
@@ -861,12 +1146,11 @@ class YrdenServer:
|
|
861
1146
|
resp["ts"] = [Fraction(x[0], x[1]) for x in resp["ts"]]
|
862
1147
|
return resp
|
863
1148
|
|
864
|
-
def _new(self, spec, sources, filters,
|
1149
|
+
def _new(self, spec, sources, filters, fmt):
|
865
1150
|
req = {
|
866
1151
|
"spec": spec,
|
867
1152
|
"sources": sources,
|
868
1153
|
"filters": filters,
|
869
|
-
"arrays": arrays,
|
870
1154
|
"width": fmt["width"],
|
871
1155
|
"height": fmt["height"],
|
872
1156
|
"pix_fmt": fmt["pix_fmt"],
|
@@ -878,14 +1162,11 @@ class YrdenServer:
|
|
878
1162
|
|
879
1163
|
return r.json()
|
880
1164
|
|
881
|
-
def _export(
|
882
|
-
self, pth, spec, sources, filters, arrays, fmt, encoder, encoder_opts, format
|
883
|
-
):
|
1165
|
+
def _export(self, pth, spec, sources, filters, fmt, encoder, encoder_opts, format):
|
884
1166
|
req = {
|
885
1167
|
"spec": spec,
|
886
1168
|
"sources": sources,
|
887
1169
|
"filters": filters,
|
888
|
-
"arrays": arrays,
|
889
1170
|
"width": fmt["width"],
|
890
1171
|
"height": fmt["height"],
|
891
1172
|
"pix_fmt": fmt["pix_fmt"],
|
vidformer/cv2/__init__.py
CHANGED
@@ -18,13 +18,13 @@ try:
|
|
18
18
|
except Exception:
|
19
19
|
_opencv2 = None
|
20
20
|
|
21
|
-
import
|
22
|
-
|
21
|
+
import re
|
23
22
|
import uuid
|
24
|
-
from fractions import Fraction
|
25
|
-
from bisect import bisect_right
|
26
23
|
import zlib
|
27
|
-
import
|
24
|
+
from bisect import bisect_right
|
25
|
+
from fractions import Fraction
|
26
|
+
|
27
|
+
import numpy as np
|
28
28
|
|
29
29
|
CAP_PROP_POS_MSEC = 0
|
30
30
|
CAP_PROP_POS_FRAMES = 1
|
@@ -51,6 +51,7 @@ LINE_AA = 16
|
|
51
51
|
_inline_mat = vf.Filter("_inline_mat")
|
52
52
|
_slice_mat = vf.Filter("_slice_mat")
|
53
53
|
_slice_write_mat = vf.Filter("_slice_write_mat")
|
54
|
+
_black = vf.Filter("_black")
|
54
55
|
|
55
56
|
|
56
57
|
_filter_scale = vf.Filter("Scale")
|
@@ -61,6 +62,7 @@ _filter_line = vf.Filter("cv2.line")
|
|
61
62
|
_filter_circle = vf.Filter("cv2.circle")
|
62
63
|
_filter_addWeighted = vf.Filter("cv2.addWeighted")
|
63
64
|
_filter_ellipse = vf.Filter("cv2.ellipse")
|
65
|
+
_set_to = vf.Filter("cv2.setTo")
|
64
66
|
|
65
67
|
|
66
68
|
def _ts_to_fps(timestamps):
|
@@ -89,11 +91,30 @@ def set_server(server):
|
|
89
91
|
_global_cv2_server = server
|
90
92
|
|
91
93
|
|
94
|
+
_PIX_FMT_MAP = {
|
95
|
+
"rgb24": "rgb24",
|
96
|
+
"yuv420p": "rgb24",
|
97
|
+
"yuv422p": "rgb24",
|
98
|
+
"yuv444p": "rgb24",
|
99
|
+
"yuvj420p": "rgb24",
|
100
|
+
"yuvj422p": "rgb24",
|
101
|
+
"yuvj444p": "rgb24",
|
102
|
+
"gray": "gray",
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
def _top_level_pix_fmt(pix_fmt):
|
107
|
+
if pix_fmt in _PIX_FMT_MAP:
|
108
|
+
return _PIX_FMT_MAP[pix_fmt]
|
109
|
+
raise Exception(f"Unsupported pix_fmt {pix_fmt}")
|
110
|
+
|
111
|
+
|
92
112
|
class Frame:
|
93
113
|
def __init__(self, f, fmt):
|
94
114
|
self._f = f
|
95
115
|
self._fmt = fmt
|
96
|
-
|
116
|
+
channels = 3 if _top_level_pix_fmt(fmt["pix_fmt"]) == "rgb24" else 1
|
117
|
+
self.shape = (fmt["height"], fmt["width"], channels)
|
97
118
|
|
98
119
|
# denotes that the frame has not yet been modified
|
99
120
|
# when a frame is modified, it is converted to rgb24 first
|
@@ -101,13 +122,22 @@ class Frame:
|
|
101
122
|
|
102
123
|
def _mut(self):
|
103
124
|
if self._modified:
|
104
|
-
assert self._fmt["pix_fmt"]
|
125
|
+
assert self._fmt["pix_fmt"] in ["rgb24", "gray"]
|
105
126
|
return
|
106
127
|
|
107
128
|
self._modified = True
|
108
|
-
if
|
129
|
+
if (
|
130
|
+
self._fmt["pix_fmt"] != "rgb24"
|
131
|
+
and _top_level_pix_fmt(self._fmt["pix_fmt"]) == "rgb24"
|
132
|
+
):
|
109
133
|
self._f = _filter_scale(self._f, pix_fmt="rgb24")
|
110
134
|
self._fmt["pix_fmt"] = "rgb24"
|
135
|
+
elif (
|
136
|
+
self._fmt["pix_fmt"] != "gray"
|
137
|
+
and _top_level_pix_fmt(self._fmt["pix_fmt"]) == "gray"
|
138
|
+
):
|
139
|
+
self._f = _filter_scale(self._f, pix_fmt="gray")
|
140
|
+
self._fmt["pix_fmt"] = "gray"
|
111
141
|
|
112
142
|
def copy(self):
|
113
143
|
return Frame(self._f, self._fmt.copy())
|
@@ -118,16 +148,29 @@ class Frame:
|
|
118
148
|
"""
|
119
149
|
|
120
150
|
self._mut()
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
151
|
+
server = _server()
|
152
|
+
if type(server) is vf.YrdenServer:
|
153
|
+
spec = vf.YrdenSpec([Fraction(0, 1)], lambda t, i: self._f, self._fmt)
|
154
|
+
loader = spec.load(_server())
|
155
|
+
|
156
|
+
frame_raster_rgb24 = loader[0]
|
157
|
+
assert type(frame_raster_rgb24) is bytes
|
158
|
+
assert len(frame_raster_rgb24) == self.shape[0] * self.shape[1] * 3
|
159
|
+
raw_data_array = np.frombuffer(frame_raster_rgb24, dtype=np.uint8)
|
160
|
+
frame = raw_data_array.reshape(self.shape)
|
161
|
+
frame = frame[:, :, ::-1] # convert RGB to BGR
|
162
|
+
return frame
|
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
|
131
174
|
|
132
175
|
def __getitem__(self, key):
|
133
176
|
if not isinstance(key, tuple):
|
@@ -171,49 +214,71 @@ class Frame:
|
|
171
214
|
return Frame(f, fmt)
|
172
215
|
|
173
216
|
def __setitem__(self, key, value):
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
+
if type(key) is tuple:
|
218
|
+
value = frameify(value, "value")
|
219
|
+
|
220
|
+
if len(key) != 2:
|
221
|
+
raise NotImplementedError("Only 2D slicing is supported")
|
222
|
+
|
223
|
+
if not all(isinstance(x, slice) for x in key):
|
224
|
+
raise NotImplementedError("Only 2D slicing is supported")
|
225
|
+
|
226
|
+
miny = key[0].start if key[0].start is not None else 0
|
227
|
+
maxy = key[0].stop if key[0].stop is not None else self.shape[0]
|
228
|
+
minx = key[1].start if key[1].start is not None else 0
|
229
|
+
maxx = key[1].stop if key[1].stop is not None else self.shape[1]
|
230
|
+
|
231
|
+
# handle negative indices
|
232
|
+
if miny < 0:
|
233
|
+
miny = self.shape[0] + miny
|
234
|
+
if maxy < 0:
|
235
|
+
maxy = self.shape[0] + maxy
|
236
|
+
if minx < 0:
|
237
|
+
minx = self.shape[1] + minx
|
238
|
+
if maxx < 0:
|
239
|
+
maxx = self.shape[1] + maxx
|
240
|
+
|
241
|
+
if (
|
242
|
+
maxy <= miny
|
243
|
+
or maxx <= minx
|
244
|
+
or miny < 0
|
245
|
+
or minx < 0
|
246
|
+
or maxy > self.shape[0]
|
247
|
+
or maxx > self.shape[1]
|
248
|
+
):
|
249
|
+
raise NotImplementedError("Invalid slice")
|
250
|
+
|
251
|
+
if value.shape[0] != maxy - miny or value.shape[1] != maxx - minx:
|
252
|
+
raise NotImplementedError("Shape mismatch")
|
253
|
+
|
254
|
+
self._mut()
|
255
|
+
value._mut()
|
256
|
+
|
257
|
+
self._f = _slice_write_mat(self._f, value._f, miny, maxy, minx, maxx)
|
258
|
+
elif type(key) is Frame or type(key) is np.ndarray:
|
259
|
+
key = frameify(key, "key")
|
260
|
+
|
261
|
+
if key.shape[0] != self.shape[0] or key.shape[1] != self.shape[1]:
|
262
|
+
raise NotImplementedError("Shape mismatch")
|
263
|
+
|
264
|
+
if key.shape[2] != 1:
|
265
|
+
raise NotImplementedError("Only 1-channel mask frames are supported")
|
266
|
+
|
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")
|
270
|
+
value = [float(x) for x in value]
|
271
|
+
if len(value) == 3:
|
272
|
+
value.append(255.0)
|
273
|
+
|
274
|
+
self._mut()
|
275
|
+
key._mut()
|
276
|
+
|
277
|
+
self._f = _set_to(self._f, value, key._f)
|
278
|
+
else:
|
279
|
+
raise NotImplementedError(
|
280
|
+
"__setitem__ only supports slicing by a 2d tuple or a mask frame"
|
281
|
+
)
|
217
282
|
|
218
283
|
|
219
284
|
def _inline_frame(arr):
|
@@ -341,6 +406,8 @@ class _IgniVideoWriter:
|
|
341
406
|
fps,
|
342
407
|
size,
|
343
408
|
batch_size=1024,
|
409
|
+
compression="gzip",
|
410
|
+
ttl=3600,
|
344
411
|
vod_segment_length=Fraction(2, 1),
|
345
412
|
):
|
346
413
|
server = _server()
|
@@ -358,27 +425,35 @@ class _IgniVideoWriter:
|
|
358
425
|
|
359
426
|
assert isinstance(size, tuple) or isinstance(size, list)
|
360
427
|
assert len(size) == 2
|
361
|
-
|
428
|
+
height, width = size
|
429
|
+
assert ttl is None or isinstance(ttl, int)
|
362
430
|
self._spec = server.create_spec(
|
363
|
-
width, height, "yuv420p", vod_segment_length, 1 / self._f_time
|
431
|
+
width, height, "yuv420p", vod_segment_length, 1 / self._f_time, ttl=ttl
|
364
432
|
)
|
365
433
|
self._batch_size = batch_size
|
434
|
+
assert compression is None or compression in ["gzip"]
|
435
|
+
self._compression = compression
|
366
436
|
self._idx = 0
|
367
|
-
self.
|
437
|
+
self._feb = vf._FrameExpressionBlock()
|
368
438
|
|
369
439
|
def _flush(self, terminal=False):
|
370
440
|
server = _server()
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
441
|
+
if len(self._feb) > 0:
|
442
|
+
server.push_spec_part_block(
|
443
|
+
self._spec,
|
444
|
+
self._idx - len(self._feb),
|
445
|
+
[self._feb],
|
446
|
+
terminal=terminal,
|
447
|
+
compression=self._compression,
|
448
|
+
)
|
449
|
+
self._feb = vf._FrameExpressionBlock()
|
450
|
+
else:
|
451
|
+
server.push_spec_part_block(
|
452
|
+
self._spec,
|
453
|
+
self._idx - len(self._feb),
|
454
|
+
[],
|
455
|
+
terminal=terminal,
|
456
|
+
)
|
382
457
|
|
383
458
|
def spec(self):
|
384
459
|
return self._spec
|
@@ -397,18 +472,14 @@ class _IgniVideoWriter:
|
|
397
472
|
if frame._fmt["pix_fmt"] != self._spec._fmt["pix_fmt"]:
|
398
473
|
f_obj = _filter_scale(frame._f, pix_fmt=self._spec._fmt["pix_fmt"])
|
399
474
|
frame = Frame(f_obj, self._spec._fmt)
|
400
|
-
|
401
|
-
self._frame_buffer.append((t, frame._f if frame is not None else None))
|
475
|
+
self._feb.insert_frame(frame._f if frame is not None else None)
|
402
476
|
self._idx += 1
|
403
477
|
|
404
|
-
if len(self.
|
478
|
+
if len(self._feb) >= self._batch_size:
|
405
479
|
self._flush()
|
406
480
|
|
407
481
|
def release(self):
|
408
|
-
|
409
|
-
self._flush(True)
|
410
|
-
else:
|
411
|
-
self._explicit_terminate()
|
482
|
+
self._flush(True)
|
412
483
|
|
413
484
|
|
414
485
|
class _YrdenVideoWriter:
|
@@ -443,8 +514,8 @@ class _YrdenVideoWriter:
|
|
443
514
|
|
444
515
|
def spec(self) -> vf.YrdenSpec:
|
445
516
|
fmt = {
|
446
|
-
"width": self._size[
|
447
|
-
"height": self._size[
|
517
|
+
"width": self._size[1],
|
518
|
+
"height": self._size[0],
|
448
519
|
"pix_fmt": self._pix_fmt,
|
449
520
|
}
|
450
521
|
domain = _fps_to_ts(self._fps, len(self._frames))
|
@@ -478,18 +549,33 @@ def frameify(obj, field_name=None):
|
|
478
549
|
def imread(path, *args):
|
479
550
|
if len(args) > 0:
|
480
551
|
raise NotImplementedError("imread does not support additional arguments")
|
481
|
-
|
482
552
|
assert path.lower().endswith((".jpg", ".jpeg", ".png"))
|
483
553
|
server = _server()
|
484
|
-
|
485
|
-
|
486
|
-
|
554
|
+
|
555
|
+
if type(server) is vf.YrdenServer:
|
556
|
+
source = vf.YrdenSource(server, str(uuid.uuid4()), path, 0)
|
557
|
+
frame = Frame(source.iloc[0], source.fmt())
|
558
|
+
return frame
|
559
|
+
else:
|
560
|
+
cap = VideoCapture(path)
|
561
|
+
assert cap.isOpened()
|
562
|
+
assert len(cap._source) == 1
|
563
|
+
ret, frame = cap.read()
|
564
|
+
assert ret
|
565
|
+
cap.release()
|
566
|
+
return frame
|
487
567
|
|
488
568
|
|
489
569
|
def imwrite(path, img, *args):
|
490
570
|
if len(args) > 0:
|
491
571
|
raise NotImplementedError("imwrite does not support additional arguments")
|
492
572
|
|
573
|
+
server = _server()
|
574
|
+
if type(server) is vf.IgniServer:
|
575
|
+
raise NotImplementedError(
|
576
|
+
"imwrite is only supported with YrdenServer, not IgniServer"
|
577
|
+
)
|
578
|
+
|
493
579
|
img = frameify(img)
|
494
580
|
|
495
581
|
fmt = img._fmt.copy()
|
@@ -518,7 +604,7 @@ def imwrite(path, img, *args):
|
|
518
604
|
fmt["pix_fmt"] = "yuvj420p"
|
519
605
|
|
520
606
|
spec = vf.YrdenSpec(domain, lambda t, i: f, fmt)
|
521
|
-
spec.save(
|
607
|
+
spec.save(server, path, encoder="mjpeg")
|
522
608
|
else:
|
523
609
|
raise Exception("Unsupported image format")
|
524
610
|
|
@@ -546,6 +632,39 @@ def vidplay(video, *args, **kwargs):
|
|
546
632
|
raise Exception("Unsupported video type to vidplay")
|
547
633
|
|
548
634
|
|
635
|
+
def zeros(shape, dtype=np.uint8):
|
636
|
+
"""
|
637
|
+
Create a black frame. Mimics numpy.zeros.
|
638
|
+
"""
|
639
|
+
assert isinstance(shape, tuple) or isinstance(shape, list)
|
640
|
+
assert len(shape) == 3
|
641
|
+
assert shape[2] in [1, 3]
|
642
|
+
assert dtype == np.uint8
|
643
|
+
|
644
|
+
height, width, channels = shape
|
645
|
+
if channels == 1:
|
646
|
+
pix_fmt = "gray"
|
647
|
+
else:
|
648
|
+
pix_fmt = "rgb24"
|
649
|
+
|
650
|
+
f = _black(width=width, height=height, pix_fmt=pix_fmt)
|
651
|
+
fmt = {"width": width, "height": height, "pix_fmt": pix_fmt}
|
652
|
+
return Frame(f, fmt)
|
653
|
+
|
654
|
+
|
655
|
+
def resize(src, dsize):
|
656
|
+
src = frameify(src)
|
657
|
+
src._mut()
|
658
|
+
|
659
|
+
assert isinstance(dsize, tuple) or isinstance(dsize, list)
|
660
|
+
assert len(dsize) == 2
|
661
|
+
height, width = dsize
|
662
|
+
|
663
|
+
f = _filter_scale(src._f, width=width, height=height)
|
664
|
+
fmt = {"width": width, "height": height, "pix_fmt": src._fmt["pix_fmt"]}
|
665
|
+
return Frame(f, fmt)
|
666
|
+
|
667
|
+
|
549
668
|
def rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None):
|
550
669
|
"""
|
551
670
|
cv.rectangle( img, pt1, pt2, color[, thickness[, lineType[, shift]]] )
|
@@ -2,17 +2,18 @@
|
|
2
2
|
vidformer.supervision is the [supervision](https://supervision.roboflow.com/) frontend for [vidformer](https://github.com/ixlab/vidformer).
|
3
3
|
"""
|
4
4
|
|
5
|
-
|
5
|
+
from math import sqrt
|
6
6
|
|
7
|
-
import supervision as _sv
|
8
7
|
import numpy as np
|
9
|
-
|
8
|
+
import supervision as _sv
|
9
|
+
from supervision import Color, ColorLookup, ColorPalette, Detections
|
10
10
|
from supervision.annotators.utils import resolve_color, resolve_text_background_xyxy
|
11
|
-
from supervision.detection.utils import spread_out_boxes
|
12
11
|
from supervision.config import CLASS_NAME_DATA_FIELD
|
13
|
-
from
|
12
|
+
from supervision.detection.utils import spread_out_boxes
|
14
13
|
from supervision.geometry.core import Position
|
15
14
|
|
15
|
+
import vidformer.cv2 as vf_cv2
|
16
|
+
|
16
17
|
CV2_FONT = vf_cv2.FONT_HERSHEY_SIMPLEX
|
17
18
|
|
18
19
|
|
@@ -499,31 +500,40 @@ class LabelAnnotator:
|
|
499
500
|
|
500
501
|
border_radius = min(border_radius, min(width, height) // 2)
|
501
502
|
|
502
|
-
|
503
|
-
((x1 + border_radius, y1), (x2 - border_radius, y2)),
|
504
|
-
((x1, y1 + border_radius), (x2, y2 - border_radius)),
|
505
|
-
]
|
506
|
-
circle_centers = [
|
507
|
-
(x1 + border_radius, y1 + border_radius),
|
508
|
-
(x2 - border_radius, y1 + border_radius),
|
509
|
-
(x1 + border_radius, y2 - border_radius),
|
510
|
-
(x2 - border_radius, y2 - border_radius),
|
511
|
-
]
|
512
|
-
|
513
|
-
for coordinates in rectangle_coordinates:
|
503
|
+
if border_radius <= 0:
|
514
504
|
vf_cv2.rectangle(
|
515
505
|
img=scene,
|
516
|
-
pt1=
|
517
|
-
pt2=
|
518
|
-
color=color,
|
519
|
-
thickness=-1,
|
520
|
-
)
|
521
|
-
for center in circle_centers:
|
522
|
-
vf_cv2.circle(
|
523
|
-
img=scene,
|
524
|
-
center=center,
|
525
|
-
radius=border_radius,
|
506
|
+
pt1=(x1, y1),
|
507
|
+
pt2=(x2, y2),
|
526
508
|
color=color,
|
527
509
|
thickness=-1,
|
528
510
|
)
|
511
|
+
else:
|
512
|
+
rectangle_coordinates = [
|
513
|
+
((x1 + border_radius, y1), (x2 - border_radius, y2)),
|
514
|
+
((x1, y1 + border_radius), (x2, y2 - border_radius)),
|
515
|
+
]
|
516
|
+
circle_centers = [
|
517
|
+
(x1 + border_radius, y1 + border_radius),
|
518
|
+
(x2 - border_radius, y1 + border_radius),
|
519
|
+
(x1 + border_radius, y2 - border_radius),
|
520
|
+
(x2 - border_radius, y2 - border_radius),
|
521
|
+
]
|
522
|
+
|
523
|
+
for coordinates in rectangle_coordinates:
|
524
|
+
vf_cv2.rectangle(
|
525
|
+
img=scene,
|
526
|
+
pt1=coordinates[0],
|
527
|
+
pt2=coordinates[1],
|
528
|
+
color=color,
|
529
|
+
thickness=-1,
|
530
|
+
)
|
531
|
+
for center in circle_centers:
|
532
|
+
vf_cv2.circle(
|
533
|
+
img=scene,
|
534
|
+
center=center,
|
535
|
+
radius=border_radius,
|
536
|
+
color=color,
|
537
|
+
thickness=-1,
|
538
|
+
)
|
529
539
|
return scene
|
@@ -0,0 +1,6 @@
|
|
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,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
vidformer/__init__.py,sha256=qpWcttHsW1wnaGx5__qJqaT-m5VF7yMiHCxdm10Fjek,44916
|
2
|
-
vidformer/cv2/__init__.py,sha256=DGm5NB4FGCHxPVez-yO748DjocKruxn4QBqqThgskWI,25555
|
3
|
-
vidformer/supervision/__init__.py,sha256=T2QJ3gKtUSoKOlxAf06TG4fD9IgIDuBpiVOBKVk3qAw,17150
|
4
|
-
vidformer-0.10.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
5
|
-
vidformer-0.10.0.dist-info/METADATA,sha256=f_aUIFbQUoVJuYTKtqexwyN5uzWRoS2Fvd2dHZ_EGbo,1800
|
6
|
-
vidformer-0.10.0.dist-info/RECORD,,
|
File without changes
|