EuuIia commited on
Commit
bc04cfa
·
verified ·
1 Parent(s): 4ed8366

Update api/ltx_server.py

Browse files
Files changed (1) hide show
  1. api/ltx_server.py +98 -10
api/ltx_server.py CHANGED
@@ -108,6 +108,68 @@ def _query_gpu_processes_via_nvidiasmi(device_index: int) -> List[Dict]:
108
  continue
109
  return results
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  def _gpu_process_table(processes: List[Dict], current_pid: int) -> str:
112
  if not processes:
113
  return " - Processos ativos: (nenhum)\n"
@@ -632,8 +694,8 @@ class VideoService:
632
  target_frames_rounded = round(duration * FPS)
633
  n_val = round((float(target_frames_rounded) - 1.0) / 8.0)
634
  actual_num_frames = max(9, min(MAX_NUM_FRAMES, int(n_val * 8 + 1)))
635
- height_padded = ((height - 1) // 32 + 1) * 32
636
- width_padded = ((width - 1) // 32 + 1) * 32
637
  padding_values = calculate_padding(height, width, height_padded, width_padded)
638
  generator = torch.Generator(device=self.device).manual_seed(used_seed)
639
 
@@ -652,14 +714,29 @@ class VideoService:
652
  print(f"[DEBUG] Conditioning items: {len(conditioning_items)}")
653
 
654
  call_kwargs = {
655
- "prompt": prompt, "negative_prompt": negative_prompt,
656
- "height": height_padded, "width": width_padded, "num_frames": actual_num_frames,
657
- "frame_rate": int(FPS), "generator": generator,
 
 
 
 
 
658
  "conditioning_items": conditioning_items if conditioning_items else None,
659
- "media_items": None, # (Lógica para video-to-video omitida por clareza)
660
- # ... (outros kwargs base como antes)
 
 
 
 
 
 
 
 
 
661
  }
662
-
 
663
  latents = None
664
 
665
  try:
@@ -675,9 +752,20 @@ class VideoService:
675
 
676
  first_pass_config = self.config.get("first_pass", {}).copy()
677
  downscale_factor = self.config.get("downscale_factor", 0.666)
 
 
 
 
 
 
 
 
 
 
 
 
678
 
679
- downscaled_width = int(width_padded * downscale_factor)
680
- downscaled_height = int(height_padded * downscale_factor)
681
 
682
  first_pass_kwargs = call_kwargs.copy()
683
  first_pass_kwargs.update({
 
108
  continue
109
  return results
110
 
111
+
112
+
113
+ def calculate_new_dimensions(orig_w, orig_h, divisor=8):
114
+ """
115
+ Calcula novas dimensões mantendo a proporção, garantindo que ambos os
116
+ lados sejam divisíveis pelo divisor especificado (padrão 8).
117
+ """
118
+ if orig_w == 0 or orig_h == 0:
119
+ # Retorna um valor padrão seguro
120
+ return 512, 512
121
+
122
+ # Preserva a orientação (paisagem vs. retrato)
123
+ if orig_w >= orig_h:
124
+ # Paisagem ou quadrado
125
+ aspect_ratio = orig_w / orig_h
126
+ # Começa com uma altura base e calcula a largura
127
+ new_h = 512 # Altura base para paisagem
128
+ new_w = new_h * aspect_ratio
129
+ else:
130
+ # Retrato
131
+ aspect_ratio = orig_h / orig_w
132
+ # Começa com uma largura base e calcula a altura
133
+ new_w = 512 # Largura base para retrato
134
+ new_h = new_w * aspect_ratio
135
+
136
+ # Arredonda AMBOS os valores para o múltiplo mais próximo do divisor
137
+ final_w = int(round(new_w / divisor)) * divisor
138
+ final_h = int(round(new_h / divisor)) * divisor
139
+
140
+ # Garante que as dimensões não sejam zero após o arredondamento
141
+ final_w = max(divisor, final_w)
142
+ final_h = max(divisor, final_h)
143
+
144
+ print(f"[Dimension Calc] Original: {orig_w}x{orig_h} -> Calculado: {new_w:.0f}x{new_h:.0f} -> Final (divisível por {divisor}): {final_w}x{final_h}")
145
+ return final_h, final_w # Retorna (altura, largura)
146
+
147
+
148
+ def handle_media_upload_for_dims(filepath, current_h, current_w):
149
+ """
150
+ Esta função agora usará o novo cálculo robusto.
151
+ (O corpo desta função não precisa de alterações, pois ela já chama a função de cálculo)
152
+ """
153
+ if not filepath or not os.path.exists(str(filepath)):
154
+ return gr.update(value=current_h), gr.update(value=current_w)
155
+ try:
156
+ if str(filepath).lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
157
+ with Image.open(filepath) as img:
158
+ orig_w, orig_h = img.size
159
+ else: # Assumir que é um vídeo
160
+ with imageio.get_reader(filepath) as reader:
161
+ meta = reader.get_meta_data()
162
+ orig_w, orig_h = meta.get('size', (current_w, current_h))
163
+
164
+ # Chama a nova função corrigida
165
+ new_h, new_w = calculate_new_dimensions(orig_w, orig_h)
166
+
167
+ return gr.update(value=new_h), gr.update(value=new_w)
168
+ except Exception as e:
169
+ print(f"Erro ao processar mídia para dimensões: {e}")
170
+ return gr.update(value=current_h), gr.update(value=current_w)
171
+
172
+
173
  def _gpu_process_table(processes: List[Dict], current_pid: int) -> str:
174
  if not processes:
175
  return " - Processos ativos: (nenhum)\n"
 
694
  target_frames_rounded = round(duration * FPS)
695
  n_val = round((float(target_frames_rounded) - 1.0) / 8.0)
696
  actual_num_frames = max(9, min(MAX_NUM_FRAMES, int(n_val * 8 + 1)))
697
+ height_padded = ((height - 1) // 8 + 1) * 8
698
+ width_padded = ((width - 1) // 8 + 1) * 8
699
  padding_values = calculate_padding(height, width, height_padded, width_padded)
700
  generator = torch.Generator(device=self.device).manual_seed(used_seed)
701
 
 
714
  print(f"[DEBUG] Conditioning items: {len(conditioning_items)}")
715
 
716
  call_kwargs = {
717
+ "prompt": prompt,
718
+ "negative_prompt": negative_prompt,
719
+ "height": height_padded,
720
+ "width": width_padded,
721
+ "num_frames": actual_num_frames,
722
+ "frame_rate": int(FPS),
723
+ "generator": generator,
724
+ "output_type": "latent",
725
  "conditioning_items": conditioning_items if conditioning_items else None,
726
+ "media_items": None,
727
+ "decode_timestep": self.config["decode_timestep"],
728
+ "decode_noise_scale": self.config["decode_noise_scale"],
729
+ "stochastic_sampling": self.config["stochastic_sampling"],
730
+ "image_cond_noise_scale": 0.01,
731
+ "is_video": True,
732
+ "vae_per_channel_normalize": True,
733
+ "mixed_precision": (self.config["precision"] == "mixed_precision"),
734
+ "offload_to_cpu": False,
735
+ "enhance_prompt": False,
736
+ "skip_layer_strategy": SkipLayerStrategy.AttentionValues,
737
  }
738
+ print(f"[DEBUG] output_type={call_kwargs['output_type']} skip_layer_strategy={call_kwargs['skip_layer_strategy']}")
739
+
740
  latents = None
741
 
742
  try:
 
752
 
753
  first_pass_config = self.config.get("first_pass", {}).copy()
754
  downscale_factor = self.config.get("downscale_factor", 0.666)
755
+
756
+ unrounded_width = width_padded * downscale_factor
757
+ unrounded_height = height_padded * downscale_factor
758
+
759
+ # Sanitiza as dimensões para serem divisíveis pelo fator do VAE (geralmente 8)
760
+ # Usamos o mesmo divisor da UI para consistência.
761
+ divisor = 8
762
+ downscaled_width = int(round(unrounded_width / divisor)) * divisor
763
+ downscaled_height = int(round(unrounded_height / divisor)) * divisor
764
+
765
+ downscaled_width = max(divisor, downscaled_width)
766
+ downscaled_height = max(divisor, downscaled_height)
767
 
768
+ print(f"[DEBUG] Dimensões do First Pass: Calculado ({unrounded_width:.0f}x{unrounded_height:.0f}) -> Sanitizado ({downscaled_width}x{downscaled_height})")
 
769
 
770
  first_pass_kwargs = call_kwargs.copy()
771
  first_pass_kwargs.update({