Aduc-sdr commited on
Commit
3da3af7
·
verified ·
1 Parent(s): f16b044

Update engineers/deformes3D.py

Browse files
Files changed (1) hide show
  1. engineers/deformes3D.py +97 -28
engineers/deformes3D.py CHANGED
@@ -2,28 +2,34 @@
2
  #
3
  # Copyright (C) 2025 Carlos Rodrigues dos Santos
4
  #
5
- # Version: 1.1.1
6
  #
7
- # This file defines the Deformes3DEngine, the specialist responsible for
8
- # generating the key visual anchors (keyframes) of the story. It acts as the
9
- # "Art Director," translating narrative scenes into static images.
10
 
11
- from PIL import Image
12
  import os
13
  import time
14
  import logging
15
  import gradio as gr
16
  import yaml
 
 
17
 
18
  from managers.flux_kontext_manager import flux_kontext_singleton
19
  from engineers.deformes2D_thinker import deformes2d_thinker_singleton
 
 
 
 
20
 
21
  logger = logging.getLogger(__name__)
22
 
23
  class Deformes3DEngine:
24
  """
25
  ADUC Specialist for static image (keyframe) generation.
26
- This is responsible for the entire process of turning a script into a gallery of keyframes.
27
  """
28
  def __init__(self, workspace_dir):
29
  self.workspace_dir = workspace_dir
@@ -46,58 +52,121 @@ class Deformes3DEngine:
46
 
47
  def generate_keyframes_from_storyboard(self, storyboard: list, initial_ref_path: str, global_prompt: str, keyframe_resolution: int, general_ref_paths: list, progress_callback_factory: callable = None):
48
  """
49
- Orchestrates the generation of all keyframes from a storyboard.
 
 
50
  """
51
  current_base_image_path = initial_ref_path
52
  previous_prompt = "N/A (initial reference image)"
53
- final_keyframes = [current_base_image_path]
54
  width, height = keyframe_resolution, keyframe_resolution
 
55
 
56
  num_keyframes_to_generate = len(storyboard) - 1
57
-
58
- logger.info(f"IMAGE SPECIALIST: Received order to generate {num_keyframes_to_generate} keyframes.")
59
 
60
  for i in range(num_keyframes_to_generate):
61
  scene_index = i + 1
62
  current_scene = storyboard[i]
63
  future_scene = storyboard[i+1]
64
- progress_callback = progress_callback_factory(scene_index, num_keyframes_to_generate) if progress_callback_factory else None
65
 
66
  logger.info(f"--> Generating Keyframe {scene_index}/{num_keyframes_to_generate}...")
 
 
 
67
 
68
- # Delegate the "thinking" part to the Deformes2DThinker
69
- new_flux_prompt = deformes2d_thinker_singleton.get_anticipatory_keyframe_prompt(
70
  global_prompt=global_prompt, scene_history=previous_prompt,
71
  current_scene_desc=current_scene, future_scene_desc=future_scene,
72
  last_image_path=current_base_image_path, fixed_ref_paths=general_ref_paths
73
  )
74
 
75
- images_for_flux_paths = list(set([current_base_image_path] + general_ref_paths))
76
- images_for_flux = [Image.open(p) for p in images_for_flux_paths]
77
 
78
- # Execute the image generation
79
- new_keyframe_path = self._generate_single_keyframe(
80
- prompt=new_flux_prompt, reference_images=images_for_flux,
81
- output_filename=f"keyframe_{scene_index}.png", width=width, height=height,
82
- callback=progress_callback
83
  )
 
 
 
 
 
 
 
84
 
85
- final_keyframes.append(new_keyframe_path)
86
- current_base_image_path = new_keyframe_path
87
- previous_prompt = new_flux_prompt
88
 
89
- logger.info("IMAGE SPECIALIST: Keyframe generation complete.")
90
- return final_keyframes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
  # --- Singleton Instantiation ---
93
  try:
94
  with open("config.yaml", 'r') as f:
95
  config = yaml.safe_load(f)
96
  WORKSPACE_DIR = config['application']['workspace_dir']
97
-
98
- # Correctly instantiate the Deformes3DEngine class
99
  deformes3d_engine_singleton = Deformes3DEngine(workspace_dir=WORKSPACE_DIR)
100
-
101
  except Exception as e:
