2024-10-04 12:25:03 +00:00
"""
This project produces " detections " for DeepSORT
in an attempt to trick the algorithm into moonwalking
over a crowd .
The framerate of rendering and detection can be distinct .
Also , all parameters ( incl . framerate ) can change along the way ,
thus positions cannot depend on that .
"""
from __future__ import annotations
from dataclasses import dataclass
import dataclasses
from typing import Optional
import time
import pyglet
import logging
import numpy as np
# from deep_sort_realtime.deepsort_tracker import DeepSort
from sort import Sort
from collections import defaultdict
2024-10-18 12:14:16 +00:00
from collections . abc import Callable
2024-10-06 11:36:15 +00:00
import math
2024-10-18 12:14:16 +00:00
import easing_functions
from easing_functions . easing import EasingBase
from functools import partial
2024-10-04 12:25:03 +00:00
Interval = float # seconds
logger = logging . getLogger ( ' moonwalk ' )
2024-10-15 08:42:22 +00:00
colorset = [ ( 255 , 0 , 0 ) ,
2024-10-06 11:36:15 +00:00
( 0 , 0 , 255 ) ,
( 0 , 255 , 0 ) ,
( 0 , 255 , 255 ) ,
( 255 , 0 , 0 ) ,
( 255 , 0 , 255 ) ,
( 255 , 255 , 0 )
]
2024-10-17 11:38:44 +00:00
VELOCITY_FACTOR = 30 * .05 # a pixels/second velocity of 400 gives a frame-duration of .05
2024-10-15 13:33:36 +00:00
images_nrs = range ( 125 , 162 )
2024-10-18 12:14:16 +00:00
angles = list ( range ( 0 , 360 , 15 ) )
# angles.pop(angles.index(60))
# angles.pop(angles.index(75))
# angles.pop(angles.index(90))
2024-10-15 13:33:36 +00:00
2024-10-17 11:38:44 +00:00
walk_animations = { }
for a in angles :
walk_images = [ ]
for nr in images_nrs :
# smaller textures doesn't seem to matter much in speed
fn = f ' walk-animation/OUT/frame { nr : 03d } _ { a : 02d } .png '
pic : pyglet . image . ImageData = pyglet . image . load ( fn )
walk_images . append ( pic )
walk_animations [ a ] = pyglet . image . animation . Animation . from_image_sequence ( walk_images , 1 , True )
2024-10-18 12:14:16 +00:00
logger . info ( " Loaded animation for {a} degree " )
2024-10-17 11:38:44 +00:00
walk_animation = walk_animations [ 0 ]
2024-10-15 13:33:36 +00:00
# walk_image = pyglet.image.load('walk_bw.jpg') # TODO investigaet pyglet.resource: https://pyglet.readthedocs.io/en/latest/programming_guide/image.html#displaying-images
# image_grid = pyglet.image.ImageGrid(walk_image, rows=1, columns=4)
# # texture_grid = image_grid.get_texture_sequence()
# walk_animation = image_grid.get_animation(period=.05)
2024-10-14 17:13:16 +00:00
walk_animation_dim = {
' y ' : walk_animation . get_max_height ( ) ,
' x ' : walk_animation . get_max_width ( )
}
2024-10-18 12:14:16 +00:00
class PotmeterShape ( ) :
value = 0
2024-10-04 12:25:03 +00:00
2024-10-18 12:14:16 +00:00
def __init__ ( self , cx , cy , r , canvas : Canvas , input : Input , batch : Optional [ pyglet . graphics . Batch ] = None ) :
# A rendered potmeter
self . input = input
self . canvas = canvas
self . batch = batch
self . cx = cx
self . cy = cy
self . r = r
x , y = self . get_xy_for_t ( input . t )
self . shapes = (
pyglet . shapes . Arc ( cx , cy , 30 , batch = batch ) , # potmeter
pyglet . shapes . Line ( cx , cy , x , y , width = 2 , batch = batch ) # potmeter position
)
# TODO: mouse scroll
# TODO: listen for actual values
self . input . add_listener ( self . rotate )
def rotate ( self , value , old_value , t , old_t ) :
x , y = self . get_xy_for_t ( t )
self . shapes [ 1 ] . x2 = x
self . shapes [ 1 ] . y2 = y
# self.shapes[1].x2
# self.shapes[1].y2
pass
# self.shapes.line......
def get_xy_for_t ( self , t ) :
return (
self . cx + self . r * math . sin ( t * math . pi * 2 ) ,
self . cy + self . r * math . cos ( t * math . pi * 2 )
)
def on_mouse_scroll ( self , x , y , scroll_x , scroll_y ) :
# determine xy position to select var to change,
# then change according to scroll_y
if x > = self . cx - self . r and \
x < = self . cx + self . r and \
y > = self . cy - self . r and \
y < = self . cy + self . r :
self . input . t + = scroll_y / 100
class Input ( ) :
"""
maps linear input ( t ) to params value
"""
def __init__ ( self , default_t : float = 0 , mapping : EasingBase = easing_functions . LinearInOut ( start = 0 , end = 100 ) ) :
# listereners are callables which have 2 float params and return nothing:
self . _listeners : list [ Callable [ [ float , float , float , float ] , None ] ] = [ ]
self . mapping = mapping
self . _t = None
self . _v = None
self . t = default_t
# pyglet.text.Label(100,100,30) #name
# pyglet.text.Label(100,100,30) # current value
@property
def t ( self ) :
return self . _t
@t.setter
def t ( self , t : float ) :
"""
Set the underlying linear value
"""
# 0 <= v <= 1024
old_t = self . _t
old_v = self . _v
t = min ( 1 , max ( 0 , t ) )
if not old_t or abs ( old_t - t ) > 0.002 : # resolution of 500, to mitigate analog potmeter noise
self . _t = t
self . _v = self . mapping ( self . _t )
for listener in self . _listeners :
listener ( self . value , old_v , self . _t , old_t )
@property
def value ( self ) - > float :
"""
Get value converted from self . v
"""
return self . mapping ( self . _t )
def add_listener ( self , callback : Callable [ [ float , float , float , float ] , None ] ) :
2024-10-06 10:41:06 +00:00
"""
Add a listener for a change , callback gets
2024-10-18 12:14:16 +00:00
called with ( old_value , new_value )
2024-10-06 10:41:06 +00:00
"""
2024-10-18 12:14:16 +00:00
self . _listeners . append ( callback )
2024-10-06 10:41:06 +00:00
2024-10-18 12:14:16 +00:00
def get_listeners ( self ) - > list [ Callable [ [ float , float , float , float ] , None ] ] :
2024-10-06 10:41:06 +00:00
return self . _listeners
2024-10-18 12:14:16 +00:00
class SteppedEasing ( EasingBase ) :
def __init__ ( self , steps : list ) :
self . steps = steps
pass
def ease ( self , alpha ) :
i = math . ceil ( max ( 1e-100 , alpha ) * len ( self . steps ) ) - 1
return self . steps [ i ]
class Params :
def __init__ ( self ) :
self . visual_variability = Input ( 0 , easing_functions . LinearInOut ( start = 0 , end = 100 ) )
# video_fps = Input(60
self . tracker_fps = Input ( .3 , easing_functions . QuadEaseIn ( start = .1 , end = 60 ) )
# iou = None
self . emitter_speed = Input ( .2 , easing_functions . LinearInOut ( start = 0 , end = 7 ) ) # objects per second
self . object_velocity = Input ( .4 , easing_functions . LinearInOut ( start = 0 , end = 500 ) ) # pixels/second TODO: allow negative
# velocity_decay = Input(1, easing_functions.LinearInOut(start=0, end=100)) # make system a bit springy
self . iou_threshold = Input ( 0 , easing_functions . LinearInOut ( start = 0 , end = 1 ) ) # SORT Intersection over union
self . movement_angle = Input ( 0 , SteppedEasing ( angles ) ) # angle at which the blobs move (-45 - +45 degrees)
def keys ( self ) - > list [ str ] :
return list ( filter ( lambda a : not a . startswith ( ' __ ' ) and a not in [ ' keys ' , ' items ' , ' values ' ] , dir ( self ) ) )
2024-10-06 10:41:06 +00:00
2024-10-18 12:14:16 +00:00
def items ( self ) - > list [ tuple [ str , Input ] ] :
return [
( k , getattr ( self , k ) ) for k in self . keys ( )
]
2024-10-06 10:41:06 +00:00
2024-10-18 12:14:16 +00:00
def values ( self ) - > list [ Input ] :
return [
getattr ( self , k ) for k in self . keys ( )
]
2024-10-15 08:42:22 +00:00
2024-10-04 12:25:03 +00:00
class DetectedObject :
def __init__ ( self , canvas : Canvas ) :
self . canvas = canvas
# TODO handle variability
self . v = self . canvas . params . object_velocity
2024-10-15 13:33:36 +00:00
self . w = 160 # width
self . h = 320 # height
2024-10-15 08:42:22 +00:00
self . l = - self . w # left
2024-10-18 12:14:16 +00:00
self . t = ( self . canvas . height - self . h ) / 2 - math . sin ( self . canvas . params . movement_angle . value / 360 * math . pi * 2 ) * self . canvas . width / 2 # top
2024-10-04 12:25:03 +00:00
2024-10-14 17:13:16 +00:00
# self.shape = pyglet.shapes.Rectangle(self.l, self.t, self.w, self.h, color=(255, 22, 20), batch=self.canvas.batch_figures)
2024-10-17 11:38:44 +00:00
2024-10-14 17:13:16 +00:00
2024-10-18 12:14:16 +00:00
self . shape = pyglet . sprite . Sprite ( img = walk_animations [ self . canvas . angle ] , x = self . l , y = self . t , batch = self . canvas . batch_figures )
2024-10-14 17:13:16 +00:00
self . shape . scale_x = self . w / walk_animation_dim [ ' x ' ]
self . shape . scale_y = self . h / walk_animation_dim [ ' y ' ]
2024-10-04 12:25:03 +00:00
# rectangle.opacity = 128
# rectangle.rotation = 33
#TODO renderer
def update ( self , dt : Interval ) :
"""
Update position
"""
2024-10-18 12:14:16 +00:00
self . l + = dt * self . canvas . params . object_velocity . value * math . cos ( self . canvas . params . movement_angle . value / 360 * 2 * math . pi )
self . t + = dt * self . canvas . params . object_velocity . value * math . sin ( self . canvas . params . movement_angle . value / 360 * 2 * math . pi )
2024-10-04 12:25:03 +00:00
self . shape . x = self . l
2024-10-17 11:38:44 +00:00
self . shape . y = self . t
2024-10-04 12:25:03 +00:00
# TODO exponential decay with self.params.velocity_decay
class ObjectEmitter :
"""
Emit detectable objects
"""
def __init__ ( self , params : Params , canvas : Canvas ) :
self . lastEmit = 0
self . params = params
self . canvas = canvas
def emit ( self , dt : Interval ) - > list [ DetectedObject ] :
self . lastEmit + = dt
2024-10-18 12:14:16 +00:00
if self . params . emitter_speed . value == 0 :
return [ ]
if self . lastEmit is None or self . lastEmit > = 1 / self . params . emitter_speed . value :
2024-10-04 12:25:03 +00:00
logger . info ( ' emit! ' )
obj = DetectedObject ( self . canvas )
self . lastEmit = 0
return [ obj ]
return [ ]
2024-10-06 11:36:15 +00:00
2024-10-17 11:38:44 +00:00
class MissingDict [ T ] ( dict ) :
2024-10-06 11:36:15 +00:00
"""
collections . defaultdict does not accept arguments , but this is what we want / need .
This implementation should do the trick
"""
2024-10-18 12:14:16 +00:00
def __init__ ( self , factory : Callable , values = { } ) :
2024-10-06 11:36:15 +00:00
self . update ( values )
self . factory = factory
2024-10-17 11:38:44 +00:00
def __missing__ ( self , key ) - > T :
2024-10-06 11:36:15 +00:00
self [ key ] = self . factory ( key )
return self [ key ]
2024-10-04 12:25:03 +00:00
class Canvas :
"""
A canvas with moving objects
"""
2024-10-04 17:56:16 +00:00
def __init__ ( self , params : Params ) :
2024-10-15 13:33:36 +00:00
self . width = 1920
self . height = 1080
2024-10-04 12:25:03 +00:00
self . objects : list [ DetectedObject ] = [ ]
self . lastSnapshot : Optional [ float ] = None
self . params = params
self . emitter = ObjectEmitter ( self . params , self )
2024-10-04 17:56:16 +00:00
self . hide_stats = False
config = pyglet . gl . Config ( sample_buffers = 1 , samples = 4 , double_buffer = True )
# , fullscreen=self.config.render_window
self . window = pyglet . window . Window ( width = self . width , height = self . height , config = config , fullscreen = False )
self . window . set_handler ( ' on_draw ' , self . on_draw )
self . window . set_handler ( ' on_key_press ' , self . on_key_press )
self . window . set_handler ( ' on_mouse_scroll ' , self . on_mouse_scroll )
self . window . set_handler ( ' on_refresh ' , self . on_refresh )
# self.window.set_handler('on_refresh', self.on_refresh)
# self.window.set_handler('on_close', self.on_close)
# Purple background color:
2024-10-15 13:33:36 +00:00
pyglet . gl . glClearColor ( 230 / 255 , 230 / 255 , 230 / 255 , 230 / 255 )
2024-10-14 17:13:16 +00:00
self . draw_stats = True
2024-10-17 11:38:44 +00:00
self . fps_display = pyglet . window . FPSDisplay ( window = self . window , color = ( 255 , 255 , 255 , 255 ) , samples = 100 )
2024-10-04 17:56:16 +00:00
self . fps_display . label . x = self . window . width - 150
self . fps_display . label . y = self . window . height - 17
self . fps_display . label . bold = False
self . fps_display . label . font_size = 10
self . batch_figures = pyglet . graphics . Batch ( )
self . batch_bounding_boxes = pyglet . graphics . Batch ( )
self . batch_info = pyglet . graphics . Batch ( )
2024-10-04 12:25:03 +00:00
self . tracks = [ ]
self . labels = {
2024-10-04 17:56:16 +00:00
' objects ' : pyglet . text . Label ( " " , x = 20 , y = 30 , color = ( 255 , 255 , 255 , 255 ) , batch = self . batch_info ) ,
' tracks ' : pyglet . text . Label ( " " , x = 120 , y = 30 , color = ( 255 , 255 , 255 , 255 ) , batch = self . batch_info ) ,
2024-10-04 12:25:03 +00:00
}
2024-10-18 12:14:16 +00:00
print ( ' ok ' , self . params )
self . potmeters : list [ PotmeterShape ] = [ ]
for i , item in enumerate ( self . params . values ( ) ) :
self . potmeters . append (
PotmeterShape ( 300 + 80 * i , 130 , 30 , self , item , self . batch_info )
)
for i , param in enumerate ( self . params . items ( ) ) :
name = param [ 0 ]
value = param [ 1 ]
print ( name , value )
self . labels [ name ] = pyglet . text . Label ( f " { name } : { value . value } " , x = 20 , y = 30 + 20 * ( i + 1 ) , color = ( 255 , 255 , 255 , 255 ) , batch = self . batch_info )
2024-10-04 12:25:03 +00:00
2024-10-06 10:41:06 +00:00
# TODO: Add a number next to the box using pyglet.graphics.Group()
2024-10-17 11:38:44 +00:00
self . track_shapes : MissingDict [ np . float64 , tuple [ pyglet . shapes . Box , pyglet . text . Label ] ] = MissingDict ( self . getTrackBboxShapes )
2024-10-04 12:25:03 +00:00
2024-10-06 11:36:15 +00:00
self . tracker = Sort ( max_age = 5 , min_hits = 1 , iou_threshold = 0 ) #DeepSort(max_age=5)
2024-10-04 12:25:03 +00:00
2024-10-18 12:14:16 +00:00
pyglet . clock . schedule_interval ( self . on_track , 1 / self . params . tracker_fps . value )
2024-10-04 12:25:03 +00:00
self . interval_items : list [ pyglet . clock . _ScheduledIntervalItem ] = [ i for i in pyglet . clock . _default . _schedule_interval_items if i . func == self . on_track ]
2024-10-18 12:14:16 +00:00
self . params . tracker_fps . add_listener ( self . on_tracker_fps_change )
self . params . iou_threshold . add_listener ( self . on_iou_threshold_change )
self . params . object_velocity . add_listener ( self . on_object_velocity_change )
self . params . movement_angle . add_listener ( self . on_movement_angle_change )
self . on_movement_angle_change ( self . params . movement_angle . value , None , None , None ) # set self.angle
2024-10-15 08:42:22 +00:00
# trigger first time around
2024-10-18 12:14:16 +00:00
self . on_object_velocity_change ( self . params . object_velocity . value , None , None , None )
2024-10-06 11:36:15 +00:00
2024-10-17 11:38:44 +00:00
def getTrackBboxShapes ( self , track_id ) - > tuple [ pyglet . shapes . Box , pyglet . text . Label ] :
2024-10-06 11:36:15 +00:00
color = colorset [ int ( track_id ) % len ( colorset ) ]
2024-10-17 11:38:44 +00:00
return (
2024-10-06 11:36:15 +00:00
pyglet . shapes . Box ( 0 , 0 , 0 , 0 , color = color , thickness = 2 , batch = self . batch_bounding_boxes ) ,
2024-10-17 11:38:44 +00:00
pyglet . text . Label ( f " { track_id : .0f } " , x = 0 , y = 0 , anchor_y = ' top ' , color = color , batch = self . batch_bounding_boxes ) ,
2024-10-06 11:36:15 +00:00
# pyglet.shapes.Lab(0,0,0,0,color=(0,255,0),thickness=2, batch=self.batch_bounding_boxes)
2024-10-17 11:38:44 +00:00
)
2024-10-04 12:25:03 +00:00
2024-10-15 08:42:22 +00:00
2024-10-18 12:14:16 +00:00
def on_tracker_fps_change ( self , value , old_value , t , old_t ) :
2024-10-06 10:41:06 +00:00
"""
Param dataclass listener for changes to tracker_fps
"""
for ii in self . interval_items :
2024-10-18 12:14:16 +00:00
ii . interval = 1 / value
2024-10-06 10:41:06 +00:00
logger . debug ( f " Set tracker interval to { ii . interval } " )
2024-10-14 17:13:16 +00:00
2024-10-18 12:14:16 +00:00
def on_object_velocity_change ( self , value , old_value , t , old_t ) :
2024-10-15 08:42:22 +00:00
"""
Param dataclass listener for changes to object_velocity as to change walk animation
"""
2024-10-18 12:14:16 +00:00
# TODO: when walking backwards, animation should reverse
duration = max ( .001 , VELOCITY_FACTOR / max ( .1 , value ) )
logger . debug ( f " set frame duration to { duration =} (for { value } p/s, factor: { VELOCITY_FACTOR } ) " )
2024-10-17 11:38:44 +00:00
for anim in walk_animations . values ( ) :
for frame in anim . frames :
# a velocity of
frame . duration = duration
2024-10-15 08:42:22 +00:00
# for ii in self.interval_items:
# ii.interval = 1/new_value
# logger.debug(f"Set tracker interval to {ii.interval}")
2024-10-18 12:14:16 +00:00
def on_movement_angle_change ( self , value , old_value , t , old_t ) :
2024-10-17 11:38:44 +00:00
"""
Param dataclass listener for changes to movement - angle
"""
2024-10-18 12:14:16 +00:00
angle = min ( angles , key = lambda x : abs ( x - value ) )
2024-10-17 11:38:44 +00:00
if hasattr ( self , ' angle ' ) and self . angle == angle :
return
self . angle = angle
for obj in self . objects :
2024-10-18 12:14:16 +00:00
idx = obj . shape . _frame_index # setting the .image property removes the frame offset, this causes the animations to sync. Because all animations have the same nr of frames, we can get and re-set this offset
2024-10-17 11:38:44 +00:00
obj . shape . image = walk_animations [ self . angle ]
obj . shape . _frame_index = idx
2024-10-18 12:14:16 +00:00
def on_iou_threshold_change ( self , value , old_value , t , old_t ) :
2024-10-14 17:13:16 +00:00
"""
Param dataclass listener for changes to iou_threshold
"""
2024-10-18 12:14:16 +00:00
self . tracker . iou_threshold = value
2024-10-04 12:25:03 +00:00
2024-10-04 17:56:16 +00:00
def run ( self ) :
self . event_loop = pyglet . app . EventLoop ( )
# pyglet.clock.schedule_interval(self.check_running, 0.1)
# pyglet.clock.schedule(self.check_frames)
# pyglet.clock.schedule(self.track)
self . event_loop . run ( )
def on_draw ( self ) :
self . window . clear ( )
2024-10-17 11:38:44 +00:00
2024-10-04 17:56:16 +00:00
self . batch_figures . draw ( )
self . batch_bounding_boxes . draw ( )
self . batch_info . draw ( )
2024-10-14 17:13:16 +00:00
if self . draw_stats :
self . fps_display . draw ( )
2024-10-04 17:56:16 +00:00
def on_close ( self ) :
logger . info ( ' closing ' )
pass
def on_key_press ( self , symbol , modifiers ) :
2024-10-06 10:41:06 +00:00
if symbol == pyglet . window . key . Q or symbol == pyglet . window . key . ESCAPE :
2024-10-04 17:56:16 +00:00
self . window . close ( )
exit ( )
if symbol == pyglet . window . key . V :
level = logging . INFO if logger . getEffectiveLevel ( ) == logging . DEBUG else logging . DEBUG
logger . setLevel ( level )
logger . info ( f " set log level: { level } " )
2024-10-14 17:13:16 +00:00
if symbol == pyglet . window . key . S :
self . draw_stats = not self . draw_stats
logger . info ( f " rendering stats: { self . draw_stats } " )
2024-10-04 17:56:16 +00:00
def on_mouse_scroll ( self , x , y , scroll_x , scroll_y ) :
# determine xy position to select var to change,
# then change according to scroll_y
2024-10-18 12:14:16 +00:00
for potmeter in self . potmeters :
potmeter . on_mouse_scroll ( x , y , scroll_x , scroll_y )
2024-10-04 17:56:16 +00:00
2024-10-04 12:25:03 +00:00
def on_track ( self , dt ) :
# bbs = object_detector.detect(frame)
objects = self . snapshot ( )
# TODO chipper & embedder
# bbs = [([o.l, o.t, o.w, o.h], 1, 1) for o in objects]
# DEEP SORT: self.tracks = self.tracker.update_tracks(bbs, frame=np.zeros([1280,720])) # bbs expected to be a list of detections, each in tuples of ( [left,top,w,h], confidence, detection_class )
bbs = np . array ( [ [ o . l , o . t , o . l + o . w , o . t + o . h , 1 , 1 ] for o in objects ] )
self . tracks = self . tracker . update ( bbs ) # a numpy array of detections in the format [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]
# self.tracks is a np array where each row contains a valid bounding box and track_id (last column)
# remove old shapes
ids = [ track [ 4 ] for track in self . tracks ]
for k in list ( self . track_shapes . keys ( ) ) :
if k not in ids :
self . track_shapes . pop ( k )
logger . debug ( f " shape removed { k } " )
# print([t[4] for t in self.tracks])
2024-10-18 12:14:16 +00:00
# def set_tracker_fps(self, fps: float):
# self.params.tracker_fps.value = fps
# for interval in self.interval_items:
# interval.interval = 1/fps
2024-10-04 12:25:03 +00:00
def prune ( self ) :
"""
Loop over objects remove those out of the frame
"""
2024-10-18 12:14:16 +00:00
# TODO now that there is rotation, this check needs to be a bit more elaborated
2024-10-04 12:25:03 +00:00
for i , object in enumerate ( self . objects . copy ( ) ) :
if object . l > self . width :
logging . info ( f ' Delete { i } ' )
2024-10-15 13:33:36 +00:00
el = self . objects . pop ( i )
el . shape . delete ( ) # clear the attached shape
2024-10-04 12:25:03 +00:00
def snapshot ( self ) - > list [ DetectedObject ] :
"""
Update all object positions base on dt = now - lastSnapshot
"""
now = time . monotonic ( )
if self . lastSnapshot is None :
self . lastSnapshot = now
dt = now - self . lastSnapshot
self . objects . extend ( self . emitter . emit ( dt ) )
for object in self . objects :
object . update ( dt )
self . prune ( )
self . lastSnapshot = now
2024-10-17 11:38:44 +00:00
# print(f"duration: {time.monotonic()-now)
2024-10-04 12:25:03 +00:00
return self . objects
def on_refresh ( self , dt : float ) :
objects = self . snapshot ( )
self . labels [ ' objects ' ] . text = f " Objects: { len ( objects ) } "
self . labels [ ' tracks ' ] . text = f " Tracks: { len ( self . tracks ) } "
# self.labels['velocity'].text = f"Velocity: {self.params.object_velocity}"
# self.labels['tracker_fps'].text = f"Tracker FPS: {self.params.tracker_fps}"
2024-10-18 12:14:16 +00:00
for name , item in self . params . items ( ) :
self . labels [ name ] . text = f " { name } : { item . value : .3f } "
2024-10-04 12:25:03 +00:00
for track in self . tracks :
nr = track [ 4 ]
2024-10-17 11:38:44 +00:00
box : pyglet . shape . Box = self . track_shapes [ nr ] [ 0 ]
label : pyglet . text . Label = self . track_shapes [ nr ] [ 1 ]
if box . x == 0 and box . y == 0 :
# set initial position
box . x = track [ 0 ]
box . y = track [ 1 ]
label . x = track [ 0 ]
label . y = track [ 1 ]
box . width = track [ 2 ] - track [ 0 ]
box . height = track [ 3 ] - track [ 1 ]
else : # not really impact
# exponential decay
label . x = box . x = exponentialDecay ( box . x , track [ 0 ] , 12 , dt )
label . y = box . y = exponentialDecay ( box . y , track [ 1 ] , 12 , dt )
# setting width and height on label is not needed _and_ makes it super slow
box . width = exponentialDecay ( box . width , track [ 2 ] - track [ 0 ] , 12 , dt )
box . height = exponentialDecay ( box . height , track [ 3 ] - track [ 1 ] , 12 , dt )
2024-10-06 11:36:15 +00:00
2024-10-04 12:25:03 +00:00
# TODO: shape in DetectedObject
# rectangle = shapes.Rectangle(250, 300, 400, 200, color=(255, 22, 20), batch=batch)
# rectangle.opacity = 128
# rectangle.rotation = 33
# print(objects)
# id(objects)
2024-10-06 11:36:15 +00:00
def exponentialDecay ( a , b , decay , dt ) :
""" Exponential decay as alternative to Lerp
Introduced by Freya Holmér : https : / / www . youtube . com / watch ? v = LSNQuFEDOyQ
"""
return b + ( a - b ) * math . exp ( - decay * dt )
2024-10-04 12:25:03 +00:00
if __name__ == " __main__ " :
logging . basicConfig ( level = logging . INFO )
params = Params ( )
2024-10-04 17:56:16 +00:00
canvas = Canvas ( params )
canvas . run ( )
2024-10-04 12:25:03 +00:00