Files
hao-ai-lab-FastVideo/examples/inference/basic/basic_gamecraft.py
T

253 lines
7.6 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""
Basic inference script for HunyuanGameCraft video generation.
HunyuanGameCraft generates game-like videos with camera/action control.
It takes an optional image input and generates video with camera motion
based on simple action commands (forward, left, right, backward, rotations).
Usage:
# Text-to-video with forward motion
python basic_gamecraft.py --prompt "A medieval village with cobblestone streets" --action forward
# Image-to-video with right motion
python basic_gamecraft.py --image path/to/image.png --prompt "A forest path" --action right
# Custom action speed
python basic_gamecraft.py --prompt "A temple corridor" --action forward --action-speed 0.3
Available actions:
- forward (w): Move camera forward
- backward (s): Move camera backward
- left (a): Move camera left (strafe)
- right (d): Move camera right (strafe)
- left_rot: Rotate camera left (pan)
- right_rot: Rotate camera right (pan)
- up_rot: Rotate camera up (tilt)
- down_rot: Rotate camera down (tilt)
"""
import argparse
import torch
from fastvideo import VideoGenerator
from fastvideo.models.camera import create_camera_trajectory
# Default prompts for demo
DEFAULT_PROMPTS = {
"village": "A charming medieval village with cobblestone streets, thatched-roof houses, and vibrant flower gardens under a bright blue sky.",
"temple": "A majestic ancient temple stands under a clear blue sky, its grandeur highlighted by towering Doric columns and intricate architectural details.",
"forest": "A lush green forest with tall trees, dappled sunlight filtering through the leaves, and a winding dirt path.",
"beach": "A tropical beach with crystal clear turquoise water, white sand, and palm trees swaying in the breeze.",
}
# Action mapping (human-readable -> official action ID)
ACTION_MAP = {
"forward": "w",
"backward": "s",
"left": "a",
"right": "d",
"left_rot": "left_rot",
"right_rot": "right_rot",
"up_rot": "up_rot",
"down_rot": "down_rot",
}
OUTPUT_PATH = "video_samples_gamecraft"
def main():
parser = argparse.ArgumentParser(
description="HunyuanGameCraft video generation with camera/action control"
)
parser.add_argument(
"--model-path",
type=str,
default="tencent/HunyuanGameCraft", # HuggingFace model path
help="Path to GameCraft model (HuggingFace path or local directory)",
)
parser.add_argument(
"--prompt",
type=str,
default=DEFAULT_PROMPTS["village"],
help="Text prompt for video generation",
)
parser.add_argument(
"--image",
type=str,
default=None,
help="Optional input image path for image-to-video generation",
)
parser.add_argument(
"--action",
type=str,
default="forward",
choices=list(ACTION_MAP.keys()),
help="Camera motion action",
)
parser.add_argument(
"--action-speed",
type=float,
default=0.2,
help="Speed of camera motion (default: 0.2)",
)
parser.add_argument(
"--num-frames",
type=int,
default=33,
help="Number of frames (33 or 65 recommended)",
)
parser.add_argument(
"--height",
type=int,
default=704,
help="Video height in pixels",
)
parser.add_argument(
"--width",
type=int,
default=1280,
help="Video width in pixels",
)
parser.add_argument(
"--num-inference-steps",
type=int,
default=50,
help="Number of denoising steps",
)
parser.add_argument(
"--seed",
type=int,
default=42,
help="Random seed for reproducibility",
)
parser.add_argument(
"--num-gpus",
type=int,
default=1,
help="Number of GPUs to use for inference",
)
parser.add_argument(
"--output-path",
type=str,
default=OUTPUT_PATH,
help="Output directory for generated videos",
)
parser.add_argument(
"--fps",
type=int,
default=24,
help="Frames per second for output video",
)
parser.add_argument(
"--cpu-offload",
action="store_true",
help="Enable CPU offloading to save GPU memory",
)
parser.add_argument(
"--negative-prompt",
type=str,
default="",
help="Negative prompt for classifier-free guidance (e.g., 'blurry, low quality, distorted')",
)
args = parser.parse_args()
print("=" * 60)
print("HunyuanGameCraft Video Generation")
print("=" * 60)
print(f"Model: {args.model_path}")
print(f"Prompt: {args.prompt[:80]}...")
print(f"Action: {args.action} (speed: {args.action_speed})")
print(f"Resolution: {args.height}x{args.width}")
print(f"Frames: {args.num_frames}")
print(f"Steps: {args.num_inference_steps}")
print(f"Seed: {args.seed}")
print(f"Negative prompt: {args.negative_prompt or '(empty)'}")
print(f"Output: {args.output_path}")
print("=" * 60)
# Initialize generator
print("\nInitializing VideoGenerator for GameCraft...")
generator = VideoGenerator.from_pretrained(
args.model_path,
num_gpus=args.num_gpus,
use_fsdp_inference=True,
dit_cpu_offload=args.cpu_offload,
vae_cpu_offload=args.cpu_offload,
text_encoder_cpu_offload=True,
pin_cpu_memory=True,
)
# Create camera trajectory
print(f"\nGenerating camera trajectory for '{args.action}' motion...")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
camera_states = create_camera_trajectory(
action=args.action,
height=args.height,
width=args.width,
num_frames=args.num_frames,
action_speed=args.action_speed,
device=device,
dtype=torch.bfloat16,
)
print(f"Camera states shape: {camera_states.shape}")
# Generate video
print("\nGenerating video...")
result = generator.generate_video(
prompt=args.prompt,
negative_prompt=args.negative_prompt,
image_path=args.image,
camera_states=camera_states,
output_path=args.output_path,
save_video=True,
height=args.height,
width=args.width,
num_frames=args.num_frames,
num_inference_steps=args.num_inference_steps,
fps=args.fps,
seed=args.seed,
guidance_scale=6.0, # Official GameCraft uses CFG with guidance_scale=6.0
)
print(f"\nVideo saved to: {args.output_path}")
print("Done!")
# Optionally generate another video with different action
print("\n" + "=" * 60)
print("Generating second video with 'right' motion...")
print("=" * 60)
camera_states_right = create_camera_trajectory(
action="right",
height=args.height,
width=args.width,
num_frames=args.num_frames,
action_speed=args.action_speed,
device=device,
dtype=torch.bfloat16,
)
result2 = generator.generate_video(
prompt=args.prompt,
negative_prompt=args.negative_prompt,
image_path=args.image,
camera_states=camera_states_right,
output_path=args.output_path,
save_video=True,
height=args.height,
width=args.width,
num_frames=args.num_frames,
num_inference_steps=args.num_inference_steps,
fps=args.fps,
seed=args.seed + 1, # Different seed for variety
guidance_scale=6.0,
)
print(f"\nSecond video saved to: {args.output_path}")
print("All done!")
if __name__ == "__main__":
main()