Carlexxx commited on
Commit
b49b2c7
·
verified ·
1 Parent(s): 6ea9267

Update dreamo_helpers.py

Browse files
Files changed (1) hide show
  1. dreamo_helpers.py +99 -281
dreamo_helpers.py CHANGED
@@ -1,288 +1,106 @@
1
- # --- app.py (O Painel de Controle do Maestro - Gestão de Memória Restaurada) ---
2
- # By Carlex & Gemini & DreamO
3
 
4
- # --- Ato 1: A Convocação da Orquestra (Importações) ---
5
- import gradio as gr
6
- import torch
7
  import os
8
- import yaml
9
- from PIL import Image
10
- import shutil
11
- import gc
12
- import subprocess
13
- import math
14
- import google.generativeai as genai
15
  import numpy as np
16
- import imageio
17
- from pathlib import Path
18
  import huggingface_hub
19
- import json
20
-
21
- # --- Músicos (Implementações Externas) ---
22
- from inference import create_ltx_video_pipeline, load_image_to_tensor_with_resize_and_crop, seed_everething, calculate_padding
23
- from ltx_video.pipelines.pipeline_ltx_video import ConditioningItem
24
- from dreamo_helpers import dreamo_generator_singleton
25
-
26
- # --- Ato 2: A Preparação do Palco (Configurações) ---
27
- config_file_path = "configs/ltxv-13b-0.9.8-distilled.yaml"
28
- with open(config_file_path, "r") as file:
29
- PIPELINE_CONFIG_YAML = yaml.safe_load(file)
30
-
31
- LTX_REPO = "Lightricks/LTX-Video"
32
- models_dir = "downloaded_models_gradio_cpu_init"
33
- Path(models_dir).mkdir(parents=True, exist_ok=True)
34
- WORKSPACE_DIR = "aduc_workspace"
35
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
36
-
37
- VIDEO_FPS = 30
38
- VIDEO_DURATION_SECONDS = 4
39
- VIDEO_TOTAL_FRAMES = VIDEO_DURATION_SECONDS * VIDEO_FPS
40
-
41
- # <<<< INICIALIZAÇÃO NA CPU >>>>
42
- print("Baixando e criando pipelines LTX na CPU...")
43
- distilled_model_actual_path = huggingface_hub.hf_hub_download(repo_id=LTX_REPO, filename=PIPELINE_CONFIG_YAML["checkpoint_path"], local_dir=models_dir, local_dir_use_symlinks=False)
44
- pipeline_instance = create_ltx_video_pipeline(ckpt_path=distilled_model_actual_path, precision=PIPELINE_CONFIG_YAML["precision"], text_encoder_model_name_or_path=PIPELINE_CONFIG_YAML["text_encoder_model_name_or_path"], sampler=PIPELINE_CONFIG_YAML["sampler"], device='cpu')
45
- print("Modelos LTX prontos (na CPU).")
46
-
47
- # --- Ato 3: As Partituras dos Músicos (Funções) ---
48
-
49
- def get_storyboard_from_director(num_fragments: int, prompt: str, initial_image_path: str, progress=gr.Progress()):
50
- progress(0.5, desc="[Diretor Gemini] Criando o storyboard...")
51
- if not initial_image_path: raise gr.Error("Por favor, forneça uma imagem de referência inicial.")
52
- if not GEMINI_API_KEY: raise gr.Error("Chave da API Gemini não configurada!")
53
- genai.configure(api_key=GEMINI_API_KEY)
54
- try:
55
- with open("prompts/director_storyboard_v2.txt", "r", encoding="utf-8") as f: template = f.read()
56
- except FileNotFoundError: raise gr.Error("'prompts/director_storyboard_v2.txt' não encontrado!")
57
- director_prompt = template.format(user_prompt=prompt, num_fragments=int(num_fragments))
58
- model = genai.GenerativeModel('gemini-2.0-flash')
59
- img = Image.open(initial_image_path)
60
- response = model.generate_content([director_prompt, img])
61
- try:
62
- cleaned_response = response.text.strip().replace("```json", "").replace("```", "")
63
- storyboard_data = json.loads(cleaned_response)
64
- return storyboard_data.get("storyboard", [])
65
- except Exception as e:
66
- raise gr.Error(f"O Diretor retornou uma resposta inesperada. Erro: {e}")
67
-
68
- def run_keyframe_generation(storyboard, ref_img_path_1, ref_img_path_2, ref_task_1, ref_task_2):
69
- if not storyboard: raise gr.Error("Nenhum roteiro para gerar imagens-chave.")
70
- if not ref_img_path_1: raise gr.Error("A Referência 1 é obrigatória.")
71
-
72
- keyframe_paths, log_history = [], ""
73
- try:
74
- dreamo_generator_singleton.to_gpu()
75
- for i, prompt in enumerate(storyboard):
76
- log_message = f"Pintando Cena {i+1}/{len(storyboard)} com DreamO..."
77
- log_history += log_message + "\n"
78
- yield {keyframe_log_output: gr.update(value=log_history)}
79
-
80
- output_path = os.path.join(WORKSPACE_DIR, f"keyframe_image_{i+1}.png")
81
-
82
- image = dreamo_generator_singleton.generate_image(
83
- ref_image1_np=np.array(Image.open(ref_img_path_1).convert("RGB")) if ref_img_path_1 else None,
84
- ref_image2_np=np.array(Image.open(ref_img_path_2).convert("RGB")) if ref_img_path_2 else None,
85
- ref_task1=ref_task_1, ref_task2=ref_task_2,
86
- prompt=prompt
87
- )
88
- image.save(output_path)
89
- keyframe_paths.append(output_path)
90
-
91
- log_message = f"Cena {i+1} pintada."
92
- log_history += log_message + "\n"
93
- yield {keyframe_log_output: gr.update(value=log_history), keyframe_gallery_output: gr.update(value=keyframe_paths), keyframe_images_state: keyframe_paths}
94
- finally:
95
- dreamo_generator_singleton.to_cpu()
96
 
