carlex3321 commited on
Commit
5ba1f0a
·
verified ·
1 Parent(s): 08508e4

Update app_vince.py

Browse files
Files changed (1) hide show
  1. app_vince.py +132 -145
app_vince.py CHANGED
@@ -1,194 +1,181 @@
1
  #!/usr/bin/env python3
2
  """
3
- VINCIE Service UI (Gradio) - Versão Final com Suporte a Multi-GPU
4
-
5
- - Interface com 3 abas: Edição Multi-Turno, Texto-para-Vídeo e Composição Multi-Conceito.
6
- - Controles avançados na UI, incluindo seleção de número de GPUs e tamanho do lote (batch size).
7
- - Configuração automática no carregamento da aplicação.
8
- - Projetado para hardware de ponta como 8x L40S.
9
- - Modelo funcional de referência: ByteDance-Seed/VINCIE.
10
- - Interface desenvolvida por Carlex ([email protected]).
11
  """
12
 
13
  import os
14
  from pathlib import Path
15
  from typing import List, Tuple, Optional
 
16
  import gradio as gr
17
 
18
- # Adapte este import para o layout do seu projeto, se necessário.
19
- from services.vincie import VincieService
 
 
 
 
 
20
 
21
- # --- Instanciação do Serviço e Constantes ---
22
  svc = VincieService()
23
- DEFAULT_NEGATIVE_PROMPT = "Worst quality, Normal quality, Low quality, Low res, Blurry, Jpeg artifacts, Grainy, text, logo, watermark, banner, extra digits, signature, subtitling, Bad anatomy, Bad proportions, Deformed, Disconnected limbs, Disfigured, Extra arms, Extra limbs, Extra hands, Fused fingers, Gross proportions, Long neck, Malformed limbs, Mutated, Mutated hands, Mutated limbs, Missing arms, Missing fingers, Poorly drawn hands, Poorly drawn face, Nsfw, Uncensored, Cleavage, Nude, Nipples, Overexposed, Plain background, Grainy, Underexposed, Deformed structures"
24
 
25
- # --- Funções Helper ---
26
  def setup_auto() -> str:
27
  """
28
- Executa uma configuração idempotente no carregamento da interface.
29
- Retorna uma string de status para a UI.
 
 
30
  """
31
  try:
32
  svc.ensure_repo()
33
  svc.ensure_model()
34
- return "Configuração concluída com sucesso: repositório e checkpoint estão prontos."
 
 
 
35
  except Exception as e:
36
- # Fornece um feedback de erro mais detalhado para depuração
37
- import traceback
38
- print(traceback.format_exc())
39
- return f"A configuração encontrou um erro: {e}"
40
 
41
  def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
42
  """
43
- Enumera as imagens resultantes e o vídeo mais recente de um diretório de saída.
 
 
 
 
 
 
 
 
 
44
  """
45
  img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
46
- # Usa rglob para encontrar imagens em subdiretórios e ordena por tempo de modificação
47
- images = sorted(
48
- [p for pat in img_globs for p in out_dir.rglob(pat)],
49
- key=lambda p: p.stat().st_mtime
50
- )
51
- image_paths = [str(p) for p in images[-max_images:]]
52
  videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
53
  video_path = str(videos[-1]) if videos else None
54
  return image_paths, video_path
55
 
56
- # --- Funções Handler da UI (com todos os parâmetros) ---
57
- def ui_multi_turn(input_image, turns_text, negative_prompt, seed, steps, cfg_scale, resolution, use_vae_slicing, num_gpus, batch_size):
58
- """Handler para a aba de Edição Multi-Turno."""
59
- if not input_image:
60
- return [], None, "Por favor, forneça uma imagem de entrada."
 
 
 
 
 
 
 
 
 
61
  if not turns_text or not turns_text.strip():
62
- return [], None, "Por favor, forneça as instruções de edição (uma por linha)."
63
 
64
  turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
65
  try:
66
- out_dir = svc.multi_turn_edit(
67
- input_image, turns,
68
- negative_prompt=negative_prompt, seed=int(seed), steps=int(steps),
69
- cfg_scale=float(cfg_scale), resolution=int(resolution), use_vae_slicing=use_vae_slicing,
70
- num_gpus=int(num_gpus), batch_size=int(batch_size)
71
- )
72
- imgs, vid = _list_media(Path(out_dir))
73
- return imgs, vid, f"Saídas salvas em: {out_dir}"
74
  except Exception as e:
