x2XcarleX2x commited on
Commit
f84d55e
·
verified ·
1 Parent(s): 09d7030

Update app_wan.py

Browse files
Files changed (1) hide show
  1. app_wan.py +57 -159
app_wan.py CHANGED
@@ -1,85 +1,24 @@
1
  # app_wan.py
2
 
3
  import os
4
-
5
- # PyTorch 2.8 (temporary hack) — manter conforme app original
6
- #os.system('pip install --upgrade --pre --extra-index-url https://download.pytorch.org/whl/nightly/cu126 "torch<2.9" spaces')
7
-
8
  import gradio as gr
9
  import tempfile
10
  import numpy as np
11
  from PIL import Image
12
- from gradio_client import Client, handle_file
13
-
14
- # === Constantes (espelhando o app original) ===
15
- MODEL_ID = "Wan-AI/Wan2.2-I2V-A14B-Diffusers"
16
-
17
- # Dimensões
18
- MAX_DIMENSION = 832
19
- MIN_DIMENSION = 480
20
- DIMENSION_MULTIPLE = 16
21
- SQUARE_SIZE = 480
22
 
23
- # Geração
24
  MAX_SEED = np.iinfo(np.int32).max
25
  FIXED_FPS = 16
26
  MIN_FRAMES_MODEL = 8
27
  MAX_FRAMES_MODEL = 81
28
-
29
  MIN_DURATION = round(MIN_FRAMES_MODEL / FIXED_FPS, 1)
30
  MAX_DURATION = round(MAX_FRAMES_MODEL / FIXED_FPS, 1)
31
 
32
- default_negative_prompt = (
33
- "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,"
34
- "JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,"
35
- "手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走,过曝,"
36
- )
37
-
38
  # === Importa o serviço de geração (manager) ===
39
  from aduc_framework.managers.wan_manager import WanManager
40
  wan_manager = WanManager()
41
 
42
- # === Utilidades de UI ===
43
- def switch_to_upload_tab():
44
- # Atualiza o Tabs existente para a aba "Upload"
45
- return gr.Tabs.update(selected="upload_tab")
46
-
47
- def generate_end_frame(start_img, gen_prompt, progress=gr.Progress(track_tqdm=True)):
48
- """
49
- Chama uma API Gradio externa para gerar uma imagem (end frame).
50
- """
51
- if start_img is None:
52
- raise gr.Error("Please provide a Start Frame first.")
53
-
54
- hf_token = os.getenv("HF_TOKEN")
55
- if not hf_token:
56
- raise gr.Error("HF_TOKEN not found in environment variables. Please set it in your Space secrets.")
57
-
58
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmpfile:
59
- start_img.save(tmpfile.name)
60
- tmp_path = tmpfile.name
61
-
62
- progress(0.1, desc="Connecting to image generation API...")
63
- client = Client("multimodalart/nano-banana")
64
-
65
- progress(0.5, desc=f"Generating with prompt: '{gen_prompt}'...")
66
- try:
67
- result = client.predict(
68
- prompt=gen_prompt,
69
- images=[{"image": handle_file(tmp_path)}],
70
- manual_token=hf_token,
71
- api_name="/unified_image_generator",
72
- )
73
- finally:
74
- try:
75
- os.remove(tmp_path)
76
- except:
77
- pass
78
-
79
- progress(1.0, desc="Done!")
80
- return result
81
-
82
- # Wrapper: a UI monta images_condition_items e delega ao serviço
83
  def ui_generate_video(
84
  start_image_pil,
85
  start_frame_text,
@@ -90,40 +29,31 @@ def ui_generate_video(
90
  end_frame_text,
91
  end_peso,
92
  prompt,
93
- negative_prompt=default_negative_prompt,
94
- duration_seconds=2.1,
95
- steps=8,
96
- guidance_scale=1.0,
97
- guidance_scale_2=1.0,
98
- seed=42,
99
- randomize_seed=False,
100
  progress=gr.Progress(track_tqdm=True),
101
  ):
102
- # Conversões simples
103
  def to_int_safe(v, default=0):
104
- try:
105
- return int(v)
106
- except:
107
- return default
108
-
109
  def to_float_safe(v, default=1.0):
110
- try:
111
- return float(v)
112
- except:
113
- return default
114
 
115
- # Peso da imagem inicial é fixo 1.0
116
  start_item = [start_image_pil, to_int_safe(start_frame_text, 0), 1.0]
117
-
118
- # Handle é opcional: se não houver imagem, não enviar item
119
  items = [start_item]
120
  if handle_image_pil is not None:
121
- items.append([handle_image_pil, to_int_safe(handle_frame_text, 4), to_float_safe(handle_peso, 1.0)])
122
-
123
- # Último item (end) sempre presente
124
  items.append([end_image_pil, to_int_safe(end_frame_text, MAX_FRAMES_MODEL - 1), to_float_safe(end_peso, 1.0)])
125
 
126
- return wan_manager.generate_video_from_conditions(
 
127
  images_condition_items=items,
128
  prompt=prompt,
129
  negative_prompt=negative_prompt,
@@ -134,82 +64,76 @@ def ui_generate_video(
134
  seed=int(seed),
135
  randomize_seed=bool(randomize_seed),
136
  )
 
 
 
137
 
138
- # === UI Gradio ===
139
  css = '''
140
  .fillable{max-width: 1100px !important}
141
  .dark .progress-text {color: white}
142
  #general_items{margin-top: 2em}
143
- #group_all{overflow:visible}
144
- #group_all .styler{overflow:visible}
145
- #group_tabs .tabitem{padding: 0}
146
- .tab-wrapper{margin-top: -33px;z-index: 999;position: absolute;width: 100%;background-color: var(--block-background-fill);padding: 0;}
147
- #component-9-button{width: 50%;justify-content: center}
148
- #component-11-button{width: 50%;justify-content: center}
149
- #or_item{text-align: center; padding-top: 1em; padding-bottom: 1em; font-size: 1.1em;margin-left: .5em;margin-right: .5em;width: calc(100% - 1em)}
150
- #fivesec{margin-top: 5em;margin-left: .5em;margin-right: .5em;width: calc(100% - 1em)}
151
  '''
152
 
153
- with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
154
- gr.Markdown("# Wan 2.2 Aduca-sdr")
155
 
156
  with gr.Row(elem_id="general_items"):
157
- with gr.Column():
158
- with gr.Group(elem_id="group_all"):
159
  with gr.Row():
160
- # Coluna: Start (peso fixo 1.0)
161
  with gr.Column():
162
  start_image = gr.Image(type="pil", label="Start Frame", sources=["upload", "clipboard"])
163
- start_frame_tb = gr.Textbox(label="Start Frame (int)", value="0")
164
 
165
  # Coluna: Handle (opcional)
166
  with gr.Column():
167
  handle_image = gr.Image(type="pil", label="Handle Image", sources=["upload", "clipboard"])
168
- handle_frame_tb = gr.Textbox(label="Handle Frame (int)", value="4")
169
- handle_peso_sl = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="Handle Peso")
170
-
171
- # Coluna: End (Upload/Generate)
172
- with gr.Tabs(elem_id="group_tabs") as tabs:
173
- with gr.TabItem("Upload", id="upload_tab"):
174
- with gr.Column():
175
- end_image = gr.Image(type="pil", label="End Frame", sources=["upload", "clipboard"])
176
- end_frame_tb = gr.Textbox(label="End Frame (int)", value=str(MAX_FRAMES_MODEL - 1))
177
- end_peso_sl = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="End Peso")
178
- with gr.TabItem("Generate", id="generate_tab"):
179
- generate_5seconds = gr.Button("Generate scene 5 seconds in the future", elem_id="fivesec")
180
- gr.Markdown(
181
- "Generate a custom end-frame with an edit model like Nano Banana or Qwen Image Edit",
182
- elem_id="or_item",
183
- )
184
- prompt = gr.Textbox(label="Prompt", info="Describe the transition between the two images")
185
 
186
  with gr.Accordion("Advanced Settings", open=False):
187
  duration_seconds_input = gr.Slider(
188
- minimum=MIN_DURATION,
189
- maximum=MAX_DURATION,
190
- step=0.1,
191
- value=2.1,
192
  label="Video Duration (seconds)",
193
- info=f"Clamped to model's {MIN_FRAMES_MODEL}-{MAX_FRAMES_MODEL} frames at {FIXED_FPS}fps.",
 
 
 
 
 
194
  )
195
- negative_prompt_input = gr.Textbox(label="Negative Prompt", value=default_negative_prompt, lines=3)
196
  steps_slider = gr.Slider(minimum=1, maximum=30, step=1, value=8, label="Inference Steps")
197
  guidance_scale_input = gr.Slider(
198
- minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale - high noise"
199
  )
200
  guidance_scale_2_input = gr.Slider(
201
- minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale - low noise"
202
  )
203
  with gr.Row():
204
- seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
205
- randomize_seed_checkbox = gr.Checkbox(label="Randomize seed", value=True)
206
 
207
  generate_button = gr.Button("Generate Video", variant="primary")
208
 
209
- with gr.Column():
210
  output_video = gr.Video(label="Generated Video", autoplay=True)
 
 
 
211
 
212
- # Inputs/outputs para o wrapper
213
  ui_inputs = [
214
  start_image, start_frame_tb,
215
  handle_image, handle_frame_tb, handle_peso_sl,
@@ -218,35 +142,9 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
218
  steps_slider, guidance_scale_input, guidance_scale_2_input,
219
  seed_input, randomize_seed_checkbox,
220
  ]
221
- ui_outputs = [output_video, seed_input]
222
 
223
  generate_button.click(fn=ui_generate_video, inputs=ui_inputs, outputs=ui_outputs)
224
 
225
- # Cadeia “5 seconds”: alterna aba, gera end frame e dispara render
226
- generate_5seconds.click(
227
- fn=switch_to_upload_tab,
228
- inputs=None,
229
- outputs=[tabs]
230
- ).then(
231
- fn=lambda img: generate_end_frame(
232
- img,
233
- "this image is a still frame from a movie. generate a new frame with what happens on this scene 5 seconds in the future"
234
- ),
235
- inputs=[start_image],
236
- outputs=[end_image]
237
- ).success(
238
- fn=ui_generate_video,
239
- inputs=ui_inputs,
240
- outputs=ui_outputs
241
- )
242
-
243
  if __name__ == "__main__":
244
- os.makedirs("examples", exist_ok=True)
245
- try:
246
- Image.new('RGB', (832, 480), color=(73, 109, 137)).save("examples/frame_1.png")
247
- Image.new('RGB', (832, 480), color=(173, 109, 237)).save("examples/frame_2.png")
248
- Image.new('RGB', (832, 480), color=(255, 255, 0)).save("examples/frame_3.png")
249
- except:
250
- pass
251
-
252
- app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
1
  # app_wan.py
2
 
3
  import os
 
 
 
 
4
  import gradio as gr
5
  import tempfile
6
  import numpy as np
7
  from PIL import Image
 
 
 
 
 
 
 
 
 
 
8
 
9
+ # === Constantes ===
10
  MAX_SEED = np.iinfo(np.int32).max
11
  FIXED_FPS = 16
12
  MIN_FRAMES_MODEL = 8
13
  MAX_FRAMES_MODEL = 81
 
14
  MIN_DURATION = round(MIN_FRAMES_MODEL / FIXED_FPS, 1)
15
  MAX_DURATION = round(MAX_FRAMES_MODEL / FIXED_FPS, 1)
16
 
 
 
 
 
 
 
17
  # === Importa o serviço de geração (manager) ===
18
  from aduc_framework.managers.wan_manager import WanManager
19
  wan_manager = WanManager()
20
 
21
+ # === Wrapper da UI para o Serviço ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  def ui_generate_video(
23
  start_image_pil,
24
  start_frame_text,
 
29
  end_frame_text,
30
  end_peso,
31
  prompt,
32
+ negative_prompt,
33
+ duration_seconds,
34
+ steps,
35
+ guidance_scale,
36
+ guidance_scale_2,
37
+ seed,
38
+ randomize_seed,
39
  progress=gr.Progress(track_tqdm=True),
40
  ):
 
41
  def to_int_safe(v, default=0):
42
+ try: return int(v)
43
+ except: return default
 
 
 
44
  def to_float_safe(v, default=1.0):
45
+ try: return float(v)
46
+ except: return default
 
 
47
 
48
+ # Prepara a lista de imagens de condição
49
  start_item = [start_image_pil, to_int_safe(start_frame_text, 0), 1.0]
 
 
50
  items = [start_item]
51
  if handle_image_pil is not None:
52
+ items.append([handle_image_pil, to_int_safe(handle_frame_text, 17), to_float_safe(handle_peso, 1.0)])
 
 
53
  items.append([end_image_pil, to_int_safe(end_frame_text, MAX_FRAMES_MODEL - 1), to_float_safe(end_peso, 1.0)])
54
 
