Spaces:
26A1
/
Running on Zero

File size: 8,096 Bytes
d56db19
 
 
 
 
 
 
 
 
 
 
 
47c0604
d56db19
 
 
 
 
9fb84f7
 
d56db19
 
cc5a53a
 
d56db19
4351ef0
 
3bc0201
cc5a53a
4351ef0
 
3bc0201
cc5a53a
4351ef0
 
d56db19
 
10b19f3
d56db19
 
 
 
 
a156459
d56db19
 
 
 
e77bb53
2920b83
ae234a2
 
d47dee0
3adf106
9fb84f7
 
 
 
cbe7eb1
9fb84f7
 
 
 
 
 
 
 
 
 
0b663c6
9fb84f7
0b663c6
9fb84f7
 
 
 
 
 
 
 
 
 
 
 
 
 
b30c1ac
0b663c6
9fb84f7
0b663c6
9fb84f7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d56db19
 
10b19f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d56db19
4351ef0
9fb84f7
4351ef0
1bb001b
 
a91c313
4351ef0
 
 
1bb001b
4351ef0
 
1bb001b
e43732f
4351ef0
1bb001b
 
74513b0
1bb001b
74513b0
e43732f
74513b0
d56db19
4c7edd2
9fb84f7
d56db19
 
0867cf0
 
 
 
 
 
 
 
 
d56db19
 
 
 
 
 
1bb001b
d56db19
9fb84f7
d56db19
 
 
 
 
 
 
 
 
 
 
 
589af5f
18214c5
589af5f
d56db19
 
425aec0
c5fcd69
d56db19
 
 
aead0ee
00206dd
cbe7eb1
ee93a9d
 
aead0ee
4351ef0
08edabf
aead0ee
 
ee93a9d
00206dd
fa4b815
 
9fb84f7
d56db19
e5f0362
d56db19
 
4e235d7
 
 
d56db19
 
 
39dcb4e
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import os; os.system('pip install --upgrade --no-deps spaces')
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")

import spaces
import torch
from diffusers import WanPipeline
from diffusers.models.transformers.transformer_wan import WanTransformer3DModel
from diffusers.utils.export_utils import export_to_video
import gradio as gr
import tempfile
import numpy as np
import random
import gc

from torchao.quantization import quantize_
from torchao.quantization import Float8DynamicActivationFloat8WeightConfig
from torchao.quantization import Int8WeightOnlyConfig

import aoti

MULTIPLE_OF = 16
ASPECT_RATIOS = {
    "21:9 (976x416)": (976, 416), 
    "16:9 (848x480)": (848, 480),
    "4:3 (768x576)": (768, 576),
    "1:1 (640x640)": (640, 640),
    
    "9:21 (624x1456)": (624, 1456),
    "9:21 (416x976)": (416, 976),
    "9:21 (288x656)": (288, 656),
    
    "9:16 (720x1280)": (720, 1280),
    "9:16 (480x848)": (480, 848),
    "9:16 (320x576)": (320, 576),
    
    "3:4 (576x768)": (576, 768),
}
DEFAULT_RATIO = "9:21 (416x976)"

MAX_SEED = np.iinfo(np.int32).max

FIXED_FPS = 16
MIN_FRAMES_MODEL = 8
MAX_FRAMES_MODEL = 240

MIN_DURATION = round(MIN_FRAMES_MODEL / FIXED_FPS, 1)
MAX_DURATION = round(MAX_FRAMES_MODEL / FIXED_FPS, 1)

MODEL_ID = "Wan-AI/Wan2.2-T2V-A14B-Diffusers"
LIGHTNING_LORA_REPO = "Kijai/WanVideo_comfy"
LORA_FILE = "LoRAs/Wan22-Lightning/Wan22_A14B_T2V_LOW_Lightning_4steps_lora_250928_rank64_fp16.safetensors"
LORA_FILE_2 = "LoRAs/Wan22-Lightning/Wan22_A14B_T2V_LOW_Lightning_4steps_lora_250928_rank64_fp16.safetensors"
lora_scale = 1.7
lora_scale_2 = 1.0

pipe = WanPipeline.from_pretrained(MODEL_ID,
    transformer=WanTransformer3DModel.from_pretrained(MODEL_ID,
        subfolder='transformer',
        torch_dtype=torch.bfloat16,
        device_map='cuda',
        low_cpu_mem_usage=True,
    ),
    transformer_2=None,
    torch_dtype=torch.bfloat16,
).to('cuda')

quantize_(pipe.text_encoder, Int8WeightOnlyConfig())

pipe.load_lora_weights(
    LIGHTNING_LORA_REPO, weight_name=LORA_FILE, adapter_name="lora_adapter"
)
pipe.fuse_lora(adapter_names=["lora_adapter"], lora_scale=lora_scale, components=["transformer"])
pipe.unload_lora_weights()
quantize_(pipe.transformer, Float8DynamicActivationFloat8WeightConfig())
gc.collect()
torch.cuda.empty_cache()

pipe.register_modules(
    transformer_2=WanTransformer3DModel.from_pretrained(MODEL_ID,
        subfolder='transformer_2',
        torch_dtype=torch.bfloat16,
        device_map='cuda',
        low_cpu_mem_usage=True,
    ),
)
pipe.load_lora_weights(
    LIGHTNING_LORA_REPO, weight_name=LORA_FILE_2,
    adapter_name="lora_adapter_2", load_into_transformer_2=True
)
pipe.fuse_lora(adapter_names=["lora_adapter_2"], lora_scale=lora_scale_2, components=["transformer_2"])
pipe.unload_lora_weights()
quantize_(pipe.transformer_2, Float8DynamicActivationFloat8WeightConfig())
gc.collect()
torch.cuda.empty_cache()