97
- log_history += "\nPintura de todas as cenas concluída!"
98
- yield {keyframe_log_output: gr.update(value=log_history)}
99
-
100
- def run_ltx_animation(current_fragment_index, motion_prompt, conditioning_items_data, seed, cfg, progress=gr.Progress()):
101
- progress(0, desc=f"[Animador LTX] Interpolando Cena {current_fragment_index}...")
102
- output_path = os.path.join(WORKSPACE_DIR, f"fragment_{current_fragment_index}.mp4")
103
- target_device = 'cuda' if torch.cuda.is_available() else 'cpu'
104
-
105
- try:
106
- pipeline_instance.to(target_device)
107
- conditioning_items = []
108
- for (path, start_frame, strength) in conditioning_items_data:
109
- tensor = load_image_to_tensor_with_resize_and_crop(path, VIDEO_HEIGHT, VIDEO_WIDTH)
110
- conditioning_items.append(ConditioningItem(tensor.to(target_device), start_frame, strength))
111
-
112
- num_frames_padded = 121
113
- padded_h, padded_w = ((VIDEO_HEIGHT - 1) // 32 + 1) * 32, ((VIDEO_WIDTH - 1) // 32 + 1) * 32
114
- padding_vals = calculate_padding(VIDEO_HEIGHT, VIDEO_WIDTH, padded_h, padded_w)
115
-
116
- for cond_item in conditioning_items:
117
- cond_item.media_item = torch.nn.functional.pad(cond_item.media_item, padding_vals)
118
-
119
- timesteps = PIPELINE_CONFIG_YAML.get("first_pass", {}).get("timesteps")
120
- kwargs = {
121
- "prompt": motion_prompt, "negative_prompt": "blurry, distorted, bad quality, artifacts",
122
- "height": padded_h, "width": padded_w, "num_frames": num_frames_padded, "frame_rate": VIDEO_FPS,
123
- "generator": torch.Generator(device=target_device).manual_seed(int(seed) + current_fragment_index),
124
- "output_type": "pt", "guidance_scale": float(cfg), "timesteps": timesteps,
125
- "conditioning_items": conditioning_items, "vae_per_channel_normalize": True,
126
- "decode_timestep": PIPELINE_CONFIG_YAML["decode_timestep"],
127
- "decode_noise_scale": PIPELINE_CONFIG_YAML["decode_noise_scale"],
128
- "stochastic_sampling": PIPELINE_CONFIG_YAML["stochastic_sampling"],
129
- "image_cond_noise_scale": 0.15, "is_video": True,
130
- "mixed_precision": (PIPELINE_CONFIG_YAML["precision"] == "mixed_precision"),
131
- "offload_to_cpu": False, "enhance_prompt": False
132
- }
133
 
134
- result_tensor = pipeline_instance(**kwargs).images
135
- pad_l, pad_r, pad_t, pad_b = padding_vals; slice_h, slice_w = (-pad_b if pad_b > 0 else None), (-pad_r if pad_r > 0 else None)
136
- cropped_tensor = result_tensor[:, :, :VIDEO_TOTAL_FRAMES, pad_t:slice_h, pad_l:slice_w];
137
- video_np = (cropped_tensor[0].permute(1, 2, 3, 0).cpu().float().numpy() * 255).astype(np.uint8)
138
- with imageio.get_writer(output_path, fps=VIDEO_FPS, codec='libx264', quality=8) as writer:
139
- for i, frame in enumerate(video_np): progress(i / len(video_np), desc=f"Renderizando frame {i+1}/{len(video_np)}..."); writer.append_data(frame)
140
- return output_path
141
- finally:
142
- pipeline_instance.to('cpu')
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  gc.collect()
144
- if torch.cuda.is_available():
145
- torch.cuda.empty_cache()
146
-
147
- def run_full_video_production(storyboard, keyframe_image_paths, seed, cfg):
148
- if not storyboard or len(storyboard) != len(keyframe_image_paths):
149
- raise gr.Error("Roteiro e imagens-chave devem existir e ter o mesmo número de cenas.")
150
- video_fragments, log_history = [], ""
151
- num_scenes = len(storyboard)
152
- end_frame_index = VIDEO_TOTAL_FRAMES - 1
153
- for i in range(num_scenes):
154
- motion_prompt = storyboard[i]
155
- start_image_path = keyframe_image_paths[i]
156
- log_message = f"Preparando Cena {i+1}/{num_scenes}..."
157
- log_history += log_message + "\n"
158
- yield {video_production_log_output: gr.update(value=log_history)}
159
- conditioning_items_data = [(start_image_path, 0, 1.0)]
160
- log_message = f" -> Ponto de Partida: {os.path.basename(start_image_path)} no quadro 0."
161
- log_history += log_message + "\n"
162
- if i < num_scenes - 1:
163
- end_image_path = keyframe_image_paths[i+1]
164
- conditioning_items_data.append((end_image_path, end_frame_index, 1.0))
165
- log_message = f" -> Ponto de Chegada: {os.path.basename(end_image_path)} no quadro {end_frame_index}."
166
- log_history += log_message + "\n"
167
- else:
168
- log_message = f" -> Ponto de Chegada: Animação livre (última cena)."
169
- log_history += log_message + "\n"
170
- yield {video_production_log_output: gr.update(value=log_history)}
171
- fragment_path = run_ltx_animation(i + 1, motion_prompt, conditioning_items_data, seed, cfg)
172
- video_fragments.append(fragment_path)
173
- log_message = f"Cena {i+1} concluída e salva em {os.path.basename(fragment_path)}."
174
- log_history += log_message + "\n"
175
- yield {video_production_log_output: gr.update(value=log_history), fragment_gallery_output: gr.update(value=video_fragments), fragment_list_state: video_fragments, final_fragments_display: gr.update(value=video_fragments)}
176
- log_history += "\nProdução de todas as cenas de vídeo concluída!"
177
- yield {video_production_log_output: gr.update(value=log_history)}
178
-
179
- def concatenate_masterpiece(fragment_paths: list, progress=gr.Progress()):
180
- progress(0.5, desc="Montando a obra-prima final..."); list_file_path, final_output_path = os.path.join(WORKSPACE_DIR, "concat_list.txt"), os.path.join(WORKSPACE_DIR, "obra_prima_final.mp4")
181
- with open(list_file_path, "w") as f:
182
- for path in fragment_paths: f.write(f"file '{os.path.abspath(path)}'\n")
183
- command = f"ffmpeg -y -f concat -safe 0 -i {list_file_path} -c copy {final_output_path}"
184
- try:
185
- subprocess.run(command, shell=True, check=True, capture_output=True, text=True); return final_output_path
186
- except subprocess.CalledProcessError as e:
187
- raise gr.Error(f"FFmpeg falhou ao unir os vídeos: {e.stderr}")
188
-
189
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
190
- gr.Markdown("# LTX Video - Storyboard em Vídeo (ADUC-SDR)\n*By Carlex & Gemini & DreamO*")
191
-
192
- storyboard_state = gr.State([])
193
- keyframe_images_state = gr.State([])
194
- fragment_list_state = gr.State([])
195
-
196
- if os.path.exists(WORKSPACE_DIR): shutil.rmtree(WORKSPACE_DIR)
197
- os.makedirs(WORKSPACE_DIR)
198
-
199
- with gr.Tabs():
200
- with gr.TabItem("ETAPA 1: O DIRETOR (Roteiro Visual)"):
201
- with gr.Row():
202
- with gr.Column():
203
- num_fragments_input = gr.Slider(2, 10, 4, step=1, label="Número de Cenas")
204
- prompt_input = gr.Textbox(label="Ideia Geral (Prompt)")
205
- image_input = gr.Image(type="filepath", label="Imagem de Referência Principal")
206
- director_button = gr.Button("▶️ 1. Gerar Roteiro Visual", variant="primary")
207
- with gr.Column():
208
- storyboard_to_show = gr.JSON(label="Roteiro Gerado (para visualização)")
209
- with gr.TabItem("ETAPA 2: O PINTOR (Imagens-Chave)"):
210
- with gr.Row():
211
- with gr.Column(scale=2):
212
- gr.Markdown("### Controles do Pintor (DreamO)")
213
- with gr.Row():
214
- ref_image_1_input = gr.Image(label="Referência 1 (Principal)", type="filepath")
215
- ref_image_2_input = gr.Image(label="Referência 2 (Opcional, para composição)", type="filepath")
216
- with gr.Row():
217
- ref_task_1_input = gr.Dropdown(choices=["ip", "id", "style"], value="ip", label="Tarefa para Referência 1")
218
- ref_task_2_input = gr.Dropdown(choices=["ip", "id", "style"], value="ip", label="Tarefa para Referência 2")
219
- photographer_button = gr.Button("▶️ 2. Pintar Imagens-Chave", variant="primary")
220
- keyframe_log_output = gr.Textbox(label="Diário de Bordo do Pintor", lines=5, interactive=False)
221
- with gr.Column(scale=1):
222
- keyframe_gallery_output = gr.Gallery(label="Imagens-Chave Pintadas", object_fit="contain", height="auto", type="filepath")
223
- with gr.TabItem("ETAPA 3: A PRODUÇÃO (Gerar Cenas em Vídeo)"):
224
- gr.Markdown(f"Gere o vídeo interpolando entre as imagens-chave. Cada clipe terá **{VIDEO_DURATION_SECONDS} segundos a {VIDEO_FPS} FPS ({VIDEO_TOTAL_FRAMES} quadros)**.")
225
- with gr.Row():
226
- with gr.Column():
227
- keyframes_to_render = gr.Gallery(label="Imagens-Chave para Animar", object_fit="contain", height="auto", interactive=False)
228
- animator_button = gr.Button("▶️ 3. Produzir Cenas em Vídeo", variant="primary")
229
- video_production_log_output = gr.Textbox(label="Diário de Bordo da Produção", lines=10, interactive=False)
230
- with gr.Column():
231
- fragment_gallery_output = gr.Gallery(label="Cenas Produzidas (Vídeos)", object_fit="contain", height="auto")
232
- with gr.Row():
233
- seed_number = gr.Number(42, label="Seed")
234
- cfg_slider = gr.Slider(1.0, 10.0, 2.5, step=0.1, label="CFG")
235
- with gr.TabItem("ETAPA 4: PÓS-PRODUÇÃO"):
236
- with gr.Row():
237
- with gr.Column():
238
- editor_button = gr.Button("▶️ 4. Concatenar Vídeo Final", variant="primary")
239
- final_fragments_display = gr.JSON(label="Fragmentos a Concatenar")
240
- with gr.Column():
241
- final_video_output = gr.Video(label="A Obra-Prima Final")
242
-
243
- def director_success(storyboard_list, img_path):
244
- if not storyboard_list: raise gr.Error("O storyboard está vazio ou em formato inválido.")
245
- return {
246
- storyboard_state: storyboard_list,
247
- storyboard_to_show: gr.update(value=storyboard_list),
248
- ref_image_1_input: gr.update(value=img_path)
249
- }
250
-
251
- director_button.click(
252
- fn=get_storyboard_from_director,
253
- inputs=[num_fragments_input, prompt_input, image_input],
254
- outputs=[storyboard_state]
255
- ).then(
256
- fn=director_success,
257
- inputs=[storyboard_state, image_input],
258
- outputs=[storyboard_state, storyboard_to_show, ref_image_1_input]
259
- )
260
-
261
- photographer_button.click(
262
- fn=run_keyframe_generation,
263
- inputs=[storyboard_state, ref_image_1_input, ref_image_2_input, ref_task_1_input, ref_task_2_input],
264
- outputs=[keyframe_log_output, keyframe_gallery_output, keyframe_images_state]
265
- ).then(
266
- lambda paths: gr.update(value=paths),
267
- inputs=[keyframe_images_state],
268
- outputs=[keyframes_to_render]
269
- )
270
-
271
- animator_button.click(
272
- fn=run_full_video_production,
273
- inputs=[storyboard_state, keyframe_images_state, seed_number, cfg_slider],
274
- outputs=[video_production_log_output, fragment_gallery_output, fragment_list_state]
275
- ).then(
276
- lambda paths: gr.update(value=paths),
277
- inputs=[fragment_list_state],
278
- outputs=[final_fragments_display]
279
- )
280
-
281
- editor_button.click(
282
- fn=concatenate_masterpiece,
283
- inputs=[fragment_list_state],
284
- outputs=[final_video_output]
285
- )
286
-
287
- if __name__ == "__main__":
288
- demo.queue().launch(server_name="0.0.0.0", share=True)
 
1
+ # dreamo_helpers.py
2
+ # Módulo de serviço OTIMIZADO para o DreamO. Sem redimensionamento automático.
3
 
 
 
 
4
  import os
5
+ import cv2
6
+ import torch
 
 
 
 
 
7
  import numpy as np
8
+ from PIL import Image
 
9
  import huggingface_hub
10
+ import gc
11
+ from facexlib.utils.face_restoration_helper import FaceRestoreHelper
12
+ from torchvision.transforms.functional import normalize
13
+ from dreamo.dreamo_pipeline import DreamOPipeline
14
+ from dreamo.utils import img2tensor, tensor2img
15
+ from tools import BEN2
16
+
17
+ class Generator:
18
+ def __init__(self):
19
+ self.cpu_device = torch.device('cpu')
20
+ self.gpu_device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
21
+
22
+ print("Carregando modelos DreamO para a CPU...")
23
+ model_root = 'black-forest-labs/FLUX.1-dev'
24
+ self.dreamo_pipeline = DreamOPipeline.from_pretrained(model_root, torch_dtype=torch.bfloat16)
25
+ self.dreamo_pipeline.load_dreamo_model(self.cpu_device, use_turbo=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ self.bg_rm_model = BEN2.BEN_Base().to(self.cpu_device).eval()
28
+ huggingface_hub.hf_hub_download(repo_id='PramaLLC/BEN2', filename='BEN2_Base.pth', local_dir='models')
29
+ self.bg_rm_model.loadcheckpoints('models/BEN2_Base.pth')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ self.face_helper = FaceRestoreHelper(
32
+ upscale_factor=1, face_size=512, crop_ratio=(1, 1),
33
+ det_model='retinaface_resnet50', save_ext='png', device=self.cpu_device,
34
+ )
35
+ print("Modelos DreamO prontos (na CPU).")
36
+
37
+ def to_gpu(self):
38
+ print("Movendo modelos DreamO para a GPU...")
39
+ self.dreamo_pipeline.to(self.gpu_device)
40
+ self.bg_rm_model.to(self.gpu_device)
41
+ self.face_helper.device = self.gpu_device
42
+ if hasattr(self.face_helper, 'face_parse'): self.face_helper.face_parse.to(self.gpu_device)
43
+ if hasattr(self.face_helper, 'face_det'): self.face_helper.face_det.to(self.gpu_device)
44
+ print("Modelos DreamO na GPU.")
45
+
46
+ def to_cpu(self):
47
+ print("Descarregando modelos DreamO da GPU...")
48
+ self.dreamo_pipeline.to(self.cpu_device)
49
+ self.bg_rm_model.to(self.cpu_device)
50
+ self.face_helper.device = self.cpu_device
51
+ if hasattr(self.face_helper, 'face_det'): self.face_helper.face_det.to(self.cpu_device)
52
+ if hasattr(self.face_helper, 'face_parse'): self.face_helper.face_parse.to(self.cpu_device)
53
  gc.collect()
54
+ if torch.cuda.is_available(): torch.cuda.empty_cache()
55
+ print("GPU limpa após uso do DreamO.")
56
+
57
+ @torch.inference_mode()
58
+ def generate_image_with_gpu_management(self, ref_image1_np, ref_image2_np, ref_task1, ref_task2, prompt):
59
+ ref_conds = []
60
+ ref_images = [ref_image1_np, ref_image2_np]
61
+ ref_tasks = [ref_task1, ref_task2]
62
+
63
+ for idx, (ref_image, ref_task) in enumerate(zip(ref_images, ref_tasks)):
64
+ if ref_image is not None:
65
+ # LÓGICA DE REDIMENSIONAMENTO REMOVIDA
66
+ if ref_task == "id":
67
+ ref_image = self.get_align_face(ref_image)
68
+ elif ref_task != "style":
69
+ ref_image = self.bg_rm_model.inference(Image.fromarray(ref_image))
70
+
71
+ ref_image_tensor = img2tensor(np.array(ref_image), bgr2rgb=False).unsqueeze(0) / 255.0
72
+ ref_image_tensor = (2 * ref_image_tensor - 1.0).to(self.gpu_device, dtype=torch.bfloat16)
73
+ ref_conds.append({'img': ref_image_tensor, 'task': ref_task, 'idx': idx + 1})
74
+
75
+ image = self.dreamo_pipeline(
76
+ prompt=prompt, width=1024, height=1024,
77
+ num_inference_steps=12, guidance_scale=4.5,
78
+ ref_conds=ref_conds, generator=torch.Generator(device="cpu").manual_seed(42)
79
+ ).images[0]
80
+ return image
81
+
82
+ @torch.no_grad()
83
+ def get_align_face(self, img):
84
+ self.face_helper.clean_all()
85
+ image_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
86
+ self.face_helper.read_image(image_bgr)
87
+ self.face_helper.get_face_landmarks_5(only_center_face=True)
88
+ self.face_helper.align_warp_face()
89
+ if len(self.face_helper.cropped_faces) == 0: return None
90
+ align_face = self.face_helper.cropped_faces[0]
91
+ input_tensor = img2tensor(align_face, bgr2rgb=True).unsqueeze(0) / 255.0
92
+ input_tensor = input_tensor.to(self.gpu_device)
93
+ parsing_out = self.face_helper.face_parse(normalize(input_tensor, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]))[0]
94
+ parsing_out = parsing_out.argmax(dim=1, keepdim=True)
95
+ bg_label = [0, 16, 18, 7, 8, 9, 14, 15]
96
+ bg = sum(parsing_out == i for i in bg_label).bool()
97
+ white_image = torch.ones_like(input_tensor)
98
+ face_features_image = torch.where(bg, white_image, input_tensor)
99
+ return tensor2img(face_features_image, rgb2bgr=False)
100
+
101
+ # --- Instância Singleton ---
102
+ print("Inicializando o Pintor de Cenas (DreamO Helper)...")
103
+ hf_token = os.getenv('HF_TOKEN')
104
+ if hf_token: huggingface_hub.login(token=hf_token)
105
+ dreamo_generator_singleton = Generator()
106
+ print("Pintor de Cenas (DreamO Helper) pronto.")