File size: 4,522 Bytes
238498e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import gradio as gr
import torch
from diffusers import StableDiffusionPipeline
import os
import uuid
import animation_logic as anim
import video_utils as vid

# --- Model Config (SDXS Optimized) ---
device = "cpu"
model_id = "IDKiro/sdxs-512-dreamshaper"
pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float32)
pipe.to(device)

def run_deforum(
    prompt_list_str, neg_prompt, max_frames, 
    zoom_str, angle_str, tx_str, ty_str,
    cadence, fps
):
    # Setup
    width, height = 256, 256
    try:
        prompts = eval(prompt_list_str)
    except:
        return None, None, "Error: Prompt dictionary format invalid."

    # Parse Schedules
    zoom_s = anim.parse_keyframe_string(zoom_str, max_frames)
    angle_s = anim.parse_keyframe_string(angle_str, max_frames)
    tx_s = anim.parse_keyframe_string(tx_str, max_frames)
    ty_s = anim.parse_keyframe_string(ty_str, max_frames)
    
    all_frames = []
    prev_gen_frame = None
    
    # Generation Loop
    for f in range(max_frames):
        if f % cadence == 0:
            # Determine prompt
            current_prompt = prompts[max(k for k in prompts.keys() if k <= f)]
            
            # Warp previous frame if it exists
            if prev_gen_frame is not None:
                # We warp the frame based on the cumulative motion across the cadence gap
                init_image = anim.anim_frame_warp(prev_gen_frame, angle_s[f], zoom_s[f], tx_s[f], ty_s[f])
                # SDXS Inference (1-step, 0 guidance)
                new_frame = pipe(
                    current_prompt, 
                    image=init_image, # This mimics the 'strength' logic
                    negative_prompt=neg_prompt,
                    num_inference_steps=1,
                    guidance_scale=0.0,
                    width=width, height=height
                ).images[0]
            else:
                # First frame
                new_frame = pipe(
                    current_prompt, 
                    negative_prompt=neg_prompt,
                    num_inference_steps=1,
                    guidance_scale=0.0,
                    width=width, height=height
                ).images[0]

            # Handle Cadence Interpolation for the gap behind us
            if cadence > 1 and prev_gen_frame is not None:
                start_gap = f - cadence
                for i in range(1, cadence):
                    alpha = i / cadence
                    interp_frame = anim.lerp_frames(prev_gen_frame, new_frame, alpha)
                    all_frames.append(interp_frame)
            
            all_frames.append(new_frame)
            prev_gen_frame = new_frame
            yield new_frame, None, None
            
    # Finalize Video and Zip
    video_file = vid.frames_to_video(all_frames, f"output_{uuid.uuid4().hex[:6]}.mp4", fps)
    zip_file = vid.export_to_zip(all_frames, f"frames_{uuid.uuid4().hex[:6]}.zip")
    
    yield all_frames[-1], video_file, zip_file

# --- Gradio Interface ---
with gr.Blocks(theme=gr.themes.Glass()) as demo:
    gr.Markdown("# 🎨 Deforum Soonr Variant 2")
    
    with gr.Row():
        with gr.Column(scale=1):
            prompts = gr.Textbox(label="Prompts (Frame: Prompt Dict)", 
                                 value='{0: "a snowy mountain", 15: "a fiery volcano"}', lines=3)
            neg_p = gr.Textbox(label="Negative Prompt", value="blur, lowres, text")
            
            with gr.Row():
                frames_n = gr.Number(label="Max Frames", value=20)
                cadence_n = gr.Slider(1, 4, value=2, step=1, label="Cadence (Skip Steps)")
                fps_n = gr.Number(label="FPS", value=10)
            
            with gr.Accordion("Motion Parameters", open=False):
                zoom = gr.Textbox(label="Zoom", value="0:(1.04)")
                angle = gr.Textbox(label="Angle", value="0:(2*sin(t/5))")
                tx = gr.Textbox(label="Translation X", value="0:(0)")
                ty = gr.Textbox(label="Translation Y", value="0:(0)")
            
            btn = gr.Button("Generate", variant="primary")
            
        with gr.Column(scale=1):
            preview = gr.Image(label="Live Frame Preview")
            video_out = gr.Video(label="Rendered Animation")
            file_out = gr.File(label="Download Batch (ZIP)")

    btn.click(
        fn=run_deforum,
        inputs=[prompts, neg_p, frames_n, zoom, angle, tx, ty, cadence_n, fps_n],
        outputs=[preview, video_out, file_out]
    )

demo.launch()