euiia commited on
Commit
b7e85da
·
verified ·
1 Parent(s): 768a8fe

Update deformes4D_engine.py

Browse files
Files changed (1) hide show
  1. deformes4D_engine.py +44 -81
deformes4D_engine.py CHANGED
@@ -69,6 +69,14 @@ class Deformes4DEngine:
69
  timestep_tensor = torch.tensor([decode_timestep] * latent_tensor.shape[0], device=self.device, dtype=latent_tensor.dtype)
70
  return vae_decode(latent_tensor, self.vae, is_video=True, timestep=timestep_tensor, vae_per_channel_normalize=True)
71
 
 
 
 
 
 
 
 
 
72
  def _preprocess_image_for_latent_conversion(self, image: Image.Image, target_resolution: tuple) -> Image.Image:
73
  if image.size != target_resolution:
74
  return ImageOps.fit(image, target_resolution, Image.Resampling.LANCZOS)
@@ -80,63 +88,45 @@ class Deformes4DEngine:
80
  tensor = (tensor * 2.0) - 1.0
81
  return self.pixels_to_latents(tensor)
82
 
83
-
84
  # --- NÚCLEO DA LÓGICA ADUC-SDR ---
85
- def generate_full_movie_old(self, keyframes: list, global_prompt: str, storyboard: list,
86
  seconds_per_fragment: float, trim_percent: int,
87
  handler_strength: float, destination_convergence_strength: float,
88
  use_upscaler: bool, use_refiner: bool, use_hd: bool, use_audio: bool,
89
  video_resolution: int, use_continuity_director: bool,
90
  progress: gr.Progress = gr.Progress()):
91
 
92
- # 1. Definição dos Parâmetros da Geração com base na Tese
93
  FPS = 24
94
  FRAMES_PER_LATENT_CHUNK = 8
95
  ECO_LATENT_CHUNKS = 2
96
 
97
  total_frames_brutos = self._quantize_to_multiple(int(seconds_per_fragment * FPS), FRAMES_PER_LATENT_CHUNK)
98
- total_latents_brutos = total_frames_brutos // FRAMES_PER_LATENT_CHUNK
99
-
100
  frames_a_podar = self._quantize_to_multiple(int(total_frames_brutos * (trim_percent / 100)), FRAMES_PER_LATENT_CHUNK)
101
  latents_a_podar = frames_a_podar // FRAMES_PER_LATENT_CHUNK
102
 
