x2XcarleX2x commited on
Commit
37298e5
·
verified ·
1 Parent(s): def1a3e

Update app_wan.py

Browse files
Files changed (1) hide show
  1. app_wan.py +134 -43
app_wan.py CHANGED
@@ -1,37 +1,86 @@
1
- # app_wan.py (trechos essenciais)
2
 
3
  import os
4
- # (mantenha o hack de instalação se necessário)
5
- #os.system('pip install --upgrade --pre --extra-index-url https://download.pytorch.org/whl/nightly/cu126 "torch<2.9" spaces')
 
6
 
7
  import gradio as gr
8
- from PIL import Image
9
- import numpy as np
10
  import tempfile
 
 
 
 
11
 
12
- # === mantém as constantes de UI exatamente como no arquivo atual ===
13
  MODEL_ID = "Wan-AI/Wan2.2-I2V-A14B-Diffusers"
 
 
14
  MAX_DIMENSION = 832
15
  MIN_DIMENSION = 480
16
  DIMENSION_MULTIPLE = 16
17
  SQUARE_SIZE = 480
 
 
18
  MAX_SEED = np.iinfo(np.int32).max
19
  FIXED_FPS = 16
20
  MIN_FRAMES_MODEL = 8
21
  MAX_FRAMES_MODEL = 81
 
22
  MIN_DURATION = round(MIN_FRAMES_MODEL / FIXED_FPS, 1)
23
  MAX_DURATION = round(MAX_FRAMES_MODEL / FIXED_FPS, 1)
24
- default_negative_prompt = "..." # mesmo valor do arquivo original
25
 
26
- # === novo: importar e instanciar o serviço ===
 
 
 
 
 
 
27
  from aduc_framework.managers.wan_manager import WanManager
28
  wan_manager = WanManager()
29
 
30
- # (opcional) manter generate_end_frame e switch_to_upload_tab como estão
31
- # def generate_end_frame(...): ... # inalterado
32
- # def switch_to_upload_tab(): ... # inalterado
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- # Wrapper fino: a UI constrói images_condition_items e delega ao serviço
 
 
 
35
  def ui_generate_video(
36
  start_image_pil,
37
  start_frame_text,
@@ -47,14 +96,15 @@ def ui_generate_video(
47
  guidance_scale_2=1.0,
48
  seed=42,
49
  randomize_seed=False,
50
- progress=gr.Progress(track_tqdm=True)
51
  ):
52
- # Conversão robusta básica (UI “burra”, sem semântica adicional)
53
  def to_int_safe(v, default=0):
54
  try:
55
  return int(v)
56
  except:
57
  return default
 
58
  def to_float_safe(v, default=1.0):
59
  try:
60
  return float(v)
@@ -63,22 +113,36 @@ def ui_generate_video(
63
 
64
  images_condition_items = [
65
  [start_image_pil, to_int_safe(start_frame_text, 0), to_float_safe(start_peso, 1.0)],
66
- [end_image_pil, to_int_safe(end_frame_text, 0), to_float_safe(end_peso, 1.0)],
67
  ]
 
68
  return wan_manager.generate_video_from_conditions(
69
  images_condition_items=images_condition_items,
70
  prompt=prompt,
71
  negative_prompt=negative_prompt,
72
- duration_seconds=duration_seconds,
73
- steps=steps,
74
- guidance_scale=guidance_scale,
75
- guidance_scale_2=guidance_scale_2,
76
- seed=seed,
77
- randomize_seed=randomize_seed,
78
  )
79
 
80
- # === UI Gradio (adicionar frame/peso abaixo de cada imagem) ===
81
- css = ''' ... (mantém igual) ... '''
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
83
  gr.Markdown("# Wan 2.2 Aduca-sdr")
84
 
@@ -94,22 +158,33 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
94
  with gr.TabItem("Upload", id="upload_tab"):
95
  with gr.Column():
96
  end_image = gr.Image(type="pil", label="End Frame", sources=["upload", "clipboard"])
97
- end_frame_tb = gr.Textbox(label="End Frame (int)", value="80")
98
  end_peso_sl = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="End Peso")
99
  with gr.TabItem("Generate", id="generate_tab"):
100
  generate_5seconds = gr.Button("Generate scene 5 seconds in the future", elem_id="fivesec")
101
  gr.Markdown(
102
  "Generate a custom end-frame with an edit model like Nano Banana or Qwen Image Edit",
103
- elem_id="or_item"
104
  )
105
  prompt = gr.Textbox(label="Prompt", info="Describe the transition between the two images")
106
 
107
  with gr.Accordion("Advanced Settings", open=False):
108
- duration_seconds_input = gr.Slider(minimum=MIN_DURATION, maximum=MAX_DURATION, step=0.1, value=2.1, label="Video Duration (seconds)")
 
 
 
 
 
 
 
109
  negative_prompt_input = gr.Textbox(label="Negative Prompt", value=default_negative_prompt, lines=3)
110
  steps_slider = gr.Slider(minimum=1, maximum=30, step=1, value=8, label="Inference Steps")
111
- guidance_scale_input = gr.Slider(minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale - high noise")
112
- guidance_scale_2_input = gr.Slider(minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale - low noise")
 
 
 
 
113
  with gr.Row():
114
  seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
115
  randomize_seed_checkbox = gr.Checkbox(label="Randomize seed", value=True)
@@ -119,21 +194,23 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
119
  with gr.Column():
120
  output_video = gr.Video(label="Generated Video", autoplay=True)
121
 
122
- # Inputs/outputs atualizados para o wrapper
123
  ui_inputs = [
124
  start_image, start_frame_tb, start_peso_sl,
125
  end_image, end_frame_tb, end_peso_sl,
126
  prompt, negative_prompt_input, duration_seconds_input,
127
  steps_slider, guidance_scale_input, guidance_scale_2_input,
128
- seed_input, randomize_seed_checkbox
129
  ]
130
  ui_outputs = [output_video, seed_input]
131
 
132
  generate_button.click(fn=ui_generate_video, inputs=ui_inputs, outputs=ui_outputs)
133
 
134
- # Cadeia “5 seconds”: mantém a lógica, apenas troca a função final para ui_generate_video
135
  generate_5seconds.click(
136
- fn=switch_to_upload_tab, inputs=None, outputs=[tabs]
 
 
137
  ).then(
138
  fn=lambda img: generate_end_frame(
139
  img,
@@ -147,14 +224,28 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
147
  outputs=ui_outputs
148
  )
149
 
150
- # Examples permanecem; os campos de frame/peso usam defaults
151
- gr.Examples(
152
- examples=[
153
- ["frame_1.png", "frame_2.png", "the man speed motorcycle"],
154
- ["frame_1.png", "frame_3.png", "the man return on road motorcycle"],
155
- ],
156
- inputs=[start_image, end_image, prompt],
157
- outputs=ui_outputs,
158
- fn=ui_generate_video,
159
- cache_examples="lazy",
160
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import random
13
+ from gradio_client import Client, handle_file
14
 
15
+ # === Constantes (espelhando o app original) ===
16
  MODEL_ID = "Wan-AI/Wan2.2-I2V-A14B-Diffusers"
17
+
18
+ # Dimensões
19
  MAX_DIMENSION = 832
20
  MIN_DIMENSION = 480
21
  DIMENSION_MULTIPLE = 16
22
  SQUARE_SIZE = 480
23
+
24
+ # Geração
25
  MAX_SEED = np.iinfo(np.int32).max
26
  FIXED_FPS = 16
27
  MIN_FRAMES_MODEL = 8
28
  MAX_FRAMES_MODEL = 81
29
+
30
  MIN_DURATION = round(MIN_FRAMES_MODEL / FIXED_FPS, 1)
31
  MAX_DURATION = round(MAX_FRAMES_MODEL / FIXED_FPS, 1)
 
32
 
33
+ default_negative_prompt = (
34
+ "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,"
35
+ "JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,"
36
+ "手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走,过曝,"
37
+ )
38
+
39
+ # === Importa o serviço de geração (manager) ===
40
  from aduc_framework.managers.wan_manager import WanManager
41
  wan_manager = WanManager()
42
 
43
+ # === Utilidades de UI ===
44
+ def switch_to_upload_tab():
45
+ # Atualiza o Tabs existente para a aba "Upload"
46
+ return gr.Tabs.update(selected="upload_tab")
47
+
48
+ def generate_end_frame(start_img, gen_prompt, progress=gr.Progress(track_tqdm=True)):
49
+ """
50
+ Chama uma API Gradio externa para gerar uma imagem (end frame).
51
+ """
52
+ if start_img is None:
53
+ raise gr.Error("Please provide a Start Frame first.")
54
+
55
+ hf_token = os.getenv("HF_TOKEN")
56
+ if not hf_token:
57
+ raise gr.Error("HF_TOKEN not found in environment variables. Please set it in your Space secrets.")
58
+
59
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmpfile:
60
+ start_img.save(tmpfile.name)
61
+ tmp_path = tmpfile.name
62
+
63
+ progress(0.1, desc="Connecting to image generation API...")
64
+ client = Client("multimodalart/nano-banana")
65
+
66
+ progress(0.5, desc=f"Generating with prompt: '{gen_prompt}'...")
67
+ try:
68
+ result = client.predict(
69
+ prompt=gen_prompt,
70
+ images=[{"image": handle_file(tmp_path)}],
71
+ manual_token=hf_token,
72
+ api_name="/unified_image_generator",
73
+ )
74
+ finally:
75
+ try:
76
+ os.remove(tmp_path)
77
+ except:
78
+ pass
79
 
80
+ progress(1.0, desc="Done!")
81
+ return result
82
+
83
+ # Wrapper: a UI monta images_condition_items e delega ao serviço
84
  def ui_generate_video(
85
  start_image_pil,
86
  start_frame_text,
 
96
  guidance_scale_2=1.0,
97
  seed=42,
98
  randomize_seed=False,
99
+ progress=gr.Progress(track_tqdm=True),
100
  ):
101
+ # UI “burra”: apenas parses simples
102
  def to_int_safe(v, default=0):
103
  try:
104
  return int(v)
105
  except:
106
  return default
107
+
108
  def to_float_safe(v, default=1.0):
109
  try:
110
  return float(v)
 
113
 
114
  images_condition_items = [
115
  [start_image_pil, to_int_safe(start_frame_text, 0), to_float_safe(start_peso, 1.0)],
116
+ [end_image_pil, to_int_safe(end_frame_text, 0), to_float_safe(end_peso, 1.0)],
117
  ]
118
+
119
  return wan_manager.generate_video_from_conditions(
120
  images_condition_items=images_condition_items,
121
  prompt=prompt,
122
  negative_prompt=negative_prompt,
123
+ duration_seconds=float(duration_seconds),
124
+ steps=int(steps),
125
+ guidance_scale=float(guidance_scale),
126
+ guidance_scale_2=float(guidance_scale_2),
127
+ seed=int(seed),
128
+ randomize_seed=bool(randomize_seed),
129
  )
130
 
131
+ # === UI Gradio ===
132
+ css = '''
133
+ .fillable{max-width: 1100px !important}
134
+ .dark .progress-text {color: white}
135
+ #general_items{margin-top: 2em}
136
+ #group_all{overflow:visible}
137
+ #group_all .styler{overflow:visible}
138
+ #group_tabs .tabitem{padding: 0}
139
+ .tab-wrapper{margin-top: -33px;z-index: 999;position: absolute;width: 100%;background-color: var(--block-background-fill);padding: 0;}
140
+ #component-9-button{width: 50%;justify-content: center}
141
+ #component-11-button{width: 50%;justify-content: center}
142
+ #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)}
143
+ #fivesec{margin-top: 5em;margin-left: .5em;margin-right: .5em;width: calc(100% - 1em)}
144
+ '''
145
+
146
  with gr.Blocks(theme=gr.themes.Citrus(), css=css) as app:
147
  gr.Markdown("# Wan 2.2 Aduca-sdr")
148
 
 
158
  with gr.TabItem("Upload", id="upload_tab"):
159
  with gr.Column():
160
  end_image = gr.Image(type="pil", label="End Frame", sources=["upload", "clipboard"])
161
+ end_frame_tb = gr.Textbox(label="End Frame (int)", value=str(MAX_FRAMES_MODEL - 1))
162
  end_peso_sl = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=1.0, label="End Peso")
163
  with gr.TabItem("Generate", id="generate_tab"):
164
  generate_5seconds = gr.Button("Generate scene 5 seconds in the future", elem_id="fivesec")
165
  gr.Markdown(
166
  "Generate a custom end-frame with an edit model like Nano Banana or Qwen Image Edit",
167
+ elem_id="or_item",
168
  )
169
  prompt = gr.Textbox(label="Prompt", info="Describe the transition between the two images")
170
 
171
  with gr.Accordion("Advanced Settings", open=False):
172
+ duration_seconds_input = gr.Slider(
173
+ minimum=MIN_DURATION,
174
+ maximum=MAX_DURATION,
175
+ step=0.1,
176
+ value=2.1,
177
+ label="Video Duration (seconds)",
178
+ info=f"Clamped to model's {MIN_FRAMES_MODEL}-{MAX_FRAMES_MODEL} frames at {FIXED_FPS}fps.",
179
+ )
180
  negative_prompt_input = gr.Textbox(label="Negative Prompt", value=default_negative_prompt, lines=3)
181
  steps_slider = gr.Slider(minimum=1, maximum=30, step=1, value=8, label="Inference Steps")
182
+ guidance_scale_input = gr.Slider(
183
+ minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale - high noise"
184
+ )
185
+ guidance_scale_2_input = gr.Slider(
186
+ minimum=0.0, maximum=10.0, step=0.5, value=1.0, label="Guidance Scale - low noise"
187
+ )
188
  with gr.Row():
189
  seed_input = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
190
  randomize_seed_checkbox = gr.Checkbox(label="Randomize seed", value=True)
 
194
  with gr.Column():
195
  output_video = gr.Video(label="Generated Video", autoplay=True)
196
 
197
+ # Inputs/outputs para o wrapper
198
  ui_inputs = [
199
  start_image, start_frame_tb, start_peso_sl,
200
  end_image, end_frame_tb, end_peso_sl,
201
  prompt, negative_prompt_input, duration_seconds_input,
202
  steps_slider, guidance_scale_input, guidance_scale_2_input,
203
+ seed_input, randomize_seed_checkbox,
204
  ]
205
  ui_outputs = [output_video, seed_input]
206
 
207
  generate_button.click(fn=ui_generate_video, inputs=ui_inputs, outputs=ui_outputs)
208
 
209
+ # Cadeia “5 seconds”: alterna aba, gera end frame e dispara render
210
  generate_5seconds.click(
211
+ fn=switch_to_upload_tab,
212
+ inputs=None,
213
+ outputs=[tabs]
214
  ).then(
215
  fn=lambda img: generate_end_frame(
216
  img,
 
224
  outputs=ui_outputs
225
  )
226
 
227
+ # Exemplos opcionais (campos de frame/peso usam defaults atuais do componente)
228
+ # gr.Examples(
229
+ # examples=[
230
+ # ["poli_tower.png", "tower_takes_off.png", "the man turns around"],
231
+ # ["ugly_sonic.jpeg", "squatting_sonic.png", "the character dodges the missiles"],
232
+ # ["capyabara_zoomed.png", "capyabara.webp", "a dramatic dolly zoom"],
233
+ # ],
234
+ # inputs=[start_image, end_image, prompt],
235
+ # outputs=ui_outputs,
236
+ # fn=ui_generate_video,
237
+ # cache_examples="lazy",
238
+ # )
239
+
240
+ if __name__ == "__main__":
241
+ # Assets opcionais de exemplo mínimos
242
+ os.makedirs("examples", exist_ok=True)
243
+ try:
244
+ Image.new('RGB', (832, 480), color=(73, 109, 137)).save("examples/frame_1.png")
245
+ Image.new('RGB', (832, 480), color=(173, 109, 237)).save("examples/frame_2.png")
246
+ Image.new('RGB', (832, 480), color=(255, 255, 0)).save("examples/frame_3.png")
247
+ except:
248
+ pass
249
+
250
+ # Lança a app
251
+ app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)