102
  logger.error(f"Could not initialize Deformes3DEngine: {e}", exc_info=True)
103
  deformes3d_engine_singleton = None
 
2
  #
3
  # Copyright (C) 2025 Carlos Rodrigues dos Santos
4
  #
5
+ # Version: 1.5.1
6
  #
7
+ # This version maintains the core FLUX-based keyframe generation and adds the
8
+ # LTX-based "enrichment" as a secondary, experimental step for each keyframe,
9
+ # allowing for direct comparison without altering the primary workflow.
10
 
11
+ from PIL import Image, ImageOps
12
  import os
13
  import time
14
  import logging
15
  import gradio as gr
16
  import yaml
17
+ import torch
18
+ import numpy as np
19
 
20
  from managers.flux_kontext_manager import flux_kontext_singleton
21
  from engineers.deformes2D_thinker import deformes2d_thinker_singleton
22
+ from aduc_types import LatentConditioningItem
23
+ from managers.ltx_manager import ltx_manager_singleton
24
+ from managers.vae_manager import vae_manager_singleton
25
+ from managers.latent_enhancer_manager import latent_enhancer_specialist_singleton
26
 
27
  logger = logging.getLogger(__name__)
28
 
29
  class Deformes3DEngine:
30
  """
31
  ADUC Specialist for static image (keyframe) generation.
32
+ Primary engine is FLUX, with an experimental LTX enrichment step.
33
  """
34
  def __init__(self, workspace_dir):
35
  self.workspace_dir = workspace_dir
 
52
 
53
  def generate_keyframes_from_storyboard(self, storyboard: list, initial_ref_path: str, global_prompt: str, keyframe_resolution: int, general_ref_paths: list, progress_callback_factory: callable = None):
54
  """
55
+ Orchestrates the generation of all keyframes. For each keyframe, first
56
+ generates a version with FLUX, and then an "enriched" version with LTX
57
+ for direct comparison.
58
  """
59
  current_base_image_path = initial_ref_path
60
  previous_prompt = "N/A (initial reference image)"
61
+ final_keyframes_gallery = [current_base_image_path]
62
  width, height = keyframe_resolution, keyframe_resolution
63
+ target_resolution_tuple = (width, height)
64
 
65
  num_keyframes_to_generate = len(storyboard) - 1
66
+ logger.info(f"IMAGE SPECIALIST: Received order to generate {num_keyframes_to_generate} keyframes (FLUX + LTX versions).")
 
67
 
68
  for i in range(num_keyframes_to_generate):
69
  scene_index = i + 1
70
  current_scene = storyboard[i]
71
  future_scene = storyboard[i+1]
72
+ progress_callback_flux = progress_callback_factory(scene_index, num_keyframes_to_generate) if progress_callback_factory else None
73
 
74
  logger.info(f"--> Generating Keyframe {scene_index}/{num_keyframes_to_generate}...")
75
+
76
+ # --- STEP A: Generate with FLUX (Primary Method) ---
77
+ logger.info(f" - Step A: Generating with FLUX...")
78
 
79
+ flux_prompt = deformes2d_thinker_singleton.get_anticipatory_keyframe_prompt(
 
80
  global_prompt=global_prompt, scene_history=previous_prompt,
81
  current_scene_desc=current_scene, future_scene_desc=future_scene,
82
  last_image_path=current_base_image_path, fixed_ref_paths=general_ref_paths
83
  )
84
 
85
+ flux_ref_paths = list(set([current_base_image_path] + general_ref_paths))
86
+ flux_ref_images = [Image.open(p) for p in flux_ref_paths]
87
 
88
+ flux_keyframe_path = self._generate_single_keyframe(
89
+ prompt=flux_prompt, reference_images=flux_ref_images,
90
+ output_filename=f"keyframe_{scene_index}_flux.png", width=width, height=height,
91
+ callback=progress_callback_flux
 
92
  )
93
+ final_keyframes_gallery.append(flux_keyframe_path)
94
+
95
+ # --- STEP B: LTX Enrichment Experiment ---
96
+ logger.info(f" - Step B: Generating enrichment with LTX...")
97
+
98
+ ltx_context_paths = list(reversed(context_paths))
99
+ logger.info(f" - LTX Context Order (Reversed): {[os.path.basename(p) for p in ltx_context_paths]}")
100
 
 
 
 
101
 