75
- import traceback
76
- print(traceback.format_exc())
77
- return [], None, f"Erro na geração: {e}"
78
 
79
- def ui_text_to_video(input_image, prompt, negative_prompt, seed, steps, cfg_scale, resolution, fps, use_vae_slicing, num_gpus, batch_size):
80
- """Handler para a aba de Texto-para-Vídeo."""
81
- if not input_image:
82
- return None, "Por favor, forneça uma imagem de entrada (frame inicial)."
83
- if not prompt or not prompt.strip():
84
- return None, "Por favor, forneça um prompt para o vídeo."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
- try:
87
- out_dir = svc.text_to_video(
88
- input_image, prompt,
89
- negative_prompt=negative_prompt, seed=int(seed), steps=int(steps),
90
- cfg_scale=float(cfg_scale), resolution=int(resolution), fps=int(fps), use_vae_slicing=use_vae_slicing,
91
- num_gpus=int(num_gpus), batch_size=int(batch_size)
92
- )
93
- _, vid = _list_media(Path(out_dir))
94
- return vid, f"Vídeo salvo em: {out_dir}"
95
- except Exception as e:
96
- import traceback
97
- print(traceback.format_exc())
98
- return None, f"Erro na geração: {e}"
99
-
100
- def ui_multi_concept(files, descs_text, final_prompt):
101
- """Handler para a aba de Composição Multi-Conceito."""
102
- if not files: return [], None, "Por favor, faça o upload das imagens de conceito."
103
- if not descs_text: return [], None, "Por favor, forneça as descrições (uma por linha)."
104
- if not final_prompt: return [], None, "Por favor, forneça um prompt final."
105
-
106
  descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
107
- if len(descs) != len(files): return [], None, f"O número de descrições ({len(descs)}) deve ser igual ao de imagens ({len(files)})."
 
108
 
109
  try:
110
  out_dir = svc.multi_concept_compose(files, descs, final_prompt)
111
- imgs, vid = _list_media(Path(out_dir))
112
- return imgs, vid, f"Saídas salvas em: {out_dir}"
113
  except Exception as e:
