Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -124,30 +124,46 @@ class ModelManager:
|
|
| 124 |
logger.info("Loading model with memory optimizations...")
|
| 125 |
clear_gpu_memory()
|
| 126 |
|
| 127 |
-
# ๋ชจ๋ธ ์ปดํฌ๋ํธ ๋ก๋ (๋ฉ๋ชจ๋ฆฌ ํจ์จ์ )
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
image_encoder = CLIPVisionModel.from_pretrained(
|
| 130 |
config.model_id,
|
| 131 |
subfolder="image_encoder",
|
| 132 |
-
torch_dtype=torch.
|
| 133 |
low_cpu_mem_usage=True
|
| 134 |
)
|
| 135 |
|
| 136 |
vae = AutoencoderKLWan.from_pretrained(
|
| 137 |
config.model_id,
|
| 138 |
subfolder="vae",
|
| 139 |
-
torch_dtype=torch.
|
| 140 |
low_cpu_mem_usage=True
|
| 141 |
)
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
|
| 152 |
# ์ค์ผ์ค๋ฌ ์ค์
|
| 153 |
self._pipe.scheduler = UniPCMultistepScheduler.from_config(
|
|
@@ -155,20 +171,24 @@ class ModelManager:
|
|
| 155 |
)
|
| 156 |
|
| 157 |
# LoRA ๋ก๋
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
# GPU ์ต์ ํ ์ค์
|
| 166 |
if hasattr(spaces, 'GPU'): # Zero GPU ํ๊ฒฝ
|
| 167 |
self._pipe.enable_model_cpu_offload()
|
| 168 |
logger.info("CPU offload enabled for Zero GPU")
|
| 169 |
-
elif config.enable_model_cpu_offload:
|
| 170 |
self._pipe.enable_model_cpu_offload()
|
| 171 |
-
|
| 172 |
self._pipe.to("cuda")
|
| 173 |
|
| 174 |
if config.enable_vae_slicing:
|
|
@@ -284,9 +304,12 @@ class VideoGenerator:
|
|
| 284 |
required_memory = (height * width * 3 * 8 * duration * self.config.fixed_fps) / (1024**3)
|
| 285 |
if free_memory < required_memory * 2:
|
| 286 |
clear_gpu_memory()
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
|
|
|
|
|
|
|
|
|
| 290 |
|
| 291 |
return True, None
|
| 292 |
|
|
@@ -394,26 +417,46 @@ def generate_video(input_image, prompt, height, width,
|
|
| 394 |
progress(0.4, desc="๐ฌ Generating video frames...")
|
| 395 |
|
| 396 |
# ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ์์ฑ
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
|
| 418 |
progress(0.9, desc="๐พ Saving video...")
|
| 419 |
filename = video_generator.generate_unique_filename(current_seed)
|
|
@@ -424,11 +467,19 @@ def generate_video(input_image, prompt, height, width,
|
|
| 424 |
|
| 425 |
progress(1.0, desc="โจ Complete!")
|
| 426 |
logger.info(f"Video generated successfully: {num_frames} frames, {target_h}x{target_w}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
return video_path, current_seed
|
| 428 |
|
|
|
|
|
|
|
|
|
|
| 429 |
except Exception as e:
|
| 430 |
logger.error(f"Unexpected error: {e}")
|
| 431 |
-
raise
|
| 432 |
|
| 433 |
finally:
|
| 434 |
# ํญ์ ๋ฉ๋ชจ๋ฆฌ ์ ๋ฆฌ ๋ฐ ๋ฝ ํด์
|
|
@@ -588,12 +639,34 @@ body {
|
|
| 588 |
font-size: 0.9em;
|
| 589 |
}
|
| 590 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
.footer {
|
| 592 |
text-align: center;
|
| 593 |
margin-top: 30px;
|
| 594 |
color: #666;
|
| 595 |
font-size: 0.9em;
|
| 596 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 597 |
"""
|
| 598 |
|
| 599 |
# Gradio UI
|
|
@@ -622,6 +695,19 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 622 |
</div>
|
| 623 |
""")
|
| 624 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
with gr.Row(elem_classes="main-content"):
|
| 626 |
with gr.Column(scale=1):
|
| 627 |
gr.Markdown("### ๐ธ Input Settings")
|
|
@@ -718,21 +804,24 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 718 |
|
| 719 |
gr.HTML("""
|
| 720 |
<div class="footer">
|
| 721 |
-
<p>๐ก Tip: For best results, use clear images with good lighting</p>
|
| 722 |
</div>
|
| 723 |
""")
|
| 724 |
|
| 725 |
-
# Examples
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
[
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
|
|
|
|
|
|
|
|
|
| 736 |
|
| 737 |
# ๊ฐ์ ์ฌํญ ์์ฝ (์๊ฒ)
|
| 738 |
gr.HTML("""
|
|
@@ -768,4 +857,9 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 768 |
)
|
| 769 |
|
| 770 |
if __name__ == "__main__":
|
| 771 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
logger.info("Loading model with memory optimizations...")
|
| 125 |
clear_gpu_memory()
|
| 126 |
|
| 127 |
+
# ๋ชจ๋ธ ์ปดํฌ๋ํธ ๋ก๋ (๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ) - autocast ์์
|
| 128 |
+
if torch.cuda.is_available():
|
| 129 |
+
with torch.amp.autocast('cuda', enabled=False): # ์์ ๋ ๋ถ๋ถ
|
| 130 |
+
image_encoder = CLIPVisionModel.from_pretrained(
|
| 131 |
+
config.model_id,
|
| 132 |
+
subfolder="image_encoder",
|
| 133 |
+
torch_dtype=torch.float16,
|
| 134 |
+
low_cpu_mem_usage=True
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
vae = AutoencoderKLWan.from_pretrained(
|
| 138 |
+
config.model_id,
|
| 139 |
+
subfolder="vae",
|
| 140 |
+
torch_dtype=torch.float16,
|
| 141 |
+
low_cpu_mem_usage=True
|
| 142 |
+
)
|
| 143 |
+
else:
|
| 144 |
+
# CPU ํ๊ฒฝ
|
| 145 |
image_encoder = CLIPVisionModel.from_pretrained(
|
| 146 |
config.model_id,
|
| 147 |
subfolder="image_encoder",
|
| 148 |
+
torch_dtype=torch.float32,
|
| 149 |
low_cpu_mem_usage=True
|
| 150 |
)
|
| 151 |
|
| 152 |
vae = AutoencoderKLWan.from_pretrained(
|
| 153 |
config.model_id,
|
| 154 |
subfolder="vae",
|
| 155 |
+
torch_dtype=torch.float32,
|
| 156 |
low_cpu_mem_usage=True
|
| 157 |
)
|
| 158 |
|
| 159 |
+
self._pipe = WanImageToVideoPipeline.from_pretrained(
|
| 160 |
+
config.model_id,
|
| 161 |
+
vae=vae,
|
| 162 |
+
image_encoder=image_encoder,
|
| 163 |
+
torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
|
| 164 |
+
low_cpu_mem_usage=True,
|
| 165 |
+
use_safetensors=True
|
| 166 |
+
)
|
| 167 |
|
| 168 |
# ์ค์ผ์ค๋ฌ ์ค์
|
| 169 |
self._pipe.scheduler = UniPCMultistepScheduler.from_config(
|
|
|
|
| 171 |
)
|
| 172 |
|
| 173 |
# LoRA ๋ก๋
|
| 174 |
+
try:
|
| 175 |
+
causvid_path = hf_hub_download(
|
| 176 |
+
repo_id=config.lora_repo_id, filename=config.lora_filename
|
| 177 |
+
)
|
| 178 |
+
self._pipe.load_lora_weights(causvid_path, adapter_name="causvid_lora")
|
| 179 |
+
self._pipe.set_adapters(["causvid_lora"], adapter_weights=[0.95])
|
| 180 |
+
self._pipe.fuse_lora()
|
| 181 |
+
logger.info("LoRA weights loaded successfully")
|
| 182 |
+
except Exception as e:
|
| 183 |
+
logger.warning(f"Failed to load LoRA weights: {e}")
|
| 184 |
|
| 185 |
# GPU ์ต์ ํ ์ค์
|
| 186 |
if hasattr(spaces, 'GPU'): # Zero GPU ํ๊ฒฝ
|
| 187 |
self._pipe.enable_model_cpu_offload()
|
| 188 |
logger.info("CPU offload enabled for Zero GPU")
|
| 189 |
+
elif config.enable_model_cpu_offload and torch.cuda.is_available():
|
| 190 |
self._pipe.enable_model_cpu_offload()
|
| 191 |
+
elif torch.cuda.is_available():
|
| 192 |
self._pipe.to("cuda")
|
| 193 |
|
| 194 |
if config.enable_vae_slicing:
|
|
|
|
| 304 |
required_memory = (height * width * 3 * 8 * duration * self.config.fixed_fps) / (1024**3)
|
| 305 |
if free_memory < required_memory * 2:
|
| 306 |
clear_gpu_memory()
|
| 307 |
+
# ์ฌํ์ธ
|
| 308 |
+
free_memory = torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_allocated()
|
| 309 |
+
if free_memory < required_memory * 1.5:
|
| 310 |
+
return False, "โ ๏ธ Not enough GPU memory. Try smaller dimensions or shorter duration."
|
| 311 |
+
except Exception as e:
|
| 312 |
+
logger.warning(f"GPU memory check failed: {e}")
|
| 313 |
|
| 314 |
return True, None
|
| 315 |
|
|
|
|
| 417 |
progress(0.4, desc="๐ฌ Generating video frames...")
|
| 418 |
|
| 419 |
# ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ์์ฑ
|
| 420 |
+
if torch.cuda.is_available():
|
| 421 |
+
with torch.inference_mode(), torch.amp.autocast('cuda', enabled=True): # ์์ ๋ ๋ถ๋ถ
|
| 422 |
+
try:
|
| 423 |
+
output_frames_list = pipe(
|
| 424 |
+
image=resized_image,
|
| 425 |
+
prompt=prompt,
|
| 426 |
+
negative_prompt=negative_prompt,
|
| 427 |
+
height=target_h,
|
| 428 |
+
width=target_w,
|
| 429 |
+
num_frames=num_frames,
|
| 430 |
+
guidance_scale=float(guidance_scale),
|
| 431 |
+
num_inference_steps=int(steps),
|
| 432 |
+
generator=torch.Generator(device="cuda").manual_seed(current_seed),
|
| 433 |
+
return_dict=True
|
| 434 |
+
).frames[0]
|
| 435 |
+
except torch.cuda.OutOfMemoryError:
|
| 436 |
+
clear_gpu_memory()
|
| 437 |
+
raise gr.Error("๐พ GPU out of memory. Try smaller dimensions or shorter duration.")
|
| 438 |
+
except Exception as e:
|
| 439 |
+
logger.error(f"Generation error: {e}")
|
| 440 |
+
raise gr.Error(f"โ Generation failed: {str(e)}")
|
| 441 |
+
else:
|
| 442 |
+
# CPU ํ๊ฒฝ
|
| 443 |
+
with torch.inference_mode():
|
| 444 |
+
try:
|
| 445 |
+
output_frames_list = pipe(
|
| 446 |
+
image=resized_image,
|
| 447 |
+
prompt=prompt,
|
| 448 |
+
negative_prompt=negative_prompt,
|
| 449 |
+
height=target_h,
|
| 450 |
+
width=target_w,
|
| 451 |
+
num_frames=num_frames,
|
| 452 |
+
guidance_scale=float(guidance_scale),
|
| 453 |
+
num_inference_steps=int(steps),
|
| 454 |
+
generator=torch.Generator().manual_seed(current_seed),
|
| 455 |
+
return_dict=True
|
| 456 |
+
).frames[0]
|
| 457 |
+
except Exception as e:
|
| 458 |
+
logger.error(f"Generation error: {e}")
|
| 459 |
+
raise gr.Error(f"โ Generation failed: {str(e)}")
|
| 460 |
|
| 461 |
progress(0.9, desc="๐พ Saving video...")
|
| 462 |
filename = video_generator.generate_unique_filename(current_seed)
|
|
|
|
| 467 |
|
| 468 |
progress(1.0, desc="โจ Complete!")
|
| 469 |
logger.info(f"Video generated successfully: {num_frames} frames, {target_h}x{target_w}")
|
| 470 |
+
|
| 471 |
+
# ์ฑ๊ณต ์ ๋ณด ๋ฐํ
|
| 472 |
+
info_text = f"โ
Generated {num_frames} frames at {target_h}x{target_w} with seed {current_seed}"
|
| 473 |
+
gr.Info(info_text)
|
| 474 |
+
|
| 475 |
return video_path, current_seed
|
| 476 |
|
| 477 |
+
except gr.Error:
|
| 478 |
+
# Gradio ์๋ฌ๋ ๊ทธ๋๋ก ์ ๋ฌ
|
| 479 |
+
raise
|
| 480 |
except Exception as e:
|
| 481 |
logger.error(f"Unexpected error: {e}")
|
| 482 |
+
raise gr.Error(f"โ Unexpected error: {str(e)}")
|
| 483 |
|
| 484 |
finally:
|
| 485 |
# ํญ์ ๋ฉ๋ชจ๋ฆฌ ์ ๋ฆฌ ๋ฐ ๋ฝ ํด์
|
|
|
|
| 639 |
font-size: 0.9em;
|
| 640 |
}
|
| 641 |
|
| 642 |
+
.info-box {
|
| 643 |
+
background: rgba(52, 152, 219, 0.1);
|
| 644 |
+
border: 1px solid rgba(52, 152, 219, 0.3);
|
| 645 |
+
border-radius: 10px;
|
| 646 |
+
padding: 15px;
|
| 647 |
+
margin: 10px 0;
|
| 648 |
+
color: #2c5282;
|
| 649 |
+
font-size: 0.9em;
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
.footer {
|
| 653 |
text-align: center;
|
| 654 |
margin-top: 30px;
|
| 655 |
color: #666;
|
| 656 |
font-size: 0.9em;
|
| 657 |
}
|
| 658 |
+
|
| 659 |
+
/* ๋ก๋ฉ ์ ๋๋ฉ์ด์
๊ฐ์ */
|
| 660 |
+
.progress-bar {
|
| 661 |
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #667eea 100%);
|
| 662 |
+
background-size: 200% 100%;
|
| 663 |
+
animation: loading 1.5s ease-in-out infinite;
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
@keyframes loading {
|
| 667 |
+
0% { background-position: 0% 0%; }
|
| 668 |
+
100% { background-position: 200% 0%; }
|
| 669 |
+
}
|
| 670 |
"""
|
| 671 |
|
| 672 |
# Gradio UI
|
|
|
|
| 695 |
</div>
|
| 696 |
""")
|
| 697 |
|
| 698 |
+
# ์๋ก์ด ์ ๋ณด ๋ฐ์ค ์ถ๊ฐ
|
| 699 |
+
gr.HTML("""
|
| 700 |
+
<div class="info-box">
|
| 701 |
+
<strong>๐ฏ Quick Start Guide:</strong>
|
| 702 |
+
<ol style="margin: 5px 0; padding-left: 20px;">
|
| 703 |
+
<li>Upload your image - AI will calculate optimal dimensions</li>
|
| 704 |
+
<li>Enter a creative prompt or use the default</li>
|
| 705 |
+
<li>Adjust duration (1.5s recommended for best results)</li>
|
| 706 |
+
<li>Click Generate and wait ~60 seconds</li>
|
| 707 |
+
</ol>
|
| 708 |
+
</div>
|
| 709 |
+
""")
|
| 710 |
+
|
| 711 |
with gr.Row(elem_classes="main-content"):
|
| 712 |
with gr.Column(scale=1):
|
| 713 |
gr.Markdown("### ๐ธ Input Settings")
|
|
|
|
| 804 |
|
| 805 |
gr.HTML("""
|
| 806 |
<div class="footer">
|
| 807 |
+
<p>๐ก Tip: For best results, use clear images with good lighting and distinct subjects</p>
|
| 808 |
</div>
|
| 809 |
""")
|
| 810 |
|
| 811 |
+
# Examples - ํ์ผ๋ช
ํ์ธ ํ์
|
| 812 |
+
try:
|
| 813 |
+
gr.Examples(
|
| 814 |
+
examples=[
|
| 815 |
+
["peng.png", "a penguin playfully dancing in the snow, Antarctica", 512, 512],
|
| 816 |
+
["forg.jpg", "the frog jumps around", 576, 320], # 16:9 aspect ratio within limits
|
| 817 |
+
],
|
| 818 |
+
inputs=[input_image, prompt_input, height_slider, width_slider],
|
| 819 |
+
outputs=[video_output, seed],
|
| 820 |
+
fn=generate_video,
|
| 821 |
+
cache_examples=False # ์บ์ ๋นํ์ฑํ๋ก ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
|
| 822 |
+
)
|
| 823 |
+
except Exception as e:
|
| 824 |
+
logger.warning(f"Failed to load examples: {e}")
|
| 825 |
|
| 826 |
# ๊ฐ์ ์ฌํญ ์์ฝ (์๊ฒ)
|
| 827 |
gr.HTML("""
|
|
|
|
| 857 |
)
|
| 858 |
|
| 859 |
if __name__ == "__main__":
|
| 860 |
+
# ์ด๊ธฐ ๋ฉ๋ชจ๋ฆฌ ์ ๋ฆฌ
|
| 861 |
+
clear_gpu_memory()
|
| 862 |
+
|
| 863 |
+
# ์ฑ ์คํ
|
| 864 |
+
demo.queue(concurrency_count=1) # ๋์ ์คํ ์ ํ
|
| 865 |
+
demo.launch()
|