loop_windows moves beside loop_window and takes the model; a test pins it on a real take with the two skeletons
The worker's window path loaded the 8B model at import, so no test reached it and swapped atan2 arguments or the 30-joint skeleton went unnoticed. loop_windows(output, model) needs only model.output_skeleton and model.fps. The test uses a committed 240-frame somaskel77 take (fixtures/take.npz, joints from a live Vili BVH by forward kinematics) and stand-ins that carry the 77- and 30-joint skeletons' names and parents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d275f00aeb
commit
0426a7ce3a
4 changed files with 77 additions and 20 deletions
|
|
@ -3,9 +3,13 @@
|
|||
A seam is the jump from frame b back to frame a. Its cost is the pose difference plus 0.1 s times the velocity
|
||||
difference (Kovar, Gleicher and Pighin 2002, motion-graph transitions), both as RMS over joints, with each frame
|
||||
taken relative to its own Hips and turned to face a fixed way, so where the take wanders and which way it faces
|
||||
do not count. Numpy only.
|
||||
do not count. loop_window is numpy only; loop_windows needs torch and kimodo's heading, and no model weights.
|
||||
"""
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from kimodo.constraints import compute_global_heading
|
||||
|
||||
VELOCITY_WEIGHT_SECONDS = 0.1
|
||||
|
||||
|
|
@ -39,3 +43,18 @@ def loop_window(joints, heading, fps, min_frames, hips=0):
|
|||
best = np.flatnonzero(cost == cost.min())
|
||||
pick = best[np.lexsort((firsts[best], -(lasts[best] - firsts[best])))[0]]
|
||||
return int(firsts[pick]), int(lasts[pick]), float(pose_cost[pick]), float(velocity_cost[pick])
|
||||
|
||||
|
||||
def loop_windows(output, model):
|
||||
"""One loop window per sample of a Kimodo output, as the worker answers them: {first, last, poseCost, velocityCost},
|
||||
frame indices into the take, last inclusive. The joints are posed_joints, always in model.output_skeleton (the
|
||||
somaskel77, not model.skeleton), and the heading is Kimodo's own from those joints."""
|
||||
skeleton = model.output_skeleton
|
||||
hips = skeleton.bone_index["Hips"]
|
||||
windows = []
|
||||
for joints in output["posed_joints"]:
|
||||
heading = compute_global_heading(torch.from_numpy(joints[None]), skeleton)[0]
|
||||
angle = torch.atan2(heading[:, 1], heading[:, 0]).numpy()
|
||||
first, last, pose_cost, velocity_cost = loop_window(joints, angle, model.fps, math.ceil(len(joints) / 2), hips)
|
||||
windows.append({"first": first, "last": last, "poseCost": pose_cost, "velocityCost": velocity_cost})
|
||||
return windows
|
||||
|
|
|
|||
Loading…
Reference in a new issue