114
- import traceback
115
- print(traceback.format_exc())
116
- return [], None, f"Erro na geração: {e}"
117
-
118
- # --- Definição da Interface Gradio Completa ---
119
- with gr.Blocks(title="VINCIE Service", theme=gr.themes.Soft()) as demo:
120
- gr.Markdown("# VINCIE Service — Geração Distribuída com Controles Avançados")
121
- gr.Markdown("- **Interface por:** Carlex ([email protected] | GitHub: carlex22)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  with gr.Row():
123
- setup_out = gr.Textbox(label="Status da Configuração", interactive=False)
124
 
125
- with gr.Tab("Edição Multi-Turno"):
126
- with gr.Row():
127
- img_mt = gr.Image(type="filepath", label="Imagem de Entrada")
128
- with gr.Column():
129
- turns_mt = gr.Textbox(lines=5, label="Instruções de Edição (uma por linha)", placeholder="Ex: adicione um chapéu azul\nagora, mude o fundo para uma praia")
130
- with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
131
- with gr.Row():
132
- num_gpus_mt = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
133
- batch_size_mt = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
134
- resolution_mt = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512, info="Maior resolução exige mais VRAM e tempo.")
135
- use_vae_slicing_mt = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
136
- neg_prompt_mt = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
137
- seed_mt = gr.Number(label="Seed (Semente)", value=1, precision=0)
138
- steps_mt = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50, info="Menos passos = mais rápido.")
139
- cfg_mt = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5, info="Quão forte o modelo segue o prompt.")
140
- run_mt = gr.Button("Executar Edição Multi-Turno", variant="primary")
141
- gallery_mt = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
142
- video_mt = gr.Video(label="Vídeo da Sequência (se disponível)")
143
- status_mt = gr.Textbox(label="Status da Saída", interactive=False)
144
- run_mt.click(ui_multi_turn,
145
- inputs=[img_mt, turns_mt, neg_prompt_mt, seed_mt, steps_mt, cfg_mt, resolution_mt, use_vae_slicing_mt, num_gpus_mt, batch_size_mt],
146
- outputs=[gallery_mt, video_mt, status_mt])
147
-
148
- with gr.Tab("Texto-para-Vídeo"):
149
- with gr.Row():
150
- img_vid = gr.Image(type="filepath", label="Frame Inicial")
151
- with gr.Column():
152
- prompt_vid = gr.Textbox(lines=2, label="Prompt do Vídeo", placeholder="Ex: um gato andando pela sala")
153
- with gr.Accordion("Configurações Avançadas e de Desempenho", open=True):
154
- with gr.Row():
155
- num_gpus_vid = gr.Slider(label="Número de GPUs", minimum=1, maximum=8, step=1, value=8, info="Use >1 para acelerar a geração com torchrun.")
156
- batch_size_vid = gr.Number(label="Batch Size por GPU", value=1, precision=0, info="Para Multi-GPU, o lote total será (GPUs x Batch Size).")
157
- resolution_vid = gr.Slider(label="Resolução", minimum=256, maximum=1024, step=128, value=512)
158
- fps_vid = gr.Slider(label="Frames por Segundo (FPS)", minimum=1, maximum=24, step=1, value=2)
159
- use_vae_slicing_vid = gr.Checkbox(label="Usar VAE Slicing (Economiza VRAM)", value=True)
160
- neg_prompt_vid = gr.Textbox(lines=3, label="Prompt Negativo", value=DEFAULT_NEGATIVE_PROMPT)
161
- seed_vid = gr.Number(label="Seed (Semente)", value=1, precision=0)
162
- steps_vid = gr.Slider(label="Passos de Inferência", minimum=10, maximum=100, step=1, value=50)
163
- cfg_vid = gr.Slider(label="Escala de Orientação (CFG)", minimum=1.0, maximum=20.0, step=0.5, value=7.5)
164
- run_vid = gr.Button("Gerar Vídeo", variant="primary")
165
- video_vid = gr.Video(label="Vídeo Gerado")
166
- status_vid = gr.Textbox(label="Status da Saída", interactive=False)
167
- run_vid.click(ui_text_to_video,
168
- inputs=[img_vid, prompt_vid, neg_prompt_vid, seed_vid, steps_vid, cfg_vid, resolution_vid, fps_vid, use_vae_slicing_vid, num_gpus_vid, batch_size_vid],
169
- outputs=[video_vid, status_vid])
170
-
171
- with gr.Tab("Composição Multi-Conceito"):
172
- gr.Markdown("Nota: A composição multi-conceito está atualmente configurada para rodar em uma única GPU para garantir estabilidade.")
173
  with gr.Row():
174
- with gr.Column(scale=1):
175
- files_mc = gr.File(file_count="multiple", type="filepath", label="1. Imagens de Conceito")
176
- with gr.Column(scale=2):
177
- descs_mc = gr.Textbox(lines=5, label="2. Descrições (uma por linha, na mesma ordem)", placeholder="Ex: <IMG1>: uma foto de um pai\n<IMG2>: uma foto de uma mãe...")
178
- final_prompt_mc = gr.Textbox(lines=3, label="3. Prompt Final de Composição", placeholder="Ex: Baseado em <IMG0>, <IMG1>..., uma família sorrindo em um retrato...")
179
- run_mc = gr.Button("Executar Composição", variant="primary")
180
- gallery_mc = gr.Gallery(label="Imagens Geradas", columns=4, height="auto")
181
- video_mc = gr.Video(label="Vídeo da Sequência (se disponível)")
182
- status_mc = gr.Textbox(label="Status da Saída", interactive=False)
183
- run_mc.click(ui_multi_concept,
184
- inputs=[files_mc, descs_mc, final_prompt_mc],
185
- outputs=[gallery_mc, video_mc, status_mc])
186
-
187
- # Gatilho de configuração automática no carregamento
 
 
 
 
 
 
 
 
 
188
  demo.load(fn=setup_auto, outputs=setup_out)
189
 
190
  if __name__ == "__main__":
