Page MenuHomedesp's stash

play.py
No OneTemporary

try: #local script
from lib.datatypes import PictureMatrix, RGB, FusionLightData, FusionLightEffect, FusionLightColor, FusionLightDirection
from lib.device import get_device
except ImportError: #as a module
from .lib.datatypes import PictureMatrix, RGB, FusionLightData, FusionLightEffect, FusionLightColor, FusionLightDirection
from .lib.device import get_device
from cv2.typing import MatLike
from typing import List
import sys, cv2, time, numpy, atexit
#10 is actually not transient - it is persistent as a secondary profile but for our purposes we can treat it as transient
#since nobody should have the need to write to a profile number this high (switching between profiles require os interaction anyways so might as well write a new image)
TRANSIENT_ID = 10
if len(sys.argv) < 2:
print(f'Usage: {__file__} <video path> [profile id]')
exit(-1)
if len(sys.argv) >= 3:
try:
profile = int(sys.argv[2])
if profile not in range(5):
raise TypeError()
except:
print('Invalid profile id given, defaulting to transience (will be reverted after play (or reboot if interrupted))...')
profile = TRANSIENT_ID
else:
profile = TRANSIENT_ID
dev = get_device()
orig_effect = dev.get_simple_light_effect()
#if transient, set it back to the old profile
if profile == TRANSIENT_ID:
def reset():
if orig_effect.fusion_effect < FusionLightEffect.Custom1:
dev.set_simple_light_effect(orig_effect)
else:
load = PictureMatrix.from_bytes(dev.get_custom_light_effect(orig_effect.fusion_effect - FusionLightEffect.Custom1))
dev.set_custom_light_effect(profile, load)
atexit.register(reset)
#change to profile for the image stream first (technically only need to change if not transient and current profile is non custom, but we revert afterwards anyway)
#to avoid any flashing set to black first (mainly when we are in simple modes which means last seen image will be shown right when set_simple_light_effect is called)
dev.set_custom_light_effect(profile, PictureMatrix([RGB(0,0,0) for i in range(105)]))
#need to set it to one of the "proper" profiles for it to know to update
dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Custom1 + profile if profile != TRANSIENT_ID else FusionLightEffect.Custom1, 1, 255, FusionLightColor.White, FusionLightDirection.Left2Right))
def set_frame(raw: MatLike, fps_data: List[float]) -> None:
pre = cv2.resize(raw, (19, 6), interpolation=cv2.INTER_AREA)
im = cv2.cvtColor(pre, cv2.COLOR_BGR2RGB)
#display what's supposed to be encoded in the keyboard at this frame
cv2.imshow('preview', cv2.resize(pre, (190*5, 60*5)))
if len(fps_data) > 1:
cv2.setWindowTitle('preview', f'Preview (FPS: {1/numpy.average(numpy.diff(fps_data))})')
cv2.waitKey(1)
#turns out even if i do translate this into a list of RGB instead of just directly send mats it still gives basically around the same fps
pixels = im
pixels = []
h, w, _ = im.shape
for y in range(h):
for x in range(w):
pixels.append(RGB(*im[y, x]))
#apply any adjustment needed to give true colors
dev.set_custom_light_effect(profile, PictureMatrix.pixel_matrix_to_keys(pixels), adjust=dev.ADJUST_RGB)
#apparently also works with static images
vid = cv2.VideoCapture(sys.argv[1])
#skip some frames so it's not too slow
#TODO make this less hardcoded - this value is from testing on my laptop with bad apple
AVG_FPS = 11
interval = vid.get(cv2.CAP_PROP_FPS) // AVG_FPS
actual_fps = []
#this actually doesnt help that much it just gives like 1 more fps and takes a while to buffer :(
PRECOMPUTE = False
if PRECOMPUTE:
buf = []
print('Buffering...')
count = 0
while vid.isOpened():
ret, frame = vid.read()
if ret:
buf.append(frame)
count += interval
vid.set(cv2.CAP_PROP_POS_FRAMES, count)
else:
vid.release()
break
print('Done. Starting player...')
for frame in buf:
actual_fps.append(time.time())
set_frame(frame, actual_fps)
#get an averagable data before popping
if len(actual_fps) > 10:
actual_fps.pop(0)
else:
count = 0
while vid.isOpened():
actual_fps.append(time.time())
ret, frame = vid.read()
if ret:
set_frame(frame, actual_fps)
count += interval
vid.set(cv2.CAP_PROP_POS_FRAMES, count)
#get an averagable data before popping
if len(actual_fps) > 10:
actual_fps.pop(0)
else:
vid.release()
break
#keep last frame until we manually quit
cv2.waitKey(0)

File Metadata

Mime Type
text/x-python
Expires
Tue, May 27, 10:49 AM (1 d, 14 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
80/96/ad6be6bccaa4d507d12c050ca3a3

Event Timeline