x2XcarleX2x commited on
Commit
05aa834
·
verified ·
1 Parent(s): 8530ae2

Update app_wan.py

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