fix(demo): pace gait windows before buffering

Make the OpenGait-studio demo drop unpaced frames before they grow the silhouette window. Separate source-frame gap tracking from paced-frame stride tracking so runtime scheduling matches the documented demo-window-and-stride behavior.

Add regressions for paced window growth and schedule-frame stride semantics.
This commit is contained in:
2026-03-14 11:31:44 +08:00
parent ede9690318
commit d4e2a59ad2
4 changed files with 131 additions and 14 deletions
+25 -12
View File
@@ -67,7 +67,8 @@ class SilhouetteWindow:
stride: int
gap_threshold: int
_buffer: deque[Float[ndarray, "64 44"]]
_frame_indices: deque[int]
_source_frame_indices: deque[int]
_schedule_frame_indices: deque[int]
_track_id: int | None
_last_classified_frame: int
_frame_count: int
@@ -91,12 +92,20 @@ class SilhouetteWindow:
# Bounded storage via deque
self._buffer = deque(maxlen=window_size)
self._frame_indices = deque(maxlen=window_size)
self._source_frame_indices = deque(maxlen=window_size)
self._schedule_frame_indices = deque(maxlen=window_size)
self._track_id = None
self._last_classified_frame = -1
self._frame_count = 0
def push(self, sil: np.ndarray, frame_idx: int, track_id: int) -> None:
def push(
self,
sil: np.ndarray,
frame_idx: int,
track_id: int,
*,
schedule_frame_idx: int | None = None,
) -> None:
"""Push a new silhouette into the window.
Automatically resets buffer on track ID change or frame gap
@@ -112,8 +121,8 @@ class SilhouetteWindow:
self.reset()
# Check for frame gap
if self._frame_indices:
last_frame = self._frame_indices[-1]
if self._source_frame_indices:
last_frame = self._source_frame_indices[-1]
gap = frame_idx - last_frame
if gap > self.gap_threshold:
self.reset()
@@ -129,7 +138,10 @@ class SilhouetteWindow:
)
self._buffer.append(sil_array)
self._frame_indices.append(frame_idx)
self._source_frame_indices.append(frame_idx)
self._schedule_frame_indices.append(
frame_idx if schedule_frame_idx is None else schedule_frame_idx
)
self._frame_count += 1
def is_ready(self) -> bool:
@@ -152,7 +164,7 @@ class SilhouetteWindow:
if self._last_classified_frame < 0:
return True
current_frame = self._frame_indices[-1]
current_frame = self._schedule_frame_indices[-1]
frames_since = current_frame - self._last_classified_frame
return frames_since >= self.stride
@@ -185,15 +197,16 @@ class SilhouetteWindow:
def reset(self) -> None:
"""Reset the window, clearing all buffers and counters."""
self._buffer.clear()
self._frame_indices.clear()
self._source_frame_indices.clear()
self._schedule_frame_indices.clear()
self._track_id = None
self._last_classified_frame = -1
self._frame_count = 0
def mark_classified(self) -> None:
"""Mark current frame as classified, updating stride tracking."""
if self._frame_indices:
self._last_classified_frame = self._frame_indices[-1]
if self._schedule_frame_indices:
self._last_classified_frame = self._schedule_frame_indices[-1]
@property
def current_track_id(self) -> int | None:
@@ -212,9 +225,9 @@ class SilhouetteWindow:
@property
def window_start_frame(self) -> int:
if not self._frame_indices:
if not self._source_frame_indices:
raise ValueError("Window is empty")
return int(self._frame_indices[0])
return int(self._source_frame_indices[0])
@property
def buffered_silhouettes(self) -> Float[ndarray, "n 64 44"]: