| """Sliding-window decode helpers used by the Gradio demo.""" |
|
|
| import torch |
| from torch_geometric.data import Batch |
|
|
|
|
| def decode_to_hatD_with_sliding_window( |
| model, |
| latent_codes, |
| src_graphs_list, |
| total_frames, |
| window_size, |
| overlap, |
| device, |
| model_type, |
| ): |
| """Decode a long latent/code sequence in overlapping windows. |
| |
| Overlapping regions keep the earlier window prediction and drop the later |
| window overlap. This matches the preprocessing convention used by the demo |
| checkpoints. |
| """ |
| if model_type not in {"vae", "rvq"}: |
| raise ValueError(f"model_type must be 'vae' or 'rvq', got: {model_type}") |
|
|
| stride = window_size - overlap |
| if stride <= 0: |
| raise ValueError("window_size must be greater than overlap") |
|
|
| if len(src_graphs_list) != total_frames: |
| raise ValueError( |
| f"src_graphs_list length ({len(src_graphs_list)}) != total_frames ({total_frames})" |
| ) |
| if latent_codes.shape[0] != total_frames: |
| raise ValueError( |
| f"latent_codes length ({latent_codes.shape[0]}) != total_frames ({total_frames})" |
| ) |
|
|
| if total_frames <= window_size: |
| num_windows = 1 |
| else: |
| num_windows = (total_frames - window_size + stride - 1) // stride + 1 |
|
|
| num_nodes_per_frame = src_graphs_list[0].skel_x.shape[0] |
| hatD_parts = [] |
|
|
| for window_idx in range(num_windows): |
| start_frame = window_idx * stride |
| if start_frame >= total_frames: |
| break |
|
|
| end_frame = min(start_frame + window_size, total_frames) |
| window_frames = end_frame - start_frame |
| latent_window = latent_codes[start_frame:end_frame].to(device) |
|
|
| src_window_graphs = src_graphs_list[start_frame:end_frame] |
| src_window = Batch.from_data_list(src_window_graphs).to(device) |
|
|
| with torch.no_grad(): |
| if model_type == "vae": |
| hatD_win = model.decode(latent_window, src_window, window_frames) |
| else: |
| hatD_win, _ = model.decode_from_codes(latent_window, src_window, window_frames) |
|
|
| hatD_dim = hatD_win.shape[1] |
| hatD_win_reshaped = hatD_win.view(window_frames, num_nodes_per_frame, hatD_dim) |
|
|
| if window_idx == 0: |
| keep_start_idx = 0 |
| else: |
| keep_start_idx = overlap |
| hatD_parts.append(hatD_win_reshaped[keep_start_idx:window_frames].cpu()) |
|
|
| del src_window, hatD_win, hatD_win_reshaped |
|
|
| hatD_full_3d = torch.cat(hatD_parts, dim=0) |
| actual_frames = hatD_full_3d.shape[0] |
| hatD_full = hatD_full_3d.view(actual_frames * num_nodes_per_frame, -1).to(device) |
| src_batch_full = Batch.from_data_list(src_graphs_list).to(device) |
|
|
| return hatD_full, src_batch_full, actual_frames, num_nodes_per_frame |
|
|