Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -49,6 +49,16 @@ class VideoGenerationConfig:
|
|
| 49 |
enable_model_cpu_offload: bool = True
|
| 50 |
enable_vae_slicing: bool = True
|
| 51 |
enable_vae_tiling: bool = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
config = VideoGenerationConfig()
|
| 54 |
MAX_SEED = np.iinfo(np.int32).max
|
|
@@ -230,19 +240,33 @@ class VideoGenerator:
|
|
| 230 |
if len(prompt) > 500:
|
| 231 |
return False, "โ ๏ธ Prompt is too long (max 500 characters)"
|
| 232 |
|
| 233 |
-
|
| 234 |
-
|
|
|
|
| 235 |
|
| 236 |
-
if duration
|
| 237 |
-
return False, f"โฑ๏ธ Duration too
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
|
| 239 |
# GPU ๋ฉ๋ชจ๋ฆฌ ์ฒดํฌ
|
| 240 |
if torch.cuda.is_available():
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
|
|
|
|
|
|
|
|
|
| 246 |
|
| 247 |
return True, None
|
| 248 |
|
|
@@ -273,25 +297,36 @@ def handle_image_upload(image):
|
|
| 273 |
|
| 274 |
def get_duration(input_image, prompt, height, width, negative_prompt,
|
| 275 |
duration_seconds, guidance_scale, steps, seed, randomize_seed, progress):
|
| 276 |
-
# GPU
|
| 277 |
base_duration = 60
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
base_duration += 15
|
|
|
|
|
|
|
| 280 |
if duration_seconds > 2:
|
| 281 |
-
base_duration +=
|
|
|
|
|
|
|
| 282 |
|
| 283 |
-
#
|
| 284 |
pixels = height * width
|
| 285 |
-
if pixels >
|
| 286 |
base_duration += 20
|
|
|
|
|
|
|
| 287 |
|
| 288 |
-
|
|
|
|
| 289 |
|
| 290 |
@spaces.GPU(duration=get_duration)
|
| 291 |
@measure_time
|
| 292 |
def generate_video(input_image, prompt, height, width,
|
| 293 |
negative_prompt=config.default_negative_prompt,
|
| 294 |
-
duration_seconds=
|
| 295 |
seed=42, randomize_seed=False,
|
| 296 |
progress=gr.Progress(track_tqdm=True)):
|
| 297 |
|
|
@@ -302,6 +337,10 @@ def generate_video(input_image, prompt, height, width,
|
|
| 302 |
try:
|
| 303 |
progress(0.1, desc="๐ Validating inputs...")
|
| 304 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
# ์
๋ ฅ ๊ฒ์ฆ
|
| 306 |
is_valid, error_msg = video_generator.validate_inputs(
|
| 307 |
input_image, prompt, height, width, duration_seconds, steps
|
|
@@ -315,8 +354,15 @@ def generate_video(input_image, prompt, height, width,
|
|
| 315 |
progress(0.2, desc="๐ฏ Preparing image...")
|
| 316 |
target_h = max(config.mod_value, (int(height) // config.mod_value) * config.mod_value)
|
| 317 |
target_w = max(config.mod_value, (int(width) // config.mod_value) * config.mod_value)
|
| 318 |
-
|
| 319 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
|
| 321 |
|
| 322 |
# ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆ (๋ฉ๋ชจ๋ฆฌ ํจ์จ์ )
|
|
@@ -357,6 +403,7 @@ def generate_video(input_image, prompt, height, width,
|
|
| 357 |
export_to_video(output_frames_list, video_path, fps=config.fixed_fps)
|
| 358 |
|
| 359 |
progress(1.0, desc="โจ Complete!")
|
|
|
|
| 360 |
return video_path, current_seed
|
| 361 |
|
| 362 |
except Exception as e:
|
|
@@ -544,11 +591,12 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 544 |
# GPU ๋ฉ๋ชจ๋ฆฌ ๊ฒฝ๊ณ
|
| 545 |
gr.HTML("""
|
| 546 |
<div class="warning-box">
|
| 547 |
-
<strong>๐ก Performance Tips:</strong>
|
| 548 |
<ul style="margin: 5px 0; padding-left: 20px;">
|
| 549 |
-
<li>
|
| 550 |
-
<li>
|
| 551 |
-
<li>Use 4-
|
|
|
|
| 552 |
</ul>
|
| 553 |
</div>
|
| 554 |
""")
|
|
@@ -572,11 +620,11 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 572 |
)
|
| 573 |
|
| 574 |
duration_input = gr.Slider(
|
| 575 |
-
minimum=round(config.
|
| 576 |
-
maximum=round(config.
|
| 577 |
step=0.1,
|
| 578 |
-
value=
|
| 579 |
-
label="โฑ๏ธ Video Duration (seconds)",
|
| 580 |
elem_classes="slider-container"
|
| 581 |
)
|
| 582 |
|
|
@@ -699,4 +747,4 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
|
|
| 699 |
)
|
| 700 |
|
| 701 |
if __name__ == "__main__":
|
| 702 |
-
demo.queue(max_size=
|
|
|
|
| 49 |
enable_model_cpu_offload: bool = True
|
| 50 |
enable_vae_slicing: bool = True
|
| 51 |
enable_vae_tiling: bool = True
|
| 52 |
+
|
| 53 |
+
@property
|
| 54 |
+
def max_duration(self):
|
| 55 |
+
"""์ต๋ ํ์ฉ duration (์ด)"""
|
| 56 |
+
return self.max_frames / self.fixed_fps
|
| 57 |
+
|
| 58 |
+
@property
|
| 59 |
+
def min_duration(self):
|
| 60 |
+
"""์ต์ ํ์ฉ duration (์ด)"""
|
| 61 |
+
return self.min_frames / self.fixed_fps
|
| 62 |
|
| 63 |
config = VideoGenerationConfig()
|
| 64 |
MAX_SEED = np.iinfo(np.int32).max
|
|
|
|
| 240 |
if len(prompt) > 500:
|
| 241 |
return False, "โ ๏ธ Prompt is too long (max 500 characters)"
|
| 242 |
|
| 243 |
+
# ์ ํํ duration ๋ฒ์ ์ฒดํฌ
|
| 244 |
+
min_duration = self.config.min_duration
|
| 245 |
+
max_duration = self.config.max_duration
|
| 246 |
|
| 247 |
+
if duration < min_duration:
|
| 248 |
+
return False, f"โฑ๏ธ Duration too short (min {min_duration:.1f}s)"
|
| 249 |
+
|
| 250 |
+
if duration > max_duration:
|
| 251 |
+
return False, f"โฑ๏ธ Duration too long (max {max_duration:.1f}s)"
|
| 252 |
+
|
| 253 |
+
# Zero GPU ํ๊ฒฝ์์๋ ๋ ๋ณด์์ ์ธ ์ ํ ์ ์ฉ
|
| 254 |
+
if hasattr(spaces, 'GPU'): # Spaces ํ๊ฒฝ ์ฒดํฌ
|
| 255 |
+
if duration > 2.5: # Zero GPU์์๋ 2.5์ด๋ก ์ ํ
|
| 256 |
+
return False, "โฑ๏ธ In Zero GPU environment, duration is limited to 2.5s for stability"
|
| 257 |
+
if height > 640 or width > 640: # ํด์๋๋ ์ ํ
|
| 258 |
+
return False, "๐ In Zero GPU environment, resolution is limited to 640x640"
|
| 259 |
|
| 260 |
# GPU ๋ฉ๋ชจ๋ฆฌ ์ฒดํฌ
|
| 261 |
if torch.cuda.is_available():
|
| 262 |
+
try:
|
| 263 |
+
free_memory = torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_allocated()
|
| 264 |
+
required_memory = (height * width * 3 * 8 * duration * self.config.fixed_fps) / (1024**3)
|
| 265 |
+
if free_memory < required_memory * 2:
|
| 266 |
+
clear_gpu_memory()
|
| 267 |
+
return False, "โ ๏ธ Not enough GPU memory. Try smaller dimensions or shorter duration."
|
| 268 |
+
except:
|
| 269 |
+
pass # GPU ์ฒดํฌ ์คํจ์ ๊ณ์ ์งํ
|
| 270 |
|
| 271 |
return True, None
|
| 272 |
|
|
|
|
| 297 |
|
| 298 |
def get_duration(input_image, prompt, height, width, negative_prompt,
|
| 299 |
duration_seconds, guidance_scale, steps, seed, randomize_seed, progress):
|
| 300 |
+
# Zero GPU ํ๊ฒฝ์์๋ ๋ ๋ณด์์ ์ธ ์๊ฐ ํ ๋น
|
| 301 |
base_duration = 60
|
| 302 |
+
|
| 303 |
+
# ๋จ๊ณ๋ณ ์ถ๊ฐ ์๊ฐ
|
| 304 |
+
if steps > 8:
|
| 305 |
+
base_duration += 30
|
| 306 |
+
elif steps > 4:
|
| 307 |
base_duration += 15
|
| 308 |
+
|
| 309 |
+
# Duration๋ณ ์ถ๊ฐ ์๊ฐ
|
| 310 |
if duration_seconds > 2:
|
| 311 |
+
base_duration += 20
|
| 312 |
+
elif duration_seconds > 1.5:
|
| 313 |
+
base_duration += 10
|
| 314 |
|
| 315 |
+
# ํด์๋๋ณ ์ถ๊ฐ ์๊ฐ
|
| 316 |
pixels = height * width
|
| 317 |
+
if pixels > 400000: # ์ฝ 640x640
|
| 318 |
base_duration += 20
|
| 319 |
+
elif pixels > 250000: # ์ฝ 512x512
|
| 320 |
+
base_duration += 10
|
| 321 |
|
| 322 |
+
# Zero GPU ํ๊ฒฝ์์๋ ์ต๋ 90์ด๋ก ์ ํ
|
| 323 |
+
return min(base_duration, 90)
|
| 324 |
|
| 325 |
@spaces.GPU(duration=get_duration)
|
| 326 |
@measure_time
|
| 327 |
def generate_video(input_image, prompt, height, width,
|
| 328 |
negative_prompt=config.default_negative_prompt,
|
| 329 |
+
duration_seconds=1.5, guidance_scale=1, steps=4,
|
| 330 |
seed=42, randomize_seed=False,
|
| 331 |
progress=gr.Progress(track_tqdm=True)):
|
| 332 |
|
|
|
|
| 337 |
try:
|
| 338 |
progress(0.1, desc="๐ Validating inputs...")
|
| 339 |
|
| 340 |
+
# Zero GPU ํ๊ฒฝ์์ ์ถ๊ฐ ๊ฒ์ฆ
|
| 341 |
+
if hasattr(spaces, 'GPU'):
|
| 342 |
+
logger.info(f"Zero GPU environment detected. Duration: {duration_seconds}s, Resolution: {height}x{width}")
|
| 343 |
+
|
| 344 |
# ์
๋ ฅ ๊ฒ์ฆ
|
| 345 |
is_valid, error_msg = video_generator.validate_inputs(
|
| 346 |
input_image, prompt, height, width, duration_seconds, steps
|
|
|
|
| 354 |
progress(0.2, desc="๐ฏ Preparing image...")
|
| 355 |
target_h = max(config.mod_value, (int(height) // config.mod_value) * config.mod_value)
|
| 356 |
target_w = max(config.mod_value, (int(width) // config.mod_value) * config.mod_value)
|
| 357 |
+
|
| 358 |
+
# ํ๋ ์ ์ ๊ณ์ฐ (Zero GPU ํ๊ฒฝ์์ ์ถ๊ฐ ์ ํ)
|
| 359 |
+
max_allowed_frames = int(2.5 * config.fixed_fps) if hasattr(spaces, 'GPU') else config.max_frames
|
| 360 |
+
num_frames = min(
|
| 361 |
+
int(round(duration_seconds * config.fixed_fps)),
|
| 362 |
+
max_allowed_frames
|
| 363 |
+
)
|
| 364 |
+
num_frames = np.clip(num_frames, config.min_frames, max_allowed_frames)
|
| 365 |
+
|
| 366 |
current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
|
| 367 |
|
| 368 |
# ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆ (๋ฉ๋ชจ๋ฆฌ ํจ์จ์ )
|
|
|
|
| 403 |
export_to_video(output_frames_list, video_path, fps=config.fixed_fps)
|
| 404 |
|
| 405 |
progress(1.0, desc="โจ Complete!")
|
| 406 |
+
logger.info(f"Video generated successfully: {num_frames} frames, {target_h}x{target_w}")
|
| 407 |
return video_path, current_seed
|
| 408 |
|
| 409 |
except Exception as e:
|
|
|
|
| 591 |
# GPU ๋ฉ๋ชจ๋ฆฌ ๊ฒฝ๊ณ
|
| 592 |
gr.HTML("""
|
| 593 |
<div class="warning-box">
|
| 594 |
+
<strong>๐ก Zero GPU Performance Tips:</strong>
|
| 595 |
<ul style="margin: 5px 0; padding-left: 20px;">
|
| 596 |
+
<li>Maximum duration: 2.5 seconds (limited by Zero GPU)</li>
|
| 597 |
+
<li>Recommended resolution: 512x512 for stable generation</li>
|
| 598 |
+
<li>Use 4-6 steps for optimal speed/quality balance</li>
|
| 599 |
+
<li>Wait between generations to avoid queue errors</li>
|
| 600 |
</ul>
|
| 601 |
</div>
|
| 602 |
""")
|
|
|
|
| 620 |
)
|
| 621 |
|
| 622 |
duration_input = gr.Slider(
|
| 623 |
+
minimum=round(config.min_duration, 1),
|
| 624 |
+
maximum=2.5 if hasattr(spaces, 'GPU') else round(config.max_duration, 1), # Zero GPU ํ๊ฒฝ ์ ํ
|
| 625 |
step=0.1,
|
| 626 |
+
value=1.5, # ์์ ํ ๊ธฐ๋ณธ๊ฐ
|
| 627 |
+
label="โฑ๏ธ Video Duration (seconds) - Limited to 2.5s in Zero GPU",
|
| 628 |
elem_classes="slider-container"
|
| 629 |
)
|
| 630 |
|
|
|
|
| 747 |
)
|
| 748 |
|
| 749 |
if __name__ == "__main__":
|
| 750 |
+
demo.queue(max_size=1).launch() # ํ ํฌ๊ธฐ ์ ํ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
|