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()
|