102
+ ltx_conditioning_items = []
103
+ context_paths = [current_base_image_path] + [p for p in general_ref_paths if p != current_base_image_path][:3]
104
+
105
+ weight = 0.6
106
+ for idx, path in enumerate(ltx_context_paths):
107
+ img_pil = Image.open(path).convert("RGB")
108
+ img_processed = self._preprocess_image_for_latent_conversion(img_pil, target_resolution_tuple)
109
+ pixel_tensor = self._pil_to_pixel_tensor(img_processed)
110
+ latent_tensor = vae_manager_singleton.encode(pixel_tensor)
111
+
112
+ ltx_conditioning_items.append(LatentConditioningItem(latent_tensor, 0, weight))
113
+
114
+ if idx >= 0:
115
+ weight -= 0.1
116
+
117
+ ltx_base_params = {"guidance_scale": 3.0, "stg_scale": 0.1, "num_inference_steps": 25}
118
+ generated_latents, _ = ltx_manager_singleton.generate_latent_fragment(
119
+ height=height, width=width,
120
+ conditioning_items_data=ltx_conditioning_items,
121
+ motion_prompt=flux_prompt,
122
+ video_total_frames=16,
123
+ video_fps=24,
124
+ **ltx_base_params
125
+ )
126
+
127
+ final_latent = generated_latents[:, :, -1:, :, :]
128
+ upscaled_latent = latent_enhancer_specialist_singleton.upscale(final_latent)
129
+ enriched_pixel_tensor = vae_manager_singleton.decode(upscaled_latent)
130
+
131
+ ltx_keyframe_path = os.path.join(self.workspace_dir, f"keyframe_{scene_index}_ltx.png")
132
+ self.save_image_from_tensor(enriched_pixel_tensor, ltx_keyframe_path)
133
+ final_keyframes_gallery.append(ltx_keyframe_path)
134
+
135
+ # Use the FLUX keyframe as the base for the next iteration to maintain the primary narrative path
136
+ current_base_image_path = flux_keyframe_path
137
+ previous_prompt = flux_prompt
138
+
139
+ logger.info(f"IMAGE SPECIALIST: Generation of all keyframe versions (FLUX + LTX) complete.")
140
+ return final_keyframes_gallery
141
+
142
+ # --- HELPER FUNCTIONS ---
143
+
144
+ def _preprocess_image_for_latent_conversion(self, image: Image.Image, target_resolution: tuple) -> Image.Image:
145
+ """Resizes and fits an image to the target resolution for VAE encoding."""
146
+ if image.size != target_resolution:
147
+ return ImageOps.fit(image, target_resolution, Image.Resampling.LANCZOS)
148
+ return image
149
+
150
+ def _pil_to_pixel_tensor(self, pil_image: Image.Image) -> torch.Tensor:
151
+ """Helper to convert PIL to the 5D pixel tensor the VAE expects."""
152
+ image_np = np.array(pil_image).astype(np.float32) / 255.0
153
+ tensor = torch.from_numpy(image_np).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
154
+ return (tensor * 2.0) - 1.0
155
+
156
+ def save_image_from_tensor(self, pixel_tensor: torch.Tensor, path: str):
157
+ """Helper to save a 1-frame pixel tensor as an image."""
158
+ tensor_chw = pixel_tensor.squeeze(0).squeeze(1)
159
+ tensor_hwc = tensor_chw.permute(1, 2, 0)
160
+ tensor_hwc = (tensor_hwc.clamp(-1, 1) + 1) / 2.0
161
+ image_np = (tensor_hwc.cpu().float().numpy() * 255).astype(np.uint8)
162
+ Image.fromarray(image_np).save(path)
163
 
164
  # --- Singleton Instantiation ---
165
  try:
166
  with open("config.yaml", 'r') as f:
167
  config = yaml.safe_load(f)
168
  WORKSPACE_DIR = config['application']['workspace_dir']
 
 
169
  deformes3d_engine_singleton = Deformes3DEngine(workspace_dir=WORKSPACE_DIR)
 
170
  except Exception as e:
171
  logger.error(f"Could not initialize Deformes3DEngine: {e}", exc_info=True)
172
  deformes3d_engine_singleton = None