Carlexxx
feat: ✨ aBINC 2.2
fb56537
raw
history blame
12.3 kB
# aduc_framework/engineers/deformes4D.py
#
# Versão 16.0.0 (Integração de Lógica ADUC-SDR Canônica)
# Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos
#
# - Refatora a lógica de geração de vídeo monolítica para o padrão de
# Especialista ADUC, operando dentro da classe Deformes4DEngine.
# - Integra o núcleo lógico de Poda Causal, Eco e Déjà-Vu (linhas 187-315
# do arquivo original) de forma intacta.
# - Substitui as chamadas de baixo nível por delegações aos managers
# especializados (VaeManager, LtxManager, VideoEncodeTool).
# - O método principal agora aceita um `VideoGenerationJob` do Planner5D.
import os
import time
import torch
import logging
from PIL import Image
import gc
from typing import Dict, Any
# Importa os managers e especialistas da arquitetura ADUC
from ..managers.ltx_manager import ltx_manager_singleton
from ..managers.vae_manager import vae_manager_singleton
from ..tools.video_encode_tool import video_encode_tool_singleton
from ..types import LatentConditioningItem, VideoGenerationJob
from ..engineers.composer import composer_singleton as Composer # Usado para decisões cinematográficas
logger = logging.getLogger(__name__)
class Deformes4DEngine:
"""
Implementa a Câmera (Ψ) e o Destilador (Δ) da arquitetura ADUC-SDR.
É responsável pela execução da geração de fragmentos de vídeo e pela
extração dos contextos causais (Eco e Déjà-Vu).
"""
def __init__(self):
self.workspace_dir: str | None = None
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
logger.info("Especialista Deformes4D (Executor ADUC-SDR: Câmera Ψ e Destilador Δ) inicializado.")
def initialize(self, workspace_dir: str):
if self.workspace_dir and self.workspace_dir == workspace_dir:
return
self.workspace_dir = workspace_dir
os.makedirs(self.workspace_dir, exist_ok=True)
logger.info(f"Executor de Cena 4D inicializado com workspace: {self.workspace_dir}.")
def _quantize_to_multiple(self, n, m):
if m == 0: return n
quantized = int(round(n / m) * m)
return m if n > 0 and quantized == 0 else quantized
# O método principal que o Planner5D chamará
def generate_movie_clip_from_job(self, job: VideoGenerationJob) -> Dict[str, Any]:
if not self.workspace_dir:
raise RuntimeError("Deformes4DEngine não foi inicializado.")
# Extrai os parâmetros da Ordem de Serviço
keyframes = job.keyframe_paths
global_prompt = job.global_prompt
storyboard = job.storyboard
seconds_per_fragment = job.duration_per_fragment
trim_percent = job.trim_percent
handler_strength = job.handler_strength
destination_convergence_strength = job.destination_convergence_strength
video_resolution = job.resolution
# Este método agora encapsula a lógica do seu 'generate_full_movie'
return self._execute_generation_logic(
keyframes, global_prompt, storyboard, seconds_per_fragment,
trim_percent, handler_strength, destination_convergence_strength,
video_resolution
)
def _execute_generation_logic(self, keyframes: list, global_prompt: str, storyboard: list,
seconds_per_fragment: float, trim_percent: int,
handler_strength: float, destination_convergence_strength: float,
video_resolution: int):
#####(atenção Gemini .. nao apagar ou modificar nada apartir dessa linha) #####
# manter o conteudo original da linha 187 ate 315
# 1. Definição dos Parâmetros da Geração com base na Tese
FPS = 32
FRAMES_PER_LATENT_CHUNK = 8
ECO_LATENT_CHUNKS = 2
total_frames_brutos = self._quantize_to_multiple(int(seconds_per_fragment * FPS), FRAMES_PER_LATENT_CHUNK)
total_latents_brutos = total_frames_brutos // FRAMES_PER_LATENT_CHUNK
frames_a_podar = 15 #self._quantize_to_multiple(int(total_frames_brutos * (trim_percent / 100)), FRAMES_PER_LATENT_CHUNK)
latents_a_podar = frames_a_podar // FRAMES_PER_LATENT_CHUNK
if total_latents_brutos <= latents_a_podar:
#raise gr.Error(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")
raise ValueError(f"A porcentagem de poda ({trim_percent}%) é muito alta. Reduza-a ou aumente a duração.")
DEJAVU_FRAME_TARGET = 23
DESTINATION_FRAME_TARGET = total_frames_brutos -8
logger.info("--- CONFIGURAÇÃO DA GERAÇÃO ADUC-SDR ---")
logger.info(f"Total de Latents por Geração Exploratória (V_bruto): {total_latents_brutos} ({total_frames_brutos} frames)")
logger.info(f"Latents a serem descartados (Poda Causal): {latents_a_podar} ({frames_a_podar} frames)")
logger.info(f"Chunks Latentes do Eco Causal (C): {ECO_LATENT_CHUNKS}")
logger.info(f"Frame alvo do Déjà-Vu (D): {DEJAVU_FRAME_TARGET}")
logger.info(f"Frame alvo do Destino (K): {DESTINATION_FRAME_TARGET}")
logger.info("------------------------------------------")
# 2. Inicialização do Estado
base_ltx_params = {"guidance_scale": 1.0, "stg_scale": 0.005, "rescaling_scale": 0.05, "num_inference_steps": 4, "image_cond_noise_scale": 0.05}
keyframe_paths = [item[0] if isinstance(item, tuple) else item for item in keyframes]
video_clips_paths, story_history = [], ""
target_resolution_tuple = (video_resolution, video_resolution)
eco_latent_for_next_loop = None
dejavu_latent_for_next_loop = None
if len(keyframe_paths) < 1:
#raise gr.Error(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
raise ValueError(f"A geração requer no mínimo 2 keyframes. Você forneceu {len(keyframe_paths)}.")
num_transitions_to_generate = len(keyframe_paths) -1
# 3. Loop Principal de Geração de Fragmentos
for i in range(num_transitions_to_generate):
fragment_index = i + 1
logger.info(f"--- INICIANDO FRAGMENTO {fragment_index}/{num_transitions_to_generate} ---")
# 3.1. Consulta ao Maestro (Γ) para obter a intenção (Pᵢ)
start_keyframe_path = keyframe_paths[i]
if i+1 < num_transitions_to_generate:
destination_keyframe_path = keyframe_paths[i + 1]
else:
destination_keyframe_path = keyframe_paths[i]
decision = Composer.execute_cognitive_task(
task_id="TASK_08_IMPROVISE_CINEMATIC_PROMPT_ATO",
template_data={"current_act_narrative": storyboard[i]},
images=[Image.open(start_keyframe_path), Image.open(destination_keyframe_path)]
)
motion_prompt = decision.strip()
story_history += f"\n- Ato {fragment_index}: {motion_prompt}"
# 3.2. Montagem das Âncoras para a Fórmula Canônica Ψ({C, D, K}, P)
conditioning_items = []
logger.info(" [Ψ.1] Montando âncoras causais...")
if eco_latent_for_next_loop is None:
logger.info(" - Primeiro fragmento: Usando Keyframe inicial como âncora de partida.")
img_start = Image.open(start_keyframe_path).convert("RGB")
img_dest = Image.open(destination_keyframe_path).convert("RGB")
start_latent, dest_latent = vae_manager_singleton.encode_batch([img_start, img_dest], target_resolution_tuple)
conditioning_items.append(LatentConditioningItem(start_latent, 0, 1.0))
else:
logger.info(" - Âncora 1: Eco Causal (C) - Herança do passado.")
conditioning_items.append(LatentConditioningItem(eco_latent_for_next_loop, 0, 1.0))
logger.info(" - Âncora 2: Déjà-Vu (D) - Memória de um futuro idealizado.")
conditioning_items.append(LatentConditioningItem(dejavu_latent_for_next_loop, 23, 0.5))
img_dest = Image.open(destination_keyframe_path).convert("RGB")
dest_latent = vae_manager_singleton.encode_batch([img_dest], target_resolution_tuple)[0]
logger.info(" - Âncora 3: Destino (K) - Âncora geométrica/narrativa.")
conditioning_items.append(LatentConditioningItem(dest_latent, DESTINATION_FRAME_TARGET, 1.0))
# 3.3. Execução da Câmera (Ψ): Geração Exploratória para criar V_bruto
logger.info(f" [Ψ.2] Câmera (Ψ) executando a geração exploratória de {total_latents_brutos} chunks latentes...")
current_ltx_params = {**base_ltx_params, "motion_prompt": motion_prompt}
latents_brutos, _ = ltx_manager_singleton.generate_latent_fragment(
height=video_resolution, width=video_resolution,
conditioning_items_data=conditioning_items,
video_total_frames=total_frames_brutos+1,
**current_ltx_params
)
# 3.4. Execução do Destilador (Δ): Implementação do Ciclo de Poda Causal
logger.info(f" [Δ] Shape do tensor bruto para vídeo final: {latents_brutos.shape}")
#latents_video = latents_brutos[:, :, :-1, :, :]
# --- Workaround empírico preservado ---
eco_latent_for_next_loop = latents_brutos[:, :, -5:-2, :, :].clone()
dejavu_latent_for_next_loop = latents_brutos[:, :, -1:, :, :].clone()
latents_video = latents_brutos[:, :, :-4, :, :].clone()
logger.info(f" [Δ] Shape do tensor video para vídeo final: {latents_video.shape}")
#if i+1 < num_transitions_to_generate:
#latents_video = latents_video[:, :, :-4, :, :]
# logger.info(f"@@@@@@@@@@# Nao é ULTIMO poda -1")
# logger.info(f" [Δ] -1 Shape do tensor video para vídeo final: {latents_video.shape}")
#if i > 0:
# latents_video = latents_video[:, :, 2:, :, :]
# logger.info(f"@@@@@@@@@@# nao é o primeiro poda 1")
# logger.info(f" [Δ] 1 Shape do tensor video para vídeo final: {latents_video.shape}")
logger.info(f" [Δ] Shape do tensor para vídeo final: {latents_video.shape}")
logger.info(f" - (Δ.1) Déjà-Vu (D) destilado. Shape: {dejavu_latent_for_next_loop.shape if dejavu_latent_for_next_loop is not None else 'N/A'}")
logger.info(f" - (Δ.2) Eco Causal (C) extraído. Shape: {eco_latent_for_next_loop.shape if eco_latent_for_next_loop is not None else 'N/A'}")
# 3.5. Renderização e Armazenamento do Fragmento Final
base_name = f"fragment_{fragment_index}_{int(time.time())}"
pixel_tensor = vae_manager_singleton.decode(latents_video)
video_path = os.path.join(self.workspace_dir, f"{base_name}.mp4")
video_encode_tool_singleton.save_video_from_tensor(pixel_tensor, video_path, fps=FPS)
video_clips_paths.append(video_path)
logger.info(f"--- FRAGMENTO {fragment_index} FINALIZADO E SALVO EM: {video_path} ---")
# 4. Montagem Final do Filme
final_movie_path = os.path.join(self.workspace_dir, f"final_movie_silent_{int(time.time())}.mp4")
final_movie_path = video_encode_tool_singleton.concatenate_videos(video_clips_paths, final_movie_path, self.workspace_dir)
#####(atenção Gemini .. nao apagar ou modificar nada acima dessa linha) #####
logger.info(f"Filme completo salvo em: {final_movie_path}")
# Retorna o resultado no formato esperado pelo Planner5D
return {
"final_path": final_movie_path,
"video_data": {"id": 0, "caminho_pixel": final_movie_path} # ID 0 para o filme completo
}
# --- Instância Singleton ---
deformes4d_engine_singleton = Deformes4DEngine()