103
- if total_latents_brutos <= latents_a_podar:
104
- raise gr.Error(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")
105
-
106
- DEJAVU_FRAME_TARGET = frames_a_podar - 1
107
  DESTINATION_FRAME_TARGET = total_frames_brutos - 1
108
 
109
- logger.info("--- CONFIGURAÇÃO DA GERAÇÃO ADUC-SDR ---")
110
- logger.info(f"Total de Latents por Geração Exploratória (V_bruto): {total_latents_brutos} ({total_frames_brutos} frames)")
111
- logger.info(f"Latents a serem descartados (Poda Causal): {latents_a_podar} ({frames_a_podar} frames)")
112
- logger.info(f"Chunks Latentes do Eco Causal (C): {ECO_LATENT_CHUNKS}")
113
- logger.info(f"Frame alvo do Déjà-Vu (D): {DEJAVU_FRAME_TARGET}")
114
- logger.info(f"Frame alvo do Destino (K): {DESTINATION_FRAME_TARGET}")
115
- logger.info("------------------------------------------")
116
-
117
- # 2. Inicialização do Estado
118
  base_ltx_params = {"guidance_scale": 2.0, "stg_scale": 0.025, "rescaling_scale": 0.15, "num_inference_steps": 20, "image_cond_noise_scale": 0.00}
119
  keyframe_paths = [item[0] if isinstance(item, tuple) else item for item in keyframes]
120
- video_clips_paths, story_history = [], ""
121
  target_resolution_tuple = (video_resolution, video_resolution)
122
 
123
  eco_latent_for_next_loop = None
124
  dejavu_latent_for_next_loop = None
125
 
126
- latent_fragments[]
 
127
 
128
  if len(keyframe_paths) < 2:
129
  raise gr.Error(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
130
 
131
  num_transitions_to_generate = len(keyframe_paths) - 1
132
 
133
- # 3. Loop Principal de Geração de Fragmentos
134
  for i in range(num_transitions_to_generate):
135
  fragment_index = i + 1
136
- logger.info(f"--- INICIANDO FRAGMENTO {fragment_index}/{num_transitions_to_generate} ---")
137
- progress(fragment_index / num_transitions_to_generate, desc=f"Produzindo Transição {fragment_index}/{num_transitions_to_generate}")
138
 
139
- # 3.1. Consulta ao Maestro (Γ) para obter a intenção (Pᵢ)
140
  past_keyframe_path = keyframe_paths[i - 1] if i > 0 else keyframe_paths[i]
141
  start_keyframe_path = keyframe_paths[i]
142
  destination_keyframe_path = keyframe_paths[i + 1]
@@ -149,32 +139,19 @@ class Deformes4DEngine:
149
  transition_type, motion_prompt = decision["transition_type"], decision["motion_prompt"]
150
  story_history += f"\n- Ato {fragment_index}: {motion_prompt}"
151
 
152
- # 3.2. Montagem das Âncoras para a Fórmula Canônica Ψ({C, D, K}, P)
153
  conditioning_items = []
154
- logger.info(" [Ψ.1] Montando âncoras causais...")
155
-
156
  if eco_latent_for_next_loop is None:
157
- logger.info(" - Primeiro fragmento: Usando Keyframe inicial como âncora de partida.")
158
  img_start = self._preprocess_image_for_latent_conversion(Image.open(start_keyframe_path).convert("RGB"), target_resolution_tuple)
159
  conditioning_items.append(LatentConditioningItem(self.pil_to_latent(img_start), 0, 1.0))
160
  else:
161
- logger.info(" - Âncora 1: Eco Causal (C) - Herança do passado.")
162
  conditioning_items.append(LatentConditioningItem(eco_latent_for_next_loop, 0, 1.0))
163
- logger.info(" - Âncora 2: Déjà-Vu (D) - Memória de um futuro idealizado.")
164
  conditioning_items.append(LatentConditioningItem(dejavu_latent_for_next_loop, DEJAVU_FRAME_TARGET, handler_strength))
165
 
166
- logger.info(" - Âncora 3: Destino (K) - Âncora geométrica/narrativa.")
167
  img_dest = self._preprocess_image_for_latent_conversion(Image.open(destination_keyframe_path).convert("RGB"), target_resolution_tuple)
168
  conditioning_items.append(LatentConditioningItem(self.pil_to_latent(img_dest), DESTINATION_FRAME_TARGET, destination_convergence_strength))
169
 
170
- # 3.3. Execução da Câmera (Ψ): Geração Exploratória para criar V_bruto
171
- logger.info(f" [Ψ.2] Câmera (Ψ) executando a geração exploratória de {total_latents_brutos} chunks latentes...")
172
  current_ltx_params = {**base_ltx_params, "motion_prompt": motion_prompt}
173
  latents_brutos = self._generate_latent_tensor_internal(conditioning_items, current_ltx_params, target_resolution_tuple, total_frames_brutos)
174
- logger.info(f" - Geração concluída. Tensor latente bruto (V_bruto) criado com shape: {latents_brutos.shape}.")
175
-
176
- # 3.4. Execução do Destilador (Δ): Implementação do Ciclo de Poda Causal (com workaround empírico)
177
- logger.info(f" [Δ] Destilador (Δ) executando o Ciclo de Poda Causal...")
178
 
179
  last_trim = latents_brutos[:, :, -(latents_a_podar+1):, :, :].clone()
180
  eco_latent_for_next_loop = last_trim[:, :, :2, :, :].clone()
@@ -183,12 +160,7 @@ class Deformes4DEngine:
183
  latents_video = latents_brutos[:, :, :-(latents_a_podar-1), :, :].clone()
184
  latents_video = latents_video[:, :, 1:, :, :]
185
 
186
- logger.info(f" [Δ] latents_video {latents_video.shape}")
187
- logger.info(f" - (Δ.1) Déjà-Vu (D) destilado. Shape: {dejavu_latent_for_next_loop.shape}")
188
- logger.info(f" - (Δ.2) Eco Causal (C) extraído. Shape: {eco_latent_for_next_loop.shape}")
189
-
190
  if transition_type == "cut":
191
- logger.warning(" - DECISÃO DO MAESTRO: Corte ('cut'). Resetando a memória causal (Eco e Déjà-Vu).")
192
  eco_latent_for_next_loop = None
193
  dejavu_latent_for_next_loop = None
194
 
@@ -197,69 +169,62 @@ class Deformes4DEngine:
197
 
198
  latent_fragments.append(latents_video)
199
 
200
- current_step += 1
201
- progress(current_step / TOTAL_STEPS, desc="Unificação Causal (Concatenação)...")
202
-
203
  logger.info("--- CONCATENANDO TODOS OS FRAGMENTOS LATENTES ---")
204
- tensors_para_concatenar = []
205
- for idx, tensor_frag in enumerate(latent_fragments):
206
- # Move cada tensor para o dispositivo de destino antes de adicioná-lo à lista.
207
- target_device = self.device
208
- tensor_on_target_device = tensor_frag.to(target_device)
209
- if idx < len(latent_fragments) - 1:
210
- tensors_para_concatenar.append(tensor_on_target_device[:, :, :-1, :, :])
211
- else:
212
- tensors_para_concatenar.append(tensor_on_target_device)
213
-
214
  processed_latents = torch.cat(tensors_para_concatenar, dim=2)
215
- logger.info(f"Concatenação concluída. Shape final do tensor latente: {final_concatenated_latents.shape}")
 
216
 
217
  if use_refiner:
218
- current_step += 1
219
- progress(current_step / TOTAL_STEPS, desc="Polimento Global (Denoise)...")
220
  processed_latents = self.refine_latents(
221
  processed_latents,
222
  motion_prompt="",
223
  guidance_scale=1.0
224
  )
225
- logger.info(f"Polimento global aplicado. Shape: {processed_latents.shape}")
226
- else:
227
- logger.info("Etapa de refinamento desativada.")
228
-
229
 
 
230
  base_name = f"movie_{int(time.time())}"
231
- current_step += 1
232
- progress(current_step / TOTAL_STEPS, desc="Renderização (em lotes)...")
233
 
234
  if use_audio:
235
- video_path = self._generate_video_and_audio_from_latents(processed_latents, global_prompt, base_name)
 
236
  else:
237
- video_path = os.path.join(self.workspace_dir, f"{base_name}_silent.mp4")
238
- logger.info("Etapa de sonoplastia desativada.")
239
  pixel_tensor = self.latents_to_pixels(processed_latents)
240
- self.save_video_from_tensor(pixel_tensor, video_path, fps=24)
 
 
 
 
 
 
241
 
242
  if use_hd:
243
- current_step += 1
244
- progress(current_step / TOTAL_STEPS, desc="Masterização Final (HD)...")
245
  try:
 
246
  hd_specialist_singleton.process_video(
247
- input_video_path=video_path,
248
- output_video_path=video_path,
249
  prompt=" "
250
  )
251
  except Exception as e:
252
  logger.error(f"Falha na masterização HD: {e}. Usando vídeo de qualidade padrão.")
 
253
  else:
254
- logger.info("Etapa de edicao HD desativada.")
255
-
 
 
256
 
257
- logger.info(f"Processo concluído! Vídeo final salvo em: {video_path}")
258
- yield {"final_path": video_path}
259
-
260
 
261
  def _generate_video_and_audio_from_latents(self, latent_tensor, audio_prompt, base_name):
262
- silent_video_path = os.path.join(self.workspace_dir, f"{base_name}_silent.mp4")
 
263
  pixel_tensor = self.latents_to_pixels(latent_tensor)
264
  self.save_video_from_tensor(pixel_tensor, silent_video_path, fps=24)
265
  del pixel_tensor; gc.collect()
@@ -270,7 +235,7 @@ class Deformes4DEngine:
270
  capture_output=True, text=True, check=True)
271
  frag_duration = float(result.stdout.strip())
272
  except (subprocess.CalledProcessError, ValueError, FileNotFoundError):
273
- logger.warning(f"ffprobe falhou em {os.path.basename(silent_video_path)}. Calculando duração manualmente.")
274
  num_pixel_frames = latent_tensor.shape[2] * 8
275
  frag_duration = num_pixel_frames / 24.0
276
 
@@ -282,7 +247,6 @@ class Deformes4DEngine:
282
  os.remove(silent_video_path)
283
  return video_with_audio_path
284
 
285
-
286
  def refine_latents(self, latents: torch.Tensor, fps: int = 24, denoise_strength: float = 0.35, refine_steps: int = 12, motion_prompt: str = "...", **kwargs) -> torch.Tensor:
287
  logger.info(f"Refinando tensor latente com shape {latents.shape}.")
288
  _, _, num_latent_frames, latent_h, latent_w = latents.shape
@@ -319,7 +283,6 @@ class Deformes4DEngine:
319
  gc.collect()
320
  torch.cuda.empty_cache()
321
  return new_full_latents
322
-
323
 
324
  def _quantize_to_multiple(self, n, m):
325
  if m == 0: return n
 
69
  timestep_tensor = torch.tensor([decode_timestep] * latent_tensor.shape[0], device=self.device, dtype=latent_tensor.dtype)
70
  return vae_decode(latent_tensor, self.vae, is_video=True, timestep=timestep_tensor, vae_per_channel_normalize=True)
71
 
72
+ def save_video_from_tensor(self, video_tensor: torch.Tensor, path: str, fps: int = 24):
73
+ if video_tensor is None or video_tensor.ndim != 5 or video_tensor.shape[2] == 0: return
74
+ video_tensor = video_tensor.squeeze(0).permute(1, 2, 3, 0)
75
+ video_tensor = (video_tensor.clamp(-1, 1) + 1) / 2.0
76
+ video_np = (video_tensor.detach().cpu().float().numpy() * 255).astype(np.uint8)
77
+ with imageio.get_writer(path, fps=fps, codec='libx264', quality=8, output_params=['-pix_fmt', 'yuv420p']) as writer:
78
+ for frame in video_np: writer.append_data(frame)
79
+
80
  def _preprocess_image_for_latent_conversion(self, image: Image.Image, target_resolution: tuple) -> Image.Image:
81
  if image.size != target_resolution:
82
  return ImageOps.fit(image, target_resolution, Image.Resampling.LANCZOS)
 
88
  tensor = (tensor * 2.0) - 1.0
89
  return self.pixels_to_latents(tensor)
90
 
 
91
  # --- NÚCLEO DA LÓGICA ADUC-SDR ---
92
+ def generate_full_movie(self, keyframes: list, global_prompt: str, storyboard: list,
93
  seconds_per_fragment: float, trim_percent: int,
94
  handler_strength: float, destination_convergence_strength: float,
95
  use_upscaler: bool, use_refiner: bool, use_hd: bool, use_audio: bool,
96
  video_resolution: int, use_continuity_director: bool,
97
  progress: gr.Progress = gr.Progress()):
98
 
 
99
  FPS = 24
100
  FRAMES_PER_LATENT_CHUNK = 8
101
  ECO_LATENT_CHUNKS = 2
102
 
103
  total_frames_brutos = self._quantize_to_multiple(int(seconds_per_fragment * FPS), FRAMES_PER_LATENT_CHUNK)
 
 
104
  frames_a_podar = self._quantize_to_multiple(int(total_frames_brutos * (trim_percent / 100)), FRAMES_PER_LATENT_CHUNK)
105
  latents_a_podar = frames_a_podar // FRAMES_PER_LATENT_CHUNK
106
 
107
+ DEJAVU_FRAME_TARGET = frames_a_podar - 1 if frames_a_podar > 0 else 0
 
 
 
108
  DESTINATION_FRAME_TARGET = total_frames_brutos - 1
109
 
 
 
 
 
 
 
 
 
 
110
  base_ltx_params = {"guidance_scale": 2.0, "stg_scale": 0.025, "rescaling_scale": 0.15, "num_inference_steps": 20, "image_cond_noise_scale": 0.00}
111
  keyframe_paths = [item[0] if isinstance(item, tuple) else item for item in keyframes]
112
+ story_history = ""
113
  target_resolution_tuple = (video_resolution, video_resolution)
114
 
115
  eco_latent_for_next_loop = None
116
  dejavu_latent_for_next_loop = None
117
 
118
+ # [CORREÇÃO 1] Inicialização correta da lista
119
+ latent_fragments = []
120
 
121
  if len(keyframe_paths) < 2:
122
  raise gr.Error(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
123
 
124
  num_transitions_to_generate = len(keyframe_paths) - 1
125
 
 
126
  for i in range(num_transitions_to_generate):
127
  fragment_index = i + 1
128
+ progress(i / num_transitions_to_generate, desc=f"Produzindo Transição {fragment_index}/{num_transitions_to_generate}")
 
129
 
 
130
  past_keyframe_path = keyframe_paths[i - 1] if i > 0 else keyframe_paths[i]
131
  start_keyframe_path = keyframe_paths[i]
132
  destination_keyframe_path = keyframe_paths[i + 1]
 
139
  transition_type, motion_prompt = decision["transition_type"], decision["motion_prompt"]
140
  story_history += f"\n- Ato {fragment_index}: {motion_prompt}"
141
 
 
142
  conditioning_items = []
 
 
143
  if eco_latent_for_next_loop is None:
 
144
  img_start = self._preprocess_image_for_latent_conversion(Image.open(start_keyframe_path).convert("RGB"), target_resolution_tuple)
145
  conditioning_items.append(LatentConditioningItem(self.pil_to_latent(img_start), 0, 1.0))
146
  else:
 
147
  conditioning_items.append(LatentConditioningItem(eco_latent_for_next_loop, 0, 1.0))
 
148
  conditioning_items.append(LatentConditioningItem(dejavu_latent_for_next_loop, DEJAVU_FRAME_TARGET, handler_strength))
149
 
 
150
  img_dest = self._preprocess_image_for_latent_conversion(Image.open(destination_keyframe_path).convert("RGB"), target_resolution_tuple)
151
  conditioning_items.append(LatentConditioningItem(self.pil_to_latent(img_dest), DESTINATION_FRAME_TARGET, destination_convergence_strength))
152
 
 
 
153
  current_ltx_params = {**base_ltx_params, "motion_prompt": motion_prompt}
154
  latents_brutos = self._generate_latent_tensor_internal(conditioning_items, current_ltx_params, target_resolution_tuple, total_frames_brutos)
 
 
 
 
155
 
156
  last_trim = latents_brutos[:, :, -(latents_a_podar+1):, :, :].clone()
157
  eco_latent_for_next_loop = last_trim[:, :, :2, :, :].clone()
 
160
  latents_video = latents_brutos[:, :, :-(latents_a_podar-1), :, :].clone()
161
  latents_video = latents_video[:, :, 1:, :, :]
162
 
 
 
 
 
163
  if transition_type == "cut":
 
164
  eco_latent_for_next_loop = None
165
  dejavu_latent_for_next_loop = None
166
 
 
169
 
170
  latent_fragments.append(latents_video)
171
 
 
 
 
172
  logger.info("--- CONCATENANDO TODOS OS FRAGMENTOS LATENTES ---")
173
+ tensors_para_concatenar = [frag.to(self.device) for frag in latent_fragments]
 
 
 
 
 
 
 
 
 
174
  processed_latents = torch.cat(tensors_para_concatenar, dim=2)
175
+ # [CORREÇÃO 2] Referência correta da variável no log
176
+ logger.info(f"Concatenação concluída. Shape final do tensor latente: {processed_latents.shape}")
177
 
178
  if use_refiner:
 
 
179
  processed_latents = self.refine_latents(
180
  processed_latents,
181
  motion_prompt="",
182
  guidance_scale=1.0
183
  )
 
 
 
 
184
 
185
+ # --- [INÍCIO DA SEÇÃO CORRIGIDA PARA EXECUÇÃO] ---
186
  base_name = f"movie_{int(time.time())}"
187
+ # Define um caminho único para o vídeo que sai desta etapa, antes do HD.
188
+ intermediate_video_path = os.path.join(self.workspace_dir, f"{base_name}_intermediate.mp4")
189
 
190
  if use_audio:
191
+ # A função de áudio agora salva o vídeo com áudio no caminho intermediário
192
+ intermediate_video_path = self._generate_video_and_audio_from_latents(processed_latents, global_prompt, base_name)
193
  else:
194
+ logger.info("Etapa de sonoplastia desativada. Renderizando vídeo silencioso.")
 
195
  pixel_tensor = self.latents_to_pixels(processed_latents)
196
+ self.save_video_from_tensor(pixel_tensor, intermediate_video_path, fps=24)
197
+ del pixel_tensor
198
+
199
+ del processed_latents; gc.collect(); torch.cuda.empty_cache()
200
+
201
+ # Define o caminho final
202
+ final_video_path = os.path.join(self.workspace_dir, f"{base_name}_FINAL.mp4")
203
 
204
  if use_hd:
205
+ progress(0.9, desc="Masterização Final (HD)...")
 
206
  try:
207
+ # O HD agora lê o intermediate_video_path e salva no final_video_path
208
  hd_specialist_singleton.process_video(
209
+ input_video_path=intermediate_video_path,
210
+ output_video_path=final_video_path,
211
  prompt=" "
212
  )
213
  except Exception as e:
214
  logger.error(f"Falha na masterização HD: {e}. Usando vídeo de qualidade padrão.")
215
+ os.rename(intermediate_video_path, final_video_path)
216
  else:
217
+ logger.info("Etapa de edição HD desativada.")
218
+ # Se o HD não for usado, o vídeo intermediário se torna o final.
219
+ os.rename(intermediate_video_path, final_video_path)
220
+ # --- [FIM DA SEÇÃO CORRIGIDA] ---
221
 
222
+ logger.info(f"Processo concluído! Vídeo final salvo em: {final_video_path}")
223
+ yield {"final_path": final_video_path}
 
224
 
225
  def _generate_video_and_audio_from_latents(self, latent_tensor, audio_prompt, base_name):
226
+ # Esta função foi movida para cima, mas sua lógica interna permanece a mesma.
227
+ silent_video_path = os.path.join(self.workspace_dir, f"{base_name}_silent_for_audio.mp4")
228
  pixel_tensor = self.latents_to_pixels(latent_tensor)
229
  self.save_video_from_tensor(pixel_tensor, silent_video_path, fps=24)
230
  del pixel_tensor; gc.collect()
 
235
  capture_output=True, text=True, check=True)
236
  frag_duration = float(result.stdout.strip())
237
  except (subprocess.CalledProcessError, ValueError, FileNotFoundError):
238
+ logger.warning(f"ffprobe falhou. Calculando duração manualmente.")
239
  num_pixel_frames = latent_tensor.shape[2] * 8
240
  frag_duration = num_pixel_frames / 24.0
241
 
 
247
  os.remove(silent_video_path)
248
  return video_with_audio_path
249
 
 
250
  def refine_latents(self, latents: torch.Tensor, fps: int = 24, denoise_strength: float = 0.35, refine_steps: int = 12, motion_prompt: str = "...", **kwargs) -> torch.Tensor:
251
  logger.info(f"Refinando tensor latente com shape {latents.shape}.")
252
  _, _, num_latent_frames, latent_h, latent_w = latents.shape
 
283
  gc.collect()
284
  torch.cuda.empty_cache()
285
  return new_full_latents
 
286
 
287
  def _quantize_to_multiple(self, n, m):
288
  if m == 0: return n