55
+ # Chama o manager, que agora retorna 4 valores
56
+ video_path, current_seed, debug_video_path, grid_image_path = wan_manager.generate_video_from_conditions(
57
  images_condition_items=items,
58
  prompt=prompt,
59
  negative_prompt=negative_prompt,
 
64
  seed=int(seed),
65
  randomize_seed=bool(randomize_seed),
66
  )
67
+
68
+ # Retorna os 4 valores para os componentes da UI
69
+ return video_path, current_seed, debug_video_path, grid_image_path
70
 
71
+ # === Interface Gradio ===
72
  css = '''
73
  .fillable{max-width: 1100px !important}
74
  .dark .progress-text {color: white}
75
  #general_items{margin-top: 2em}
 
 
 
 
 
 
 
 
76
  '''
77
 
78
+ with gr.Blocks(theme=gr.themes.Glass(), css=css) as app:
79
+ gr.Markdown("# Wan 2.2 Aduca-SDR")
80
 
81
  with gr.Row(elem_id="general_items"):
82
+ with gr.Column(scale=2):
83
+ with gr.Group():
84
  with gr.Row():
85
+ # Coluna: Start
86
  with gr.Column():
87
  start_image = gr.Image(type="pil", label="Start Frame", sources=["upload", "clipboard"])
88
+ start_frame_tb = gr.Textbox(label="Start Frame Index", value="0", interactive=False)
89
 
90
  # Coluna: Handle (opcional)
91
  with gr.Column():
92
  handle_image = gr.Image(type="pil", label="Handle Image", sources=["upload", "clipboard"])
93
+ handle_frame_tb = gr.Textbox(label="Handle Frame Index", value="17")
94
+ handle_peso_sl = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="Handle Weight")
95
+
96
+ # Coluna: End
97
+ with gr.Column():
98
+ end_image = gr.Image(type="pil", label="End Frame", sources=["upload", "clipboard"])
99
+ end_frame_tb = gr.Textbox(label="End Frame Index", value=str(MAX_FRAMES_MODEL - 1), interactive=False)
100
+ end_peso_sl = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="End Weight")
101
+
102
+ prompt = gr.Textbox(
103
+ label="Prompt",
104
+ info="Descreva a transição e a cena. Ex: 'a beautiful woman walking on the beach, cinematic'."
105
+ )
 
 
 
 
106
 
107
  with gr.Accordion("Advanced Settings", open=False):
108
  duration_seconds_input = gr.Slider(
109
+ minimum=MIN_DURATION, maximum=MAX_DURATION, step=0.1, value=3.2,
 
 
 
110
  label="Video Duration (seconds)",
111
+ info=f"Será ajustado para o formato 4n+1. Mín: {MIN_FRAMES_MODEL} frames, Máx: {MAX_FRAMES_MODEL} frames."
112
+ )
113
+ negative_prompt_input = gr.Textbox(
114
+ label="Negative Prompt",
115
+ value=wan_manager.default_negative_prompt,
116
+ lines=3
117
  )
 
118
  steps_slider = gr.Slider(minimum=1, maximum=30, step=1, value=8, label="Inference Steps")
119
  guidance_scale_input = gr.Slider(
120
+ minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale (High Noise)"
121
  )
122
  guidance_scale_2_input = gr.Slider(
123
+ minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale (Low Noise)"
124
  )
125
  with gr.Row():
126
+ seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42, interactive=True)
127
+ randomize_seed_checkbox = gr.Checkbox(label="Randomize Seed", value=True)
128
 
129
  generate_button = gr.Button("Generate Video", variant="primary")
130
 
131
+ with gr.Column(scale=1):
132
  output_video = gr.Video(label="Generated Video", autoplay=True)
133
+ with gr.Accordion("Debugging Visuals", open=True):
134
+ debug_video = gr.Video(label="Denoising Process Video", autoplay=False)
135
+ steps_grid_image = gr.Image(label="Denoising Steps Grid", interactive=False, type="filepath")
136
 
 
137
  ui_inputs = [
138
  start_image, start_frame_tb,
139
  handle_image, handle_frame_tb, handle_peso_sl,
 
142
  steps_slider, guidance_scale_input, guidance_scale_2_input,
143
  seed_input, randomize_seed_checkbox,
144
  ]
145
+ ui_outputs = [output_video, seed_input, debug_video, steps_grid_image]
146
 
147
  generate_button.click(fn=ui_generate_video, inputs=ui_inputs, outputs=ui_outputs)
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  if __name__ == "__main__":
150
+ app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)