multimodalart HF Staff commited on
Commit
bfecffd
·
verified ·
1 Parent(s): e9d7076

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -0
app.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import subprocess
4
+ import argparse
5
+ from pathlib import Path
6
+ import torch
7
+ import datetime
8
+ import numpy as np
9
+ from PIL import Image
10
+ import imageio
11
+ import spaces
12
+
13
+ # --- Part 1: Auto-Setup (Clone Repo & Download Weights) ---
14
+
15
+ REPO_URL = "https://github.com/Tencent-Hunyuan/HunyuanVideo-1.5.git"
16
+ REPO_DIR = "HunyuanVideo-1.5"
17
+ MODEL_DIR = "ckpts"
18
+ HF_REPO_ID = "tencent/HunyuanVideo"
19
+
20
+ # Configuration
21
+ TRANSFORMER_VERSION = "480p_i2v_distilled"
22
+ DTYPE = torch.bfloat16
23
+ # Set to False if you have >40GB VRAM and want everything on GPU constantly.
24
+ # Set to True (Default) to allow running on 16GB-24GB cards via CPU offloading.
25
+ ENABLE_OFFLOADING = True
26
+
27
+ def setup_environment():
28
+ """Clones the repo and downloads weights if they don't exist."""
29
+ print("=" * 50)
30
+ print("Checking Environment & Dependencies...")
31
+
32
+ # 1. Clone Repository
33
+ if not os.path.exists(REPO_DIR):
34
+ print(f"Cloning repository from {REPO_URL}...")
35
+ subprocess.run(["git", "clone", REPO_URL], check=True)
36
+ else:
37
+ print(f"Repository {REPO_DIR} exists.")
38
+
39
+ # 2. Add Repo to Python Path
40
+ repo_path = os.path.abspath(REPO_DIR)
41
+ if repo_path not in sys.path:
42
+ sys.path.insert(0, repo_path)
43
+
44
+ # 3. Download Weights
45
+ if not os.path.exists(MODEL_DIR) or not os.listdir(MODEL_DIR):
46
+ print(f"Downloading weights from {HF_REPO_ID} to {MODEL_DIR}...")
47
+ try:
48
+ from huggingface_hub import snapshot_download
49
+ allow_patterns = [
50
+ f"transformer/{TRANSFORMER_VERSION}/*",
51
+ "vae/*",
52
+ "text_encoder/*",
53
+ "vision_encoder/*",
54
+ "scheduler/*",
55
+ "tokenizer/*"
56
+ ]
57
+ snapshot_download(repo_id=HF_REPO_ID, local_dir=MODEL_DIR, allow_patterns=allow_patterns)
58
+ print("Download complete.")
59
+ except Exception as e:
60
+ print(f"Error downloading weights: {e}")
61
+ sys.exit(1)
62
+ print("Environment Ready.")
63
+ print("=" * 50)
64
+
65
+ # Run setup immediately
66
+ setup_environment()
67
+
68
+ # --- Part 2: Imports from Cloned Repo ---
69
+
70
+ # Set Env Vars for HyVideo
71
+ if 'PYTORCH_CUDA_ALLOC_CONF' not in os.environ:
72
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'
73
+ os.environ['RANK'] = '0'
74
+ os.environ['WORLD_SIZE'] = '1'
75
+
76
+ try:
77
+ from hyvideo.pipelines.hunyuan_video_pipeline import HunyuanVideo_1_5_Pipeline
78
+ from hyvideo.commons.parallel_states import initialize_parallel_state
79
+ from hyvideo.commons.infer_state import initialize_infer_state
80
+ except ImportError as e:
81
+ print(f"CRITICAL ERROR: Could not import hyvideo modules. {e}")
82
+ sys.exit(1)
83
+
84
+ import gradio as gr
85
+
86
+ # --- Part 3: Model Initialization (Pre-Load) ---
87
+
88
+ # Initialize Distributed/Infer States
89
+ parallel_dims = initialize_parallel_state(sp=1)
90
+ if torch.cuda.is_available():
91
+ torch.cuda.set_device(0)
92
+
93
+ class ArgsNamespace:
94
+ def __init__(self):
95
+ self.use_sageattn = False
96
+ self.sage_blocks_range = "0-53"
97
+ self.enable_torch_compile = False
98
+
99
+ initialize_infer_state(ArgsNamespace())
100
+
101
+ # Global Pipeline Variable
102
+ pipe = None
103
+
104
+ def pre_load_model():
105
+ """Loads the model into memory/GPU before UI launch."""
106
+ global pipe
107
+ print(f"⏳ Initializing Pipeline ({TRANSFORMER_VERSION})... this may take a moment...")
108
+
109
+ try:
110
+ pipe = HunyuanVideo_1_5_Pipeline.create_pipeline(
111
+ pretrained_model_name_or_path=MODEL_DIR,
112
+ transformer_version=TRANSFORMER_VERSION,
113
+ enable_offloading=ENABLE_OFFLOADING,
114
+ enable_group_offloading=ENABLE_OFFLOADING,
115
+ transformer_dtype=DTYPE,
116
+ )
117
+ print("✅ Model loaded successfully!")
118
+ if not ENABLE_OFFLOADING:
119
+ print(" Model is fully resident on GPU.")
120
+ else:
121
+ print(" Model loaded with CPU Offloading enabled (optimizes VRAM usage).")
122
+ except Exception as e:
123
+ print(f"❌ Failed to load model: {e}")
124
+ sys.exit(1)
125
+
126
+ def save_video_tensor(video_tensor, path, fps=24):
127
+ if isinstance(video_tensor, list): video_tensor = video_tensor[0]
128
+ if video_tensor.ndim == 5: video_tensor = video_tensor[0]
129
+ vid = (video_tensor * 255).clamp(0, 255).to(torch.uint8)
130
+ vid = vid.permute(1, 2, 3, 0).cpu().numpy()
131
+ imageio.mimwrite(path, vid, fps=fps)
132
+
133
+ @spaces.GPU(duration=120)
134
+ def generate(input_image, prompt, length, steps, shift, seed, guidance):
135
+ if pipe is None:
136
+ raise gr.Error("Pipeline not initialized!")
137
+
138
+ if input_image is None:
139
+ raise gr.Error("Reference image required.")
140
+
141
+ if isinstance(input_image, np.ndarray):
142
+ input_image = Image.fromarray(input_image).convert("RGB")
143
+
144
+ if seed == -1: seed = torch.randint(0, 1000000, (1,)).item()
145
+ generator = torch.Generator(device="cpu").manual_seed(int(seed))
146
+
147
+ print(f"Generating: {prompt} | Seed: {seed}")
148
+
149
+ try:
150
+ output = pipe(
151
+ prompt=prompt,
152
+ height=480, width=854, aspect_ratio="16:9",
153
+ video_length=int(length),
154
+ num_inference_steps=int(steps),
155
+ guidance_scale=float(guidance),
156
+ flow_shift=float(shift),
157
+ reference_image=input_image,
158
+ seed=int(seed),
159
+ generator=generator,
160
+ output_type="pt",
161
+ enable_sr=False,
162
+ return_dict=True
163
+ )
164
+ except Exception as e:
165
+ raise gr.Error(f"Inference Failed: {e}")
166
+
167
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
168
+ os.makedirs("outputs", exist_ok=True)
169
+ output_path = f"outputs/gen_{timestamp}.mp4"
170
+ save_video_tensor(output.videos, output_path)
171
+
172
+ return output_path
173
+
174
+ # --- Part 4: UI Definition & Launch ---
175
+
176
+ def create_ui():
177
+ with gr.Blocks(title="HunyuanVideo 1.5 I2V") as demo:
178
+ gr.Markdown(f"### 🎬 HunyuanVideo 1.5 I2V ({TRANSFORMER_VERSION})")
179
+ gr.Markdown("Model is pre-loaded. Ready to generate.")
180
+
181
+ with gr.Row():
182
+ with gr.Column():
183
+ img = gr.Image(label="Reference", type="pil", height=250)
184
+ prompt = gr.Textbox(label="Prompt", placeholder="Describe motion...", lines=2)
185
+ with gr.Row():
186
+ steps = gr.Slider(2, 20, value=6, step=1, label="Steps")
187
+ guidance = gr.Slider(1.0, 5.0, value=1.0, step=0.1, label="Guidance")
188
+ with gr.Row():
189
+ shift = gr.Slider(1.0, 20.0, value=5.0, step=0.5, label="Shift")
190
+ length = gr.Slider(1, 129, value=61, step=4, label="Length")
191
+ seed = gr.Number(value=-1, label="Seed", precision=0)
192
+ btn = gr.Button("Generate", variant="primary")
193
+
194
+ with gr.Column():
195
+ out = gr.Video(label="Result", autoplay=True)
196
+
197
+ btn.click(generate, inputs=[img, prompt, length, steps, shift, seed, guidance], outputs=[out])
198
+ return demo
199
+
200
+ if __name__ == "__main__":
201
+ # 1. Execute the pre-load BEFORE the UI launches
202
+ pre_load_model()
203
+
204
+ # 2. Launch UI
205
+ ui = create_ui()
206
+ ui.queue().launch(server_name="0.0.0.0", share=True)