| """ |
| Collect and visualize SV4D batch results. |
| |
| For each sample, concatenate the 4 novel-view videos into a 2x2 grid, |
| and save all grids to a single folder for easy browsing. |
| |
| Usage: |
| python scripts/sampling/collect_results.py |
| """ |
|
|
| import os |
| import argparse |
| import numpy as np |
| import imageio |
| from PIL import Image |
|
|
|
|
| INPUT_DIR = "./outputs/outputs_4d" |
| OUTPUT_DIR = "./outputs/gallery" |
|
|
|
|
| def make_grid(frames_list, nrow=2): |
| """ |
| frames_list: list of N lists, each containing T frames (H, W, 3). |
| Returns: list of T grid frames. |
| """ |
| n_views = len(frames_list) |
| n_frames = min(len(f) for f in frames_list) |
| h, w = frames_list[0][0].shape[:2] |
| ncol = (n_views + nrow - 1) // nrow |
|
|
| grids = [] |
| for t in range(n_frames): |
| canvas = np.full((h * nrow, w * ncol, 3), 255, dtype=np.uint8) |
| for idx in range(n_views): |
| r, c = divmod(idx, ncol) |
| canvas[r * h : (r + 1) * h, c * w : (c + 1) * w] = frames_list[idx][t] |
| grids.append(canvas) |
| return grids |
|
|
|
|
| def find_view_videos(directory): |
| """Find novel-view mp4s (_v0XX.mp4) in a directory, handling both SV4D and SV4D2.""" |
| view_files = [] |
| for f in sorted(os.listdir(directory)): |
| if not f.endswith(".mp4"): |
| continue |
| if "_v0" in f and "_v000" not in f: |
| view_files.append(os.path.join(directory, f)) |
| return view_files |
|
|
|
|
| def process_one(sample_dir, output_dir): |
| for sub in ["sv4d2", "sv4d", ""]: |
| search_dir = os.path.join(sample_dir, sub) if sub else sample_dir |
| if not os.path.isdir(search_dir): |
| continue |
| view_files = find_view_videos(search_dir) |
| if view_files: |
| break |
| else: |
| return False |
|
|
| if not view_files: |
| return False |
|
|
| all_views = [] |
| for vf in view_files: |
| frames = [np.array(f) for f in imageio.mimread(vf)] |
| all_views.append(frames) |
|
|
| grid_frames = make_grid(all_views, nrow=2) |
|
|
| name = os.path.basename(sample_dir) |
| out_path = os.path.join(output_dir, f"{name}.mp4") |
| imageio.mimwrite(out_path, grid_frames, fps=4) |
| return True |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--input_dir", default=INPUT_DIR) |
| parser.add_argument("--output_dir", default=OUTPUT_DIR) |
| args = parser.parse_args() |
|
|
| os.makedirs(args.output_dir, exist_ok=True) |
|
|
| samples = sorted([ |
| os.path.join(args.input_dir, d) |
| for d in os.listdir(args.input_dir) |
| if os.path.isdir(os.path.join(args.input_dir, d)) |
| ]) |
|
|
| done = 0 |
| for s in samples: |
| name = os.path.basename(s) |
| ok = process_one(s, args.output_dir) |
| if ok: |
| done += 1 |
| print(f"[{done}/{len(samples)}] {name}") |
| else: |
| print(f"[skip] {name} (no view videos found)") |
|
|
| print(f"\nDone! {done} grid videos saved to {args.output_dir}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|