Anmation per angle

This commit is contained in:
Ruben van de Ven 2024-10-17 13:38:44 +02:00
parent 112cbd83da
commit e67395c44f
2 changed files with 74 additions and 38 deletions

View file

@ -36,16 +36,22 @@ colorset = [(255, 0, 0),
]
VELOCITY_FACTOR = 20*.05 # a pixels/second velocity of 400 gives a frame-duration of .05
VELOCITY_FACTOR = 30*.05 # a pixels/second velocity of 400 gives a frame-duration of .05
images_nrs = range(125, 162)
walk_images = []
for nr in images_nrs:
fn = f'walk-animation/OUT/{nr:03d}_frame{nr}.png'
pic: pyglet.image.ImageData = pyglet.image.load(fn)
walk_images.append(pic)
walk_animation = pyglet.image.animation.Animation.from_image_sequence(walk_images, 2, True)
angles = [0,15,30,45]
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)
walk_animation = walk_animations[0]
# 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)
@ -63,10 +69,11 @@ class Params:
# video_fps: float = 60
tracker_fps: float = 5.6
# iou = None
emitter_speed: float = 2 # objects per second
object_velocity: float = 200 # pixels/second
emitter_speed: float = 1.2 # objects per second
object_velocity: float = 100 # pixels/second
velocity_decay: float = 1 # make system a bit springy
iou_threshold: float = 0 # SORT Intersection over union
movement_angle: float = 0 # angle at which the blobs move (-45 - +45 degrees)
def add_listener(self, attr: str, callback: callable):
"""
@ -112,11 +119,13 @@ class DetectedObject:
self.w = 160 # width
self.h = 320 # height
self.l = -self.w # left
self.t = (self.canvas.height - self.h)/2 # top
self.t = (self.canvas.height - self.h)/2 - math.sin(self.canvas.params.movement_angle/360*math.pi*2) * self.canvas.width/2 # top
# self.shape = pyglet.shapes.Rectangle(self.l, self.t, self.w, self.h, color=(255, 22, 20), batch=self.canvas.batch_figures)
angle = min(angles, key=lambda x:abs(x-self.canvas.params.movement_angle))
self.shape = pyglet.sprite.Sprite(img=walk_animation,x=self.l, y=self.t, batch=self.canvas.batch_figures)
self.shape = pyglet.sprite.Sprite(img=walk_animations[angle],x=self.l, y=self.t, batch=self.canvas.batch_figures)
self.shape.scale_x = self.w/walk_animation_dim['x']
self.shape.scale_y = self.h/walk_animation_dim['y']
# rectangle.opacity = 128
@ -127,8 +136,10 @@ class DetectedObject:
"""
Update position
"""
self.l += dt * self.canvas.params.object_velocity
self.l += dt * self.canvas.params.object_velocity * math.cos(self.canvas.params.movement_angle/360*2 * math.pi)
self.t += dt * self.canvas.params.object_velocity * math.sin(self.canvas.params.movement_angle/360*2 * math.pi)
self.shape.x = self.l
self.shape.y = self.t
# TODO exponential decay with self.params.velocity_decay
class ObjectEmitter:
@ -149,7 +160,7 @@ class ObjectEmitter:
return [obj]
return []
class MissingDict(dict):
class MissingDict[T](dict):
"""
collections.defaultdict does not accept arguments, but this is what we want/need.
This implementation should do the trick
@ -158,7 +169,7 @@ class MissingDict(dict):
self.update(values)
self.factory = factory
def __missing__(self, key):
def __missing__(self, key) -> T:
self[key] = self.factory(key)
return self[key]
@ -194,7 +205,7 @@ class Canvas:
# Purple background color:
pyglet.gl.glClearColor(230/255,230/255,230/255,230/255)
self.draw_stats = True
self.fps_display = pyglet.window.FPSDisplay(window=self.window, color=(255,255,255,255))
self.fps_display = pyglet.window.FPSDisplay(window=self.window, color=(255,255,255,255), samples=100)
self.fps_display.label.x = self.window.width - 150
self.fps_display.label.y = self.window.height - 17
self.fps_display.label.bold = False
@ -214,7 +225,7 @@ class Canvas:
self.labels[field.name] = pyglet.text.Label(f"{field.name}: {field.default}", x=20, y=30 + 20*(i+1), color=(255,255,255,255), batch=self.batch_info)
# TODO: Add a number next to the box using pyglet.graphics.Group()
self.track_shapes = MissingDict(self.getTrackBboxShapes)
self.track_shapes: MissingDict[np.float64, tuple[pyglet.shapes.Box, pyglet.text.Label]] = MissingDict(self.getTrackBboxShapes)
self.tracker = Sort(max_age=5, min_hits=1, iou_threshold=0) #DeepSort(max_age=5)
@ -224,16 +235,17 @@ class Canvas:
self.params.add_listener('tracker_fps', self.on_tracker_fps_change)
self.params.add_listener('iou_threshold', self.on_iou_threshold_change)
self.params.add_listener('object_velocity', self.on_object_velocity_change)
self.params.add_listener('movement_angle', self.on_movement_angle_change)
# trigger first time around
self.on_object_velocity_change('object_velocity', None, self.params.object_velocity)
def getTrackBboxShapes(self, track_id):
def getTrackBboxShapes(self, track_id) -> tuple[pyglet.shapes.Box, pyglet.text.Label]:
color = colorset[int(track_id) % len(colorset)]
return [
return (
pyglet.shapes.Box(0,0,0,0,color=color,thickness=2, batch=self.batch_bounding_boxes),
pyglet.text.Label(f"{track_id:.0f}", x=0, y=0, anchor_y='top', color=color, batch=self.batch_info),
pyglet.text.Label(f"{track_id:.0f}", x=0, y=0, anchor_y='top', color=color, batch=self.batch_bounding_boxes),
# pyglet.shapes.Lab(0,0,0,0,color=(0,255,0),thickness=2, batch=self.batch_bounding_boxes)
]
)
def on_tracker_fps_change(self, field, old_value, new_value):
@ -250,14 +262,31 @@ class Canvas:
"""
duration = max(.001, VELOCITY_FACTOR / new_value)
logger.debug(f"set frame duration to {duration=} (for {new_value} p/s, factor: {VELOCITY_FACTOR})")
for frame in walk_animation.frames:
# a velocity of
frame.duration = duration
for anim in walk_animations.values():
for frame in anim.frames:
# a velocity of
frame.duration = duration
# for ii in self.interval_items:
# ii.interval = 1/new_value
# logger.debug(f"Set tracker interval to {ii.interval}")
def on_movement_angle_change(self, field, old_value, new_value):
"""
Param dataclass listener for changes to movement-angle
"""
angle = min(angles, key=lambda x:abs(x-new_value))
if hasattr(self, 'angle') and self.angle == angle:
return
self.angle = angle
for obj in self.objects:
idx = obj.shape._frame_index
obj.shape.image = walk_animations[self.angle]
obj.shape._frame_index = idx
def on_iou_threshold_change(self, field, old_value, new_value):
"""
Param dataclass listener for changes to iou_threshold
@ -274,6 +303,7 @@ class Canvas:
def on_draw(self):
self.window.clear()
self.batch_figures.draw()
self.batch_bounding_boxes.draw()
self.batch_info.draw()
@ -379,6 +409,7 @@ class Canvas:
self.prune()
self.lastSnapshot = now
# print(f"duration: {time.monotonic()-now)
return self.objects
@ -394,21 +425,26 @@ class Canvas:
for name, value in dataclasses.asdict(self.params).items():
self.labels[name].text = f"{name}: {value}"
# BIG impact
for track in self.tracks:
nr = track[4]
for shape in self.track_shapes[nr]:
if shape.x == 0 and shape.y == 0:
# set initial position
shape.x = track[0]
shape.y = track[1]
shape.width = track[2] - track[0]
shape.height = track[3] - track[1]
else:
# exponential decay
shape.x = exponentialDecay(shape.x, track[0], 12, dt)
shape.y = exponentialDecay(shape.y, track[1], 12, dt)
shape.width = exponentialDecay(shape.width, track[2] - track[0], 12, dt)
shape.height = exponentialDecay(shape.height, track[3] - track[1], 12, dt)
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)
# TODO: shape in DetectedObject

BIN
walk-animation/walking-body.blend (Stored with Git LFS)

Binary file not shown.