spaces.aoti_load(
    module=pipe.transformer,
    repo_id='cbensimon/WanTransformer3DModel-sm120-cu130-raa',
)
spaces.aoti_load(
    module=pipe.transformer_2,
    repo_id='cbensimon/WanTransformer3DModel-sm120-cu130-raa',
)

pipe.vae.enable_tiling()
pipe.vae.enable_slicing()

def get_num_frames(duration_seconds: float):
    raw_frames = int(round(duration_seconds * FIXED_FPS))
    raw_frames = np.clip(raw_frames, MIN_FRAMES_MODEL, MAX_FRAMES_MODEL)
    
    raw_frames_adjusted = raw_frames - 1
    remainder = raw_frames_adjusted % 4
    
    if remainder == 0:
        adjusted_frames = raw_frames
    elif remainder <= 2:
        adjusted_frames = raw_frames - remainder
    else:
        adjusted_frames = raw_frames + (4 - remainder)
    
    adjusted_frames = max(MIN_FRAMES_MODEL, min(adjusted_frames, MAX_FRAMES_MODEL))
    
    if (adjusted_frames - 1) % 4 != 0:
        adjusted_frames = ((adjusted_frames - 1) // 4) * 4 + 1
    
    return adjusted_frames

def get_duration(prompt, aspect_ratio, steps, negative_prompt, duration_seconds, GPU_time,
                 guidance_scale, guidance_scale_2, seed, randomize_seed, progress=None):

    GPU_time = float(GPU_time)
    
    if GPU_time == 0:
        width, height = ASPECT_RATIOS.get(aspect_ratio, ASPECT_RATIOS[DEFAULT_RATIO])
        BASE_FRAMES_HEIGHT_WIDTH = 81 * 832 * 624
        BASE_STEP_DURATION = 11
        frames = get_num_frames(float(duration_seconds))
        factor = frames * width * height / BASE_FRAMES_HEIGHT_WIDTH
        step_duration = BASE_STEP_DURATION * factor ** 1.5
        estimate = int(steps) * step_duration
        estimate = min(max(estimate, 10), 120)
    
        if float(guidance_scale) > 1 or float(guidance_scale_2) > 1:
            estimate *= 2          
    else:      
        estimate = GPU_time / 1.5

    gr.Info(f"GPU time = {estimate * 1.5}s")  
    return estimate

@spaces.GPU(duration=get_duration)

def generate_video(
    prompt,
    aspect_ratio,
    steps,
    negative_prompt,
    duration_seconds,
    GPU_time,
    guidance_scale,
    guidance_scale_2,
    seed,
    randomize_seed,
    progress=gr.Progress(track_tqdm=True),
):
    if not prompt or not prompt.strip():
        raise gr.Error("Please enter a prompt.")

    width, height = ASPECT_RATIOS.get(aspect_ratio, ASPECT_RATIOS[DEFAULT_RATIO])
    num_frames = get_num_frames(float(duration_seconds))
    current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)

    output_frames_list = pipe(
        prompt=prompt,
        negative_prompt=negative_prompt,
        height=height,
        width=width,
        num_frames=num_frames,
        guidance_scale=float(guidance_scale),
        guidance_scale_2=float(guidance_scale_2),
        num_inference_steps=int(steps),
        generator=torch.Generator(device="cuda").manual_seed(current_seed),
    ).frames[0]

    video_filename = f"{current_seed}_{guidance_scale}_{guidance_scale_2}.mp4"
    video_path = os.path.join(tempfile.gettempdir(), video_filename)
    export_to_video(output_frames_list, video_path, fps=FIXED_FPS, quality=7)

    return video_path, current_seed
    
with gr.Blocks(theme=gr.Theme.from_hub("26A1/_")) as demo:

    with gr.Row():
        with gr.Column():
            prompt_input = gr.Textbox(label="Prompt", value="", lines=2)
            duration_seconds_input = gr.Slider(minimum=MIN_DURATION, maximum=MAX_DURATION, step=0.1, value=4.4, label="Duration (s)")
            GPU_time_input = gr.Slider(value=90.0,minimum=0.0,maximum=300.0,step=1.0,label="GPU time (s)")
            randomize_seed_checkbox = gr.Checkbox(label="Randomize seed", value=True, interactive=True)
            seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0, interactive=True)           
            generate_button = gr.Button("Generate Video", variant="primary")

            with gr.Accordion("Advanced Settings", open=True):
                negative_prompt_input = gr.Textbox(label="Negative Prompt", value="", lines=2)                
                aspect_ratio_input = gr.Dropdown(choices=list(ASPECT_RATIOS.keys()), value=DEFAULT_RATIO, label="Aspect ratio")
             
                steps_slider = gr.Slider(minimum=1, maximum=12, step=1, value=6, label="Inference Steps")
                guidance_scale_input = gr.Slider(minimum=0.0, maximum=10.0, step=0.1, value=1.5, label="Guidance Scale - high noise stage")
                guidance_scale_2_input = gr.Slider(minimum=0.0, maximum=10.0, step=0.1, value=1.5, label="Guidance Scale 2 - low noise stage")
                
        with gr.Column():
            video_output = gr.Video(label="Generated Video", autoplay=False, interactive=False)

    ui_inputs = [
        prompt_input, aspect_ratio_input, steps_slider, negative_prompt_input, 
        duration_seconds_input, GPU_time_input, guidance_scale_input, 
        guidance_scale_2_input, seed_input, randomize_seed_checkbox
    ]
    generate_button.click(fn=generate_video, inputs=ui_inputs, outputs=[video_output, seed_input], api_name="generate_video")
if __name__ == "__main__":
    demo.queue().launch(ssr_mode=False, show_error=True)