Spaces:
Running on Zero
Running on Zero
Commit ·
9fbf3f5
1
Parent(s): 819311d
Wire up graceful degradation and effect preview (FR-7, QF-2, QF-3)
Browse files- A failed interpolation or upscaling stage now surfaces a gr.Warning and falls back to
the last successfully-produced result instead of discarding the generated video (QF-2).
- Adds a live "Output: N frames @ X fps, WxHpx" preview that updates as duration, video
fluidity, and upscale settings change, before the user clicks Generate (QF-3).
Closes #7
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -73,13 +73,21 @@ def run_inference(
|
|
| 73 |
def _report_interpolation_progress(done: int, total: int) -> None:
|
| 74 |
progress(0.7 + 0.2 * (done / total), desc=f"Interpolating frames ({done}/{total})...")
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
else:
|
| 80 |
final_frames = list(raw_frames)
|
|
|
|
| 81 |
|
| 82 |
-
final_fps = FIXED_FPS * frame_factor
|
| 83 |
return final_frames, final_fps
|
| 84 |
|
| 85 |
|
|
@@ -123,10 +131,15 @@ def generate_video(
|
|
| 123 |
progress(0.95 + 0.04 * (done / total), desc=f"Upscaling 4x on CPU ({done}/{total} frames)...")
|
| 124 |
|
| 125 |
progress(0.95, desc=f"Upscaling 4x on CPU (0/{len(final_frames)} frames)...")
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 132 |
video_path = tmpfile.name
|
|
@@ -135,6 +148,25 @@ def generate_video(
|
|
| 135 |
return video_path, video_path, current_seed
|
| 136 |
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
with gr.Blocks() as demo:
|
| 139 |
gr.Markdown("# High Quality Video Generation")
|
| 140 |
gr.Markdown("Turn a still image into a short video clip guided by a text prompt.")
|
|
@@ -164,6 +196,7 @@ with gr.Blocks() as demo:
|
|
| 164 |
guidance_scale_input = gr.Slider(minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale")
|
| 165 |
seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
|
| 166 |
randomize_seed_checkbox = gr.Checkbox(label="Randomize seed", value=True)
|
|
|
|
| 167 |
generate_button = gr.Button("Generate Video", variant="primary")
|
| 168 |
with gr.Column():
|
| 169 |
video_output = gr.Video(label="Generated Video", autoplay=True)
|
|
@@ -186,5 +219,10 @@ with gr.Blocks() as demo:
|
|
| 186 |
outputs=[video_output, file_output, seed_input],
|
| 187 |
)
|
| 188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
if __name__ == "__main__":
|
| 190 |
demo.queue().launch(show_error=True)
|
|
|
|
| 73 |
def _report_interpolation_progress(done: int, total: int) -> None:
|
| 74 |
progress(0.7 + 0.2 * (done / total), desc=f"Interpolating frames ({done}/{total})...")
|
| 75 |
|
| 76 |
+
try:
|
| 77 |
+
final_frames = interpolate_frames(
|
| 78 |
+
raw_frames, multiplier=int(frame_factor), progress_callback=_report_interpolation_progress
|
| 79 |
+
)
|
| 80 |
+
final_fps = FIXED_FPS * frame_factor
|
| 81 |
+
except Exception as e:
|
| 82 |
+
# QF-2: don't discard a successfully generated base video over a post-processing failure.
|
| 83 |
+
print(f"Interpolation failed, returning base video: {e}")
|
| 84 |
+
gr.Warning(f"Frame interpolation failed ({e}); returning the video without it.")
|
| 85 |
+
final_frames = list(raw_frames)
|
| 86 |
+
final_fps = FIXED_FPS
|
| 87 |
else:
|
| 88 |
final_frames = list(raw_frames)
|
| 89 |
+
final_fps = FIXED_FPS
|
| 90 |
|
|
|
|
| 91 |
return final_frames, final_fps
|
| 92 |
|
| 93 |
|
|
|
|
| 131 |
progress(0.95 + 0.04 * (done / total), desc=f"Upscaling 4x on CPU ({done}/{total} frames)...")
|
| 132 |
|
| 133 |
progress(0.95, desc=f"Upscaling 4x on CPU (0/{len(final_frames)} frames)...")
|
| 134 |
+
try:
|
| 135 |
+
pil_frames = [
|
| 136 |
+
Image.fromarray((np.asarray(frame) * 255.0).round().astype(np.uint8)) for frame in final_frames
|
| 137 |
+
]
|
| 138 |
+
final_frames = upscale_frames(pil_frames, progress_callback=_report_upscale_progress)
|
| 139 |
+
except Exception as e:
|
| 140 |
+
# QF-2: a failed upscale still returns the pre-upscale (interpolated/base) result.
|
| 141 |
+
print(f"Upscaling failed, returning pre-upscale video: {e}")
|
| 142 |
+
gr.Warning(f"Upscaling failed ({e}); returning the video without it.")
|
| 143 |
|
| 144 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 145 |
video_path = tmpfile.name
|
|
|
|
| 148 |
return video_path, video_path, current_seed
|
| 149 |
|
| 150 |
|
| 151 |
+
def preview_effect(input_image, duration_seconds, frame_multiplier, upscale_output):
|
| 152 |
+
"""QF-3: show the resulting fps/resolution/duration before the user hits Generate."""
|
| 153 |
+
num_frames = get_num_frames(duration_seconds)
|
| 154 |
+
frame_factor = frame_multiplier // FIXED_FPS
|
| 155 |
+
out_fps = FIXED_FPS * frame_factor if frame_factor > 1 else FIXED_FPS
|
| 156 |
+
out_frame_count = ((num_frames - 1) * frame_factor + 1) if frame_factor > 1 else num_frames
|
| 157 |
+
|
| 158 |
+
if input_image is not None:
|
| 159 |
+
w, h = resize_image(input_image).size
|
| 160 |
+
else:
|
| 161 |
+
w = h = None
|
| 162 |
+
|
| 163 |
+
if upscale_output and w is not None:
|
| 164 |
+
w, h = w * 4, h * 4
|
| 165 |
+
|
| 166 |
+
resolution = f"{w}×{h}px" if w is not None else "resolution depends on the uploaded image"
|
| 167 |
+
return f"**Output:** ~{out_frame_count} frames @ {out_fps} fps, {resolution}"
|
| 168 |
+
|
| 169 |
+
|
| 170 |
with gr.Blocks() as demo:
|
| 171 |
gr.Markdown("# High Quality Video Generation")
|
| 172 |
gr.Markdown("Turn a still image into a short video clip guided by a text prompt.")
|
|
|
|
| 196 |
guidance_scale_input = gr.Slider(minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale")
|
| 197 |
seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
|
| 198 |
randomize_seed_checkbox = gr.Checkbox(label="Randomize seed", value=True)
|
| 199 |
+
effect_preview = gr.Markdown()
|
| 200 |
generate_button = gr.Button("Generate Video", variant="primary")
|
| 201 |
with gr.Column():
|
| 202 |
video_output = gr.Video(label="Generated Video", autoplay=True)
|
|
|
|
| 219 |
outputs=[video_output, file_output, seed_input],
|
| 220 |
)
|
| 221 |
|
| 222 |
+
preview_inputs = [input_image_component, duration_input, frame_multiplier_input, upscale_checkbox]
|
| 223 |
+
for component in preview_inputs:
|
| 224 |
+
component.change(fn=preview_effect, inputs=preview_inputs, outputs=effect_preview)
|
| 225 |
+
demo.load(fn=preview_effect, inputs=preview_inputs, outputs=effect_preview)
|
| 226 |
+
|
| 227 |
if __name__ == "__main__":
|
| 228 |
demo.queue().launch(show_error=True)
|