#!/usr/bin/env python """ orbit_qwen_poc.py — near-real turntable test via Qwen-Image-Edit. python orbit_qwen_poc.py --input front.png --output ./out \ --views 12 --mode turntable --interp 4 --seed 42 Generates one re-rendered view per yaw angle (anchored to the input, fixed seed), flow-interpolates for smoothness, and stitches a looping MP4. """ import argparse import os import sys import time _HERE = os.path.dirname(os.path.abspath(__file__)) if _HERE not in sys.path: sys.path.insert(0, _HERE) from orbit_qwen import run_qwen_orbit def main(): ap = argparse.ArgumentParser(description="Qwen turntable (near-real subject turning).") ap.add_argument("--input", "-i", required=True, help="Front-facing source image") ap.add_argument("--output", "-o", default="./out_turntable", help="Output directory") ap.add_argument("--views", "-v", type=int, default=24, help="Qwen keyframes (yaw steps)") ap.add_argument("--mode", "-m", choices=["turntable", "swing"], default="turntable", help="turntable=full 360 loop, swing=front-facing arc only") ap.add_argument("--sweep", type=float, default=180.0, help="swing arc width in degrees") ap.add_argument("--anchor", choices=["original", "chain"], default="original", help="original=each view from source (stable), chain=from previous (smoother)") ap.add_argument("--interp", type=int, default=1, help="interpolated frames per keyframe gap (1=none; >1 ghosts, not advised)") ap.add_argument("--no-smooth", action="store_true", help="crossfade instead of optical-flow morph") ap.add_argument("--fps", type=int, default=12) ap.add_argument("--seed", type=int, default=42) ap.add_argument("--steps", type=int, default=8, help="Qwen sampler steps (4 fast, 8 nicer)") ap.add_argument("--max-area", type=int, default=0, help="output pixel budget (0=API default)") args = ap.parse_args() if not os.path.exists(args.input): print(f"[error] input not found: {args.input}", file=sys.stderr) sys.exit(1) def prog(i, n, deg): print(f" [{i+1}/{n}] rendering {int(deg):3d}°…", flush=True) print(f"[turntable] input : {args.input}") print(f"[turntable] mode={args.mode} views={args.views} anchor={args.anchor} " f"interp×{args.interp} seed={args.seed} steps={args.steps}") t0 = time.perf_counter() res = run_qwen_orbit( image_path=args.input, output_dir=args.output, n_views=args.views, seed=args.seed, mode=args.mode, sweep_deg=args.sweep, anchor=args.anchor, interp_factor=args.interp, smooth=not args.no_smooth, fps=args.fps, max_area=args.max_area, steps=args.steps, on_progress=prog, ) dt = time.perf_counter() - t0 print(f"\n[turntable] done in {dt:.1f}s " f"({dt/max(1,res['n_views']):.1f}s/view)") print(f" keyframes : {res['n_views']} → {res['views_dir']}") print(f" frames : {res['n_frames']} (after interpolation)") print(f" video : {res['video_path']}") if __name__ == "__main__": main()