191
  demo.launch(
192
  server_name="0.0.0.0",
193
  server_port=int(os.getenv("PORT", "7860")),
194
- )
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ VINCIE Service UI (Gradio)
4
+ - Automatic setup runs on app load (no manual setup button).
5
+ - Multi-turn editing and multi-concept composition front-end.
6
+ - Designed for NVIDIA L40S (SM 8.9) environments aligned with CUDA 12.x.
7
+ - Functional reference: ByteDance-Seed/VINCIE.
8
+ - Space and Docker developed by Carlex (contact below).
 
 
9
  """
10
 
11
  import os
12
  from pathlib import Path
13
  from typing import List, Tuple, Optional
14
+
15
  import gradio as gr
16
 
17
+ # Adapt this import to the project layout.
18
+ # Provide a VincieService with:
19
+ # - ensure_repo(): clones/updates upstream repo if missing
20
+ # - ensure_model(): downloads/validates checkpoints to /app/ckpt/VINCIE-3B
21
+ # - multi_turn_edit(image_path: str, turns: List[str]) -> str (output dir)
22
+ # - multi_concept_compose(files: List[str], descs: List[str], final_prompt: str) -> str (output dir)
23
+ from services.vincie import VincieService # change path if needed
24
 
25
+ # Instantiate the service (defaults to /app/VINCIE and /app/ckpt/VINCIE-3B)
26
  svc = VincieService()
 
27
 
28
+
29
  def setup_auto() -> str:
30
  """
31
+ Run an idempotent setup on interface load:
32
+ - Ensure the upstream repository is present and ready.
33
+ - Ensure the model checkpoint is downloaded and ready.
34
+ Returns an English status string for the UI.
35
  """
36
  try:
37
  svc.ensure_repo()
38
  svc.ensure_model()
39
+ return (
40
+ "Setup completed successfully: repository and checkpoint are ready "
41
+ "for inference on an NVIDIA L40S environment."
42
+ )
43
  except Exception as e:
44
+ return f"Setup encountered an error: {e}"
45
+
 
 
46
 
47
  def _list_media(out_dir: Path, max_images: int = 24) -> Tuple[List[str], Optional[str]]:
48
  """
