Spaces:
Paused
Paused
Upload 3 files
Browse files- app_wan.py +171 -0
- config.yaml +26 -9
- start.sh +1 -1
app_wan.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# demo_aduc_wan.py
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import torch
|
| 4 |
+
import logging
|
| 5 |
+
from PIL import Image
|
| 6 |
+
from typing import List, Dict, Any
|
| 7 |
+
|
| 8 |
+
# --- Importa os Pilares da nossa Arquitetatura ADUC ---
|
| 9 |
+
from aduc_framework.managers.vae_wan_manager import vae_wan_manager_singleton as VaeWan
|
| 10 |
+
from aduc_framework.managers.wan_manager import wan_manager_singleton as Wan
|
| 11 |
+
from aduc_framework.types import LatentConditioningItem
|
| 12 |
+
from aduc_framework.tools.video_encode_tool import video_encode_tool_singleton as VideoTool
|
| 13 |
+
|
| 14 |
+
# --- Configuração do Logging ---
|
| 15 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - [%(name)s] - %(message)s')
|
| 16 |
+
logger = logging.getLogger("ADUC_WAN_DEMO")
|
| 17 |
+
|
| 18 |
+
# --- Lógica Principal da Geração (Adaptada para Gradio) ---
|
| 19 |
+
|
| 20 |
+
def generate_aduc_video(
|
| 21 |
+
image1: Image.Image, strength1: float, frame_idx1: int,
|
| 22 |
+
image2: Image.Image, strength2: float, frame_idx2: int,
|
| 23 |
+
image3: Image.Image, strength3: float, frame_idx3: int,
|
| 24 |
+
motion_prompt: str,
|
| 25 |
+
progress=gr.Progress(track_tqdm=True)
|
| 26 |
+
):
|
| 27 |
+
"""
|
| 28 |
+
Função principal que orquestra a geração de vídeo e é chamada pela interface Gradio.
|
| 29 |
+
"""
|
| 30 |
+
try:
|
| 31 |
+
# --- 1. Validação e Montagem do Plano de Produção ---
|
| 32 |
+
progress(0, desc="[Diretor] Validando o plano de produção...")
|
| 33 |
+
|
| 34 |
+
production_plan = []
|
| 35 |
+
if image1: production_plan.append({"image": image1, "frame_index": int(frame_idx1), "strength": strength1})
|
| 36 |
+
if image2: production_plan.append({"image": image2, "frame_index": int(frame_idx2), "strength": strength2})
|
| 37 |
+
if image3: production_plan.append({"image": image3, "frame_index": int(frame_idx3), "strength": strength3})
|
| 38 |
+
|
| 39 |
+
if not production_plan:
|
| 40 |
+
raise gr.Error("Por favor, forneça pelo menos uma imagem para iniciar a geração.")
|
| 41 |
+
|
| 42 |
+
resolution = (480, 832) # Resolução padrão para o teste
|
| 43 |
+
|
| 44 |
+
# --- 2. Pré-Produção: Conversão para Latentes ---
|
| 45 |
+
progress(0.1, desc="[VaeWan] Convertendo imagens para o espaço latente...")
|
| 46 |
+
pil_images = [item["image"] for item in production_plan]
|
| 47 |
+
latent_tensors = VaeWan.encode_batch(pil_images, target_resolution=resolution)
|
| 48 |
+
|
| 49 |
+
# --- 3. Estruturação: Montando as Condições ADUC ---
|
| 50 |
+
progress(0.2, desc="[Diretor] Montando ordens de serviço (LatentConditioningItems)...")
|
| 51 |
+
conditioning_items = [
|
| 52 |
+
LatentConditioningItem(
|
| 53 |
+
latent_tensor=latent_tensors[i],
|
| 54 |
+
media_frame_number=item["frame_index"],
|
| 55 |
+
conditioning_strength=item["strength"]
|
| 56 |
+
) for i, item in enumerate(production_plan)
|
| 57 |
+
]
|
| 58 |
+
|
| 59 |
+
# --- 4. Produção: Geração pelo WanManager ---
|
| 60 |
+
progress(0.3, desc="[WanManager] Luz, Câmera, Ação! Gerando fragmento latente (pode levar um momento)...")
|
| 61 |
+
job_params = {
|
| 62 |
+
"conditioning_items_data": conditioning_items,
|
| 63 |
+
"motion_prompt": motion_prompt,
|
| 64 |
+
"height": resolution[0],
|
| 65 |
+
"width": resolution[1],
|
| 66 |
+
"video_total_frames": 81,
|
| 67 |
+
"num_inference_steps": 8,
|
| 68 |
+
}
|
| 69 |
+
video_latents, _ = Wan.generate_latent_fragment(**job_params)
|
| 70 |
+
|
| 71 |
+
# --- 5. Pós-Produção: Decodificação ---
|
| 72 |
+
progress(0.8, desc="[VaeWan] Decodificando o resultado final para pixels...")
|
| 73 |
+
video_pixels = VaeWan.decode(video_latents)
|
| 74 |
+
|
| 75 |
+
# --- 6. Salvamento ---
|
| 76 |
+
progress(0.9, desc="[VideoTool] Salvando o arquivo de vídeo final...")
|
| 77 |
+
output_path = "demo_aduc_wan_output.mp4"
|
| 78 |
+
VideoTool.save_video_from_tensor(video_pixels, path=output_path, fps=16)
|
| 79 |
+
|
| 80 |
+
progress(1.0, desc="Produção Concluída!")
|
| 81 |
+
return output_path
|
| 82 |
+
|
| 83 |
+
except Exception as e:
|
| 84 |
+
logger.error("Ocorreu um erro durante a geração do vídeo.", exc_info=True)
|
| 85 |
+
# Lança um erro que será exibido na interface do Gradio
|
| 86 |
+
raise gr.Error(f"Falha na geração: {e}")
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
# --- Construção da Interface Gráfica (UI) ---
|
| 90 |
+
|
| 91 |
+
with gr.Blocks(theme=gr.themes.Soft(), title="ADUC-SDR Wan Demo") as demo:
|
| 92 |
+
gr.Markdown(
|
| 93 |
+
"""
|
| 94 |
+
# 🎬 Demo ADUC-SDR com WanManager (Lightning)
|
| 95 |
+
### Bem-vindo, Mestre Deformes!
|
| 96 |
+
Esta interface permite controlar o especialista `WanManager` usando a metodologia ADUC.
|
| 97 |
+
Faça o upload de até 3 keyframes, defina suas posições e forças no vídeo e escreva um prompt para guiar a animação.
|
| 98 |
+
"""
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
with gr.Row():
|
| 102 |
+
with gr.Column(scale=1):
|
| 103 |
+
gr.Markdown("### Keyframe 1 (Inicial)")
|
| 104 |
+
img1 = gr.Image(type="pil", label="Imagem de Início")
|
| 105 |
+
strength1 = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, label="Força (Strength)", info="1.0 = o vídeo começa EXATAMENTE nesta imagem.")
|
| 106 |
+
frame_idx1 = gr.Number(value=0, label="Índice do Frame", info="Em que ponto do vídeo este frame aparece (0 é o início).", precision=0)
|
| 107 |
+
|
| 108 |
+
with gr.Column(scale=1):
|
| 109 |
+
gr.Markdown("### Keyframe 2 (Intermediário)")
|
| 110 |
+
img2 = gr.Image(type="pil", label="Imagem do Meio")
|
| 111 |
+
strength2 = gr.Slider(minimum=0.0, maximum=1.0, value=0.6, label="Força (Strength)", info="< 1.0 = o vídeo é INFLUENCIADO por esta imagem.")
|
| 112 |
+
frame_idx2 = gr.Number(value=40, label="Índice do Frame", info="O padrão é o meio do vídeo (frame 40 de 81).", precision=0)
|
| 113 |
+
|
| 114 |
+
with gr.Column(scale=1):
|
| 115 |
+
gr.Markdown("### Keyframe 3 (Final)")
|
| 116 |
+
img3 = gr.Image(type="pil", label="Imagem Final")
|
| 117 |
+
strength3 = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, label="Força (Strength)", info="1.0 = o vídeo termina EXATAMENTE nesta imagem.")
|
| 118 |
+
frame_idx3 = gr.Number(value=80, label="Índice do Frame", info="O padrão é o último frame do vídeo.", precision=0)
|
| 119 |
+
|
| 120 |
+
with gr.Row():
|
| 121 |
+
motion_prompt_input = gr.Textbox(
|
| 122 |
+
label="📝 Prompt de Movimento",
|
| 123 |
+
placeholder="Ex: a beautiful cinematic shot of a majestic landscape changing seasons",
|
| 124 |
+
lines=2,
|
| 125 |
+
scale=3
|
| 126 |
+
)
|
| 127 |
+
generate_btn = gr.Button("🎬 Gerar Vídeo", variant="primary", scale=1)
|
| 128 |
+
|
| 129 |
+
with gr.Row():
|
| 130 |
+
output_video = gr.Video(label="Resultado da Produção", interactive=False)
|
| 131 |
+
|
| 132 |
+
# Conecta o botão à função de geração
|
| 133 |
+
generate_btn.click(
|
| 134 |
+
fn=generate_aduc_video,
|
| 135 |
+
inputs=[
|
| 136 |
+
img1, strength1, frame_idx1,
|
| 137 |
+
img2, strength2, frame_idx2,
|
| 138 |
+
img3, strength3, frame_idx3,
|
| 139 |
+
motion_prompt_input
|
| 140 |
+
],
|
| 141 |
+
outputs=[output_video]
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
gr.Examples(
|
| 145 |
+
examples=[
|
| 146 |
+
["examples/frame_1.png", 1.0, 0, "examples/frame_2.png", 0.7, 40, "examples/frame_3.png", 1.0, 80, "A slow zoom out revealing a vast alien desert at sunset"],
|
| 147 |
+
],
|
| 148 |
+
inputs=[img1, strength1, frame_idx1, img2, strength2, frame_idx2, img3, strength3, frame_idx3, motion_prompt_input],
|
| 149 |
+
outputs=[output_video],
|
| 150 |
+
fn=generate_aduc_video,
|
| 151 |
+
cache_examples=False, # Desativa o cache para sempre rodar a geração
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
if __name__ == "__main__":
|
| 155 |
+
# Cria a pasta e as imagens de exemplo se não existirem
|
| 156 |
+
if not os.path.exists("examples"):
|
| 157 |
+
os.makedirs("examples")
|
| 158 |
+
try:
|
| 159 |
+
Image.new('RGB', (832, 480), color = (73, 109, 137)).save("examples/frame_1.png")
|
| 160 |
+
Image.new('RGB', (832, 480), color = (173, 109, 237)).save("examples/frame_2.png")
|
| 161 |
+
Image.new('RGB', (832, 480), color = (255, 255, 0)).save("examples/frame_3.png")
|
| 162 |
+
except:
|
| 163 |
+
pass # Evita que o app quebre se não tiver permissão de escrita
|
| 164 |
+
|
| 165 |
+
os.makedirs(WORKSPACE_DIR, exist_ok=True)
|
| 166 |
+
logger.info("Aplicação Gradio pronta. Lançando interface...")
|
| 167 |
+
demo.launch(
|
| 168 |
+
debug=True
|
| 169 |
+
server_name="0.0.0.0",
|
| 170 |
+
server_port=int(os.getenv("PORT", "7860")),
|
| 171 |
+
)
|
config.yaml
CHANGED
|
@@ -40,15 +40,32 @@ specialists:
|
|
| 40 |
# Alterado para usar o modelo 0.9.8-dev.
|
| 41 |
config_file: "ltxv-13b-0.9.8-distilled.yaml"
|
| 42 |
enable_prompt_enhancement: false
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
|
| 54 |
mmaudio:
|
|
|
|
| 40 |
# Alterado para usar o modelo 0.9.8-dev.
|
| 41 |
config_file: "ltxv-13b-0.9.8-distilled.yaml"
|
| 42 |
enable_prompt_enhancement: false
|
| 43 |
+
lora:
|
| 44 |
+
# Exemplo de como adicionar LoRAs para estilização.
|
| 45 |
+
- model_id: "Lightricks/LTX-Video-ICLoRA-pose-13b-0.9.7"
|
| 46 |
+
weight: 0.5
|
| 47 |
+
- model_id: "Lightricks/LTX-Video-ICLoRA-depth-13b-0.9.7"
|
| 48 |
+
weight: 0.3
|
| 49 |
+
- model_id: "Lightricks/LTX-Video-ICLoRA-detailer-13b-0.9.8"
|
| 50 |
+
weight: 0.6
|
| 51 |
+
|
| 52 |
+
vae_wan: # O VAE dedicado e treinado especificamente para o Wan2.2.
|
| 53 |
+
gpus_required: 1 # Aloca 1 GPU para manter o VAE do Wan "quente".
|
| 54 |
+
model_id: "Wan-AI/Wan2.2-I2V-A14B-Diffusers"
|
| 55 |
+
|
| 56 |
+
wan:
|
| 57 |
+
gpus_required: 1 # Aloca 1 GPU para o worker do WanManager.
|
| 58 |
+
model_id: "Wan-AI/Wan2.2-I2V-A14B-Diffusers"
|
| 59 |
+
optimized_model_id: "cbensimon/Wan2.2-I2V-A14B-bf16-Diffusers"
|
| 60 |
+
lora_repo: "Kijai/WanVideo_comfy"
|
| 61 |
+
lora_filename: "Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank128_bf16.safetensors"
|
| 62 |
+
# Parâmetros padrão para o modo Lightning (8 passos).
|
| 63 |
+
default_height: 480
|
| 64 |
+
default_width: 832
|
| 65 |
+
default_frames: 81
|
| 66 |
+
guidance_scale: 1.0
|
| 67 |
+
guidance_scale_2: 1.0
|
| 68 |
+
inference_steps: 8
|
| 69 |
|
| 70 |
|
| 71 |
mmaudio:
|
start.sh
CHANGED
|
@@ -61,4 +61,4 @@ echo "✅ Modelos prontos."
|
|
| 61 |
# --- Etapa 3: Iniciar a Aplicação Principal ---
|
| 62 |
echo "🎬 Iniciando a aplicação ($1)..."
|
| 63 |
echo "🚀 Iniciando app.py..."
|
| 64 |
-
python3 /app/
|
|
|
|
| 61 |
# --- Etapa 3: Iniciar a Aplicação Principal ---
|
| 62 |
echo "🎬 Iniciando a aplicação ($1)..."
|
| 63 |
echo "🚀 Iniciando app.py..."
|
| 64 |
+
python3 /app/app_wan.py --listen --port ${PORT:-7860}
|