Files
qwen-image/tour-comfy/orbit_qwen_poc.py
2026-06-27 00:45:45 +02:00

72 lines
3.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()