49
+ Enumerate resulting images and the most recent video from an output directory.
50
+
51
+ Args:
52
+ out_dir: Path to the directory where the service wrote its results.
53
+ max_images: Upper bound on how many images to surface in the gallery.
54
+
55
+ Returns:
56
+ A tuple (images, video) where:
57
+ - images is a list of file paths to images sorted by modified time,
58
+ - video is the path to the latest .mp4 if found, otherwise None.
59
  """
60
  img_globs = ("*.png", "*.jpg", "*.jpeg", "*.webp")
61
+ images: List[Path] = []
62
+ for pat in img_globs:
63
+ images += list(out_dir.rglob(pat))
64
+ images = sorted(images, key=lambda p: p.stat().st_mtime)
65
+ image_paths = [str(p) for p in images[-max_images:]] if images else []
 
66
  videos = sorted(out_dir.rglob("*.mp4"), key=lambda p: p.stat().st_mtime)
67
  video_path = str(videos[-1]) if videos else None
68
  return image_paths, video_path
69
 
70
+
71
+ def ui_multi_turn(input_image: Optional[str], turns_text: Optional[str]):
72
+ """
73
+ Multi-turn image editing entrypoint for the UI.
74
+
75
+ Args:
76
+ input_image: Path to a single input image on disk.
77
+ turns_text: User-provided editing turns, one instruction per line.
78
+
79
+ Returns:
80
+ (gallery, video, status) for Gradio components.
81
+ """
82
+ if not input_image or not str(input_image).strip():
83
+ return [], None, "Please provide an input image."
84
  if not turns_text or not turns_text.strip():
85
+ return [], None, "Please provide edit turns (one per line)."
86
 
87
  turns = [ln.strip() for ln in turns_text.splitlines() if ln.strip()]
88
  try:
89
+ out_dir = svc.multi_turn_edit(input_image, turns)
 
 
 
 
 
 
 
90
  except Exception as e:
91
+ return [], None, f"Generation error: {e}"
 
 
92
 
93
+ imgs, vid = _list_media(Path(out_dir))
94
+ status = f"Outputs saved to: {out_dir}"
95
+ return imgs, vid, status
96
+
97
+
98
+ def ui_multi_concept(files: Optional[List[str]], descs_text: Optional[str], final_prompt: Optional[str]):
99
+ """
100
+ Multi-concept composition entrypoint for the UI.
101
+
102
+ Args:
103
+ files: List of paths to concept images on disk.
104
+ descs_text: Per-image descriptions (one line per image, in the same order).
105
+ final_prompt: A final composition prompt that aggregates the concepts.
106
+
107
+ Returns:
108
+ (gallery, video, status) for Gradio components.
109
+ """
110
+ if not files:
111
+ return [], None, "Please upload concept images."
112
+ if not descs_text or not descs_text.strip():
113
+ return [], None, "Please provide descriptions (one per line)."
114
+ if not final_prompt or not final_prompt.strip():
115
+ return [], None, "Please provide a final prompt."
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  descs = [ln.strip() for ln in descs_text.splitlines() if ln.strip()]
118
+ if len(descs) != len(files):
119
+ return [], None, f"Descriptions count ({len(descs)}) must match images count ({len(files)})."
120
 
121
  try:
122
  out_dir = svc.multi_concept_compose(files, descs, final_prompt)
 
 
123
  except Exception as e:
124
+ return [], None, f"Generation error: {e}"
125
+
126
+ imgs, vid = _list_media(Path(out_dir))
127
+ status = f"Outputs saved to: {out_dir}"
128
+ return imgs, vid, status
129
+
130
+
131
+ with gr.Blocks(title="VINCIE Service") as demo:
132
+ # Header and credits
133
+ gr.Markdown(
134
+ "\n".join(
135
+ [
136
+ "# VINCIE Service — Multi-turn Editing and Multi-concept Composition",
137
+ "- Automatic setup runs at startup; setup status appears below.",
138
+ "- Hardware requirement: NVIDIA L40S (SM 8.9) is recommended for this build.",
139
+ "- Functional upstream model: ByteDance-Seed/VINCIE (see project repository).",
140
+ "- Space and Docker were developed by Carlex.",
141
+ "- Contact: Email: [email protected] | GitHub: carlex22",
142
+ ]
143
+ )
144
+ )
145
+
146
  with gr.Row():
147
+ setup_out = gr.Textbox(label="Setup Status", interactive=False)
148
 
149
+ with gr.Tab("Multi-turn Editing"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  with gr.Row():
151
+ img = gr.Image(type="filepath", label="Input image")
152
+ turns = gr.Textbox(lines=8, label="Turns (one per line)")
153
+ run1 = gr.Button("Run")
154
+ out_gallery = gr.Gallery(label="Images", columns=4, height="auto")
155
+ out_video = gr.Video(label="Video (if available)")
156
+ out_status = gr.Textbox(label="Output", interactive=False)
157
+ run1.click(ui_multi_turn, inputs=[img, turns], outputs=[out_gallery, out_video, out_status])
158
+
159
+ with gr.Tab("Multi-concept Composition"):
160
+ files = gr.File(file_count="multiple", type="filepath", label="Concept images")
161
+ descs = gr.Textbox(lines=8, label="Descriptions (one per line, same order as images)")
162
+ final_prompt = gr.Textbox(lines=2, label="Final prompt")
163
+ run2 = gr.Button("Run")
164
+ out_gallery2 = gr.Gallery(label="Images", columns=4, height="auto")
165
+ out_video2 = gr.Video(label="Video (if available)")
166
+ out_status2 = gr.Textbox(label="Output", interactive=False)
167
+ run2.click(
168
+ ui_multi_concept,
169
+ inputs=[files, descs, final_prompt],
170
+ outputs=[out_gallery2, out_video2, out_status2],
171
+ )
172
+
173
+ # Auto-setup on load (no manual button)
174
  demo.load(fn=setup_auto, outputs=setup_out)
175
 
176
  if __name__ == "__main__":
177
  demo.launch(
178
  server_name="0.0.0.0",
179
  server_port=int(os.getenv("PORT", "7860")),
180
+ allowed_paths=["/app/outputs", "/app/ckpt"],
181
+ )