Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -266,8 +266,8 @@ def resize_image_landscape(image: Image.Image) -> Image.Image:
|
|
| 266 |
return image.resize((LANDSCAPE_WIDTH, LANDSCAPE_HEIGHT), Image.LANCZOS)
|
| 267 |
|
| 268 |
def get_duration(input_image, prompt, steps, negative_prompt, duration_seconds, guidance_scale, guidance_scale_2, seed, randomize_seed):
|
| 269 |
-
#
|
| 270 |
-
return int(steps) *
|
| 271 |
|
| 272 |
@spaces.GPU(duration=get_duration)
|
| 273 |
def generate_video(
|
|
@@ -275,7 +275,7 @@ def generate_video(
|
|
| 275 |
prompt,
|
| 276 |
steps=4,
|
| 277 |
negative_prompt=default_negative_prompt,
|
| 278 |
-
duration_seconds=
|
| 279 |
guidance_scale=1,
|
| 280 |
guidance_scale_2=1,
|
| 281 |
seed=42,
|
|
@@ -286,61 +286,76 @@ def generate_video(
|
|
| 286 |
if input_image is None:
|
| 287 |
raise gr.Error("Please generate or upload an image first.")
|
| 288 |
|
| 289 |
-
# Initialize pipeline if needed
|
| 290 |
-
initialize_video_pipeline()
|
| 291 |
-
|
| 292 |
-
if video_pipe is None:
|
| 293 |
-
raise gr.Error("Video pipeline not initialized. Please check GPU availability.")
|
| 294 |
-
|
| 295 |
try:
|
| 296 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
num_frames = int(round(duration_seconds * FIXED_FPS))
|
| 298 |
-
num_frames = np.clip(num_frames,
|
| 299 |
-
# Round to nearest number divisible by 4
|
| 300 |
num_frames = ((num_frames - 1) // 4) * 4 + 1
|
| 301 |
|
| 302 |
current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
|
| 303 |
-
resized_image = resize_image_for_video(input_image)
|
| 304 |
|
| 305 |
-
#
|
| 306 |
-
|
| 307 |
-
gc.collect()
|
| 308 |
|
| 309 |
-
# Generate
|
| 310 |
with torch.inference_mode():
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
|
|
|
| 323 |
|
| 324 |
# Clear cache after generation
|
| 325 |
torch.cuda.empty_cache()
|
| 326 |
gc.collect()
|
| 327 |
|
|
|
|
| 328 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 329 |
video_path = tmpfile.name
|
| 330 |
|
| 331 |
export_to_video(output_frames_list, video_path, fps=FIXED_FPS)
|
| 332 |
|
| 333 |
-
return video_path, current_seed, "🎬 Video generated successfully!"
|
| 334 |
|
| 335 |
except RuntimeError as e:
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
raise gr.Error("GPU memory
|
| 340 |
else:
|
| 341 |
-
raise gr.Error(f"
|
| 342 |
except Exception as e:
|
| 343 |
-
raise gr.Error(f"
|
| 344 |
|
| 345 |
# ===========================
|
| 346 |
# Enhanced CSS
|
|
@@ -541,12 +556,12 @@ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
|
| 541 |
lines=3
|
| 542 |
)
|
| 543 |
duration_input = gr.Slider(
|
| 544 |
-
minimum=
|
| 545 |
-
maximum=
|
| 546 |
step=0.1,
|
| 547 |
-
value=
|
| 548 |
label="Duration (seconds)",
|
| 549 |
-
info=
|
| 550 |
)
|
| 551 |
|
| 552 |
with gr.Accordion("Advanced Settings", open=False):
|
|
@@ -568,10 +583,10 @@ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
|
| 568 |
)
|
| 569 |
steps_slider = gr.Slider(
|
| 570 |
minimum=1,
|
| 571 |
-
maximum=
|
| 572 |
step=1,
|
| 573 |
-
value=
|
| 574 |
-
label="Inference Steps"
|
| 575 |
)
|
| 576 |
guidance_1 = gr.Slider(
|
| 577 |
minimum=0.0,
|
|
|
|
| 266 |
return image.resize((LANDSCAPE_WIDTH, LANDSCAPE_HEIGHT), Image.LANCZOS)
|
| 267 |
|
| 268 |
def get_duration(input_image, prompt, steps, negative_prompt, duration_seconds, guidance_scale, guidance_scale_2, seed, randomize_seed):
|
| 269 |
+
# Shorter duration for stability
|
| 270 |
+
return min(60, int(steps) * 10)
|
| 271 |
|
| 272 |
@spaces.GPU(duration=get_duration)
|
| 273 |
def generate_video(
|
|
|
|
| 275 |
prompt,
|
| 276 |
steps=4,
|
| 277 |
negative_prompt=default_negative_prompt,
|
| 278 |
+
duration_seconds=2.0, # Reduced default
|
| 279 |
guidance_scale=1,
|
| 280 |
guidance_scale_2=1,
|
| 281 |
seed=42,
|
|
|
|
| 286 |
if input_image is None:
|
| 287 |
raise gr.Error("Please generate or upload an image first.")
|
| 288 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
try:
|
| 290 |
+
# Initialize pipeline if needed (simplified)
|
| 291 |
+
global video_pipe
|
| 292 |
+
if video_pipe is None:
|
| 293 |
+
print("Initializing video pipeline...")
|
| 294 |
+
video_pipe = WanImageToVideoPipeline.from_pretrained(
|
| 295 |
+
VIDEO_MODEL_ID,
|
| 296 |
+
torch_dtype=torch.bfloat16,
|
| 297 |
+
variant="fp16",
|
| 298 |
+
use_safetensors=True
|
| 299 |
+
).to('cuda')
|
| 300 |
+
|
| 301 |
+
# Load Lightning LoRA for faster generation
|
| 302 |
+
try:
|
| 303 |
+
video_pipe.load_lora_weights("Kijai/WanVideo_comfy", weight_name="Wan22-Lightning-4-cfg1_bf16_v0.9.safetensors")
|
| 304 |
+
video_pipe.fuse_lora(lora_scale=1.0)
|
| 305 |
+
except:
|
| 306 |
+
pass
|
| 307 |
+
|
| 308 |
+
# Clear cache before generation
|
| 309 |
+
torch.cuda.empty_cache()
|
| 310 |
+
gc.collect()
|
| 311 |
+
|
| 312 |
+
# Ensure frames are divisible by 4 and limit to reasonable range
|
| 313 |
num_frames = int(round(duration_seconds * FIXED_FPS))
|
| 314 |
+
num_frames = np.clip(num_frames, 9, 33) # Limit to 0.5-2 seconds
|
|
|
|
| 315 |
num_frames = ((num_frames - 1) // 4) * 4 + 1
|
| 316 |
|
| 317 |
current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
|
|
|
|
| 318 |
|
| 319 |
+
# Resize image
|
| 320 |
+
resized_image = resize_image_for_video(input_image)
|
|
|
|
| 321 |
|
| 322 |
+
# Generate with reduced settings
|
| 323 |
with torch.inference_mode():
|
| 324 |
+
with torch.autocast('cuda', dtype=torch.bfloat16):
|
| 325 |
+
output_frames_list = video_pipe(
|
| 326 |
+
image=resized_image,
|
| 327 |
+
prompt=prompt,
|
| 328 |
+
negative_prompt=negative_prompt,
|
| 329 |
+
height=resized_image.height,
|
| 330 |
+
width=resized_image.width,
|
| 331 |
+
num_frames=num_frames,
|
| 332 |
+
guidance_scale=float(guidance_scale),
|
| 333 |
+
guidance_scale_2=float(guidance_scale_2),
|
| 334 |
+
num_inference_steps=int(steps),
|
| 335 |
+
generator=torch.Generator(device="cuda").manual_seed(current_seed),
|
| 336 |
+
).frames[0]
|
| 337 |
|
| 338 |
# Clear cache after generation
|
| 339 |
torch.cuda.empty_cache()
|
| 340 |
gc.collect()
|
| 341 |
|
| 342 |
+
# Save video
|
| 343 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 344 |
video_path = tmpfile.name
|
| 345 |
|
| 346 |
export_to_video(output_frames_list, video_path, fps=FIXED_FPS)
|
| 347 |
|
| 348 |
+
return video_path, current_seed, f"🎬 Video generated successfully! ({num_frames} frames)"
|
| 349 |
|
| 350 |
except RuntimeError as e:
|
| 351 |
+
torch.cuda.empty_cache()
|
| 352 |
+
gc.collect()
|
| 353 |
+
if "out of memory" in str(e).lower():
|
| 354 |
+
raise gr.Error("GPU memory exceeded. Try reducing duration to 1-2 seconds and steps to 4.")
|
| 355 |
else:
|
| 356 |
+
raise gr.Error(f"GPU error: {str(e)[:100]}")
|
| 357 |
except Exception as e:
|
| 358 |
+
raise gr.Error(f"Error: {str(e)[:200]}")
|
| 359 |
|
| 360 |
# ===========================
|
| 361 |
# Enhanced CSS
|
|
|
|
| 556 |
lines=3
|
| 557 |
)
|
| 558 |
duration_input = gr.Slider(
|
| 559 |
+
minimum=0.5,
|
| 560 |
+
maximum=2.0,
|
| 561 |
step=0.1,
|
| 562 |
+
value=1.5,
|
| 563 |
label="Duration (seconds)",
|
| 564 |
+
info="Shorter videos use less memory"
|
| 565 |
)
|
| 566 |
|
| 567 |
with gr.Accordion("Advanced Settings", open=False):
|
|
|
|
| 583 |
)
|
| 584 |
steps_slider = gr.Slider(
|
| 585 |
minimum=1,
|
| 586 |
+
maximum=8,
|
| 587 |
step=1,
|
| 588 |
+
value=4,
|
| 589 |
+
label="Inference Steps (4 recommended)"
|
| 590 |
)
|
| 591 |
guidance_1 = gr.Slider(
|
| 592 |
minimum=0.0,
|