Update api/ltx_server.py
Browse files- api/ltx_server.py +43 -54
api/ltx_server.py
CHANGED
|
@@ -473,6 +473,7 @@ class VideoService:
|
|
| 473 |
pass
|
| 474 |
|
| 475 |
|
|
|
|
| 476 |
def generate(
|
| 477 |
self,
|
| 478 |
prompt,
|
|
@@ -575,11 +576,10 @@ class VideoService:
|
|
| 575 |
print(f"[DEBUG] media_items shape={tuple(media.shape)}")
|
| 576 |
|
| 577 |
latents = None
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
# ltx_server.py (dentro da função generate)
|
| 581 |
-
|
| 582 |
try:
|
|
|
|
|
|
|
| 583 |
if improve_texture:
|
| 584 |
if not self.latent_upsampler:
|
| 585 |
raise ValueError("Upscaler espacial não carregado.")
|
|
@@ -617,7 +617,6 @@ class VideoService:
|
|
| 617 |
|
| 618 |
print(f"[DEBUG] Passo 1: Dimensões reduzidas e ajustadas para {height_p1}x{width_p1}")
|
| 619 |
|
| 620 |
-
ctx = torch.autocast(device_type="cuda", dtype=self.runtime_autocast_dtype) if self.device == "cuda" else contextlib.nullcontext()
|
| 621 |
with ctx:
|
| 622 |
first_pass_result = self.pipeline(**first_pass_kwargs)
|
| 623 |
|
|
@@ -656,14 +655,24 @@ class VideoService:
|
|
| 656 |
"skip_block_list": second_pass_args.get("skip_block_list"),
|
| 657 |
})
|
| 658 |
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
if "
|
| 666 |
-
del second_pass_kwargs["guidance_timesteps"]
|
| 667 |
|
| 668 |
second_pass_kwargs["latents"] = latents_high_res
|
| 669 |
|
|
@@ -672,8 +681,9 @@ class VideoService:
|
|
| 672 |
|
| 673 |
latents = second_pass_result.images
|
| 674 |
log_tensor_info(latents, "Latentes Finais (Passo 2)")
|
| 675 |
-
|
| 676 |
else:
|
|
|
|
| 677 |
single_pass_kwargs = call_kwargs.copy()
|
| 678 |
first_pass_config = self.config.get("first_pass", {})
|
| 679 |
single_pass_kwargs.update(
|
|
@@ -693,30 +703,22 @@ class VideoService:
|
|
| 693 |
print(f"[DEBUG] Single-pass: timesteps_len={len(schedule) if schedule else 0}")
|
| 694 |
|
| 695 |
print("\n[INFO] Executando pipeline de etapa única...")
|
| 696 |
-
t_sp = time.perf_counter()
|
| 697 |
-
ctx = torch.autocast(device_type="cuda", dtype=self.runtime_autocast_dtype) if self.device == "cuda" else contextlib.nullcontext()
|
| 698 |
with ctx:
|
| 699 |
result = self.pipeline(**single_pass_kwargs)
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
if hasattr(result, "latents"):
|
| 703 |
-
latents = result.latents
|
| 704 |
-
elif hasattr(result, "images") and isinstance(result.images, torch.Tensor):
|
| 705 |
-
latents = result.images
|
| 706 |
-
else:
|
| 707 |
-
latents = result
|
| 708 |
print(f"[DEBUG] Latentes (single-pass): shape={tuple(latents.shape)}")
|
| 709 |
|
| 710 |
-
#
|
| 711 |
|
| 712 |
latents_cpu = latents.detach().to("cpu", non_blocking=True)
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
|
|
|
| 718 |
|
| 719 |
-
# 2) Divide em duas partes
|
| 720 |
lat_a, lat_b = self._dividir_latentes(latents_cpu)
|
| 721 |
lat_a1, lat_a2 = self._dividir_latentes(lat_a)
|
| 722 |
lat_b1, lat_b2 = self._dividir_latentes(lat_b)
|
|
@@ -729,22 +731,19 @@ class VideoService:
|
|
| 729 |
partes_mp4 = []
|
| 730 |
par = 0
|
| 731 |
|
| 732 |
-
for
|
| 733 |
-
print(f"[DEBUG] Partição {par}: {tuple(
|
| 734 |
-
|
| 735 |
par = par + 1
|
| 736 |
output_video_path = os.path.join(temp_dir, f"output_{used_seed}_{par}.mp4")
|
| 737 |
-
final_output_path = None
|
| 738 |
|
| 739 |
print("[DEBUG] Decodificando bloco de latentes com VAE → tensor de pixels...")
|
| 740 |
-
# Usar manager com timestep por item; previne target_shape e rota NoneType.decode
|
| 741 |
pixel_tensor = vae_manager_singleton.decode(
|
| 742 |
-
|
| 743 |
decode_timestep=float(self.config.get("decode_timestep", 0.05))
|
| 744 |
)
|
| 745 |
log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
|
| 746 |
|
| 747 |
-
print("[DEBUG] Codificando MP4 a partir do tensor de pixels
|
| 748 |
video_encode_tool_singleton.save_video_from_tensor(
|
| 749 |
pixel_tensor,
|
| 750 |
output_video_path,
|
|
@@ -758,9 +757,7 @@ class VideoService:
|
|
| 758 |
final_output_path = candidate
|
| 759 |
print(f"[DEBUG] MP4 parte {par} movido para {final_output_path}")
|
| 760 |
partes_mp4.append(final_output_path)
|
| 761 |
-
|
| 762 |
except Exception as e:
|
| 763 |
-
final_output_path = output_video_path
|
| 764 |
print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
|
| 765 |
|
| 766 |
final_concat = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")
|
|
@@ -769,31 +766,23 @@ class VideoService:
|
|
| 769 |
self._log_gpu_memory("Fim da Geração")
|
| 770 |
return final_concat, used_seed
|
| 771 |
|
| 772 |
-
|
| 773 |
except Exception as e:
|
| 774 |
print("[DEBUG] EXCEÇÃO NA GERAÇÃO:")
|
| 775 |
print("".join(traceback.format_exception(type(e), e, e.__traceback__)))
|
| 776 |
raise
|
| 777 |
finally:
|
| 778 |
try:
|
| 779 |
-
del latents
|
| 780 |
-
except Exception:
|
| 781 |
-
pass
|
| 782 |
-
try:
|
| 783 |
-
del multi_scale_pipeline
|
| 784 |
except Exception:
|
| 785 |
pass
|
| 786 |
|
| 787 |
gc.collect()
|
| 788 |
-
|
| 789 |
-
|
| 790 |
torch.cuda.empty_cache()
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
pass
|
| 795 |
-
except Exception as e:
|
| 796 |
-
print(f"[DEBUG] Limpeza GPU no finally falhou: {e}")
|
| 797 |
|
| 798 |
try:
|
| 799 |
self.finalize(keep_paths=[])
|
|
@@ -801,4 +790,4 @@ class VideoService:
|
|
| 801 |
print(f"[DEBUG] finalize() no finally falhou: {e}")
|
| 802 |
|
| 803 |
print("Criando instância do VideoService. O carregamento do modelo começará agora...")
|
| 804 |
-
video_generation_service = VideoService()
|
|
|
|
| 473 |
pass
|
| 474 |
|
| 475 |
|
| 476 |
+
|
| 477 |
def generate(
|
| 478 |
self,
|
| 479 |
prompt,
|
|
|
|
| 576 |
print(f"[DEBUG] media_items shape={tuple(media.shape)}")
|
| 577 |
|
| 578 |
latents = None
|
| 579 |
+
|
|
|
|
|
|
|
|
|
|
| 580 |
try:
|
| 581 |
+
ctx = torch.autocast(device_type="cuda", dtype=self.runtime_autocast_dtype) if self.device == "cuda" else contextlib.nullcontext()
|
| 582 |
+
|
| 583 |
if improve_texture:
|
| 584 |
if not self.latent_upsampler:
|
| 585 |
raise ValueError("Upscaler espacial não carregado.")
|
|
|
|
| 617 |
|
| 618 |
print(f"[DEBUG] Passo 1: Dimensões reduzidas e ajustadas para {height_p1}x{width_p1}")
|
| 619 |
|
|
|
|
| 620 |
with ctx:
|
| 621 |
first_pass_result = self.pipeline(**first_pass_kwargs)
|
| 622 |
|
|
|
|
| 655 |
"skip_block_list": second_pass_args.get("skip_block_list"),
|
| 656 |
})
|
| 657 |
|
| 658 |
+
schedule_p2 = second_pass_args.get("timesteps") or second_pass_args.get("guidance_timesteps")
|
| 659 |
+
if schedule_p2:
|
| 660 |
+
timesteps_para_refinamento = schedule_p2
|
| 661 |
+
print(f"[DEBUG] Passo 2: Usando {len(timesteps_para_refinamento)} timesteps pré-definidos do config para refinamento.")
|
| 662 |
+
else:
|
| 663 |
+
strength_p2 = second_pass_args.get("strength", second_pass_args.get("denoising_strength", 0.4))
|
| 664 |
+
num_steps_passo2_total = second_pass_args.get("num_inference_steps", 20)
|
| 665 |
+
|
| 666 |
+
self.pipeline.scheduler.set_timesteps(num_steps_passo2_total, device=self.device)
|
| 667 |
+
todos_os_timesteps_p2 = self.pipeline.scheduler.timesteps
|
| 668 |
+
|
| 669 |
+
ponto_de_corte = int(len(todos_os_timesteps_p2) * (1.0 - strength_p2))
|
| 670 |
+
timesteps_para_refinamento = todos_os_timesteps_p2[ponto_de_corte:]
|
| 671 |
+
print(f"[DEBUG] Passo 2: Calculando {len(timesteps_para_refinamento)} timesteps manuais (strength ≈ {strength_p2})")
|
| 672 |
|
| 673 |
+
second_pass_kwargs["timesteps"] = timesteps_para_refinamento
|
| 674 |
+
|
| 675 |
+
if "strength" in second_pass_kwargs: del second_pass_kwargs["strength"]
|
|
|
|
| 676 |
|
| 677 |
second_pass_kwargs["latents"] = latents_high_res
|
| 678 |
|
|
|
|
| 681 |
|
| 682 |
latents = second_pass_result.images
|
| 683 |
log_tensor_info(latents, "Latentes Finais (Passo 2)")
|
| 684 |
+
|
| 685 |
else:
|
| 686 |
+
# --- PASSO ÚNICO (SINGLE-PASS) ---
|
| 687 |
single_pass_kwargs = call_kwargs.copy()
|
| 688 |
first_pass_config = self.config.get("first_pass", {})
|
| 689 |
single_pass_kwargs.update(
|
|
|
|
| 703 |
print(f"[DEBUG] Single-pass: timesteps_len={len(schedule) if schedule else 0}")
|
| 704 |
|
| 705 |
print("\n[INFO] Executando pipeline de etapa única...")
|
|
|
|
|
|
|
| 706 |
with ctx:
|
| 707 |
result = self.pipeline(**single_pass_kwargs)
|
| 708 |
+
|
| 709 |
+
latents = result.images
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
print(f"[DEBUG] Latentes (single-pass): shape={tuple(latents.shape)}")
|
| 711 |
|
| 712 |
+
# --- DECODIFICAÇÃO E CODIFICAÇÃO DE VÍDEO FINAL ---
|
| 713 |
|
| 714 |
latents_cpu = latents.detach().to("cpu", non_blocking=True)
|
| 715 |
+
if self.device == "cuda":
|
| 716 |
+
torch.cuda.empty_cache()
|
| 717 |
+
try:
|
| 718 |
+
torch.cuda.ipc_collect()
|
| 719 |
+
except Exception:
|
| 720 |
+
pass
|
| 721 |
|
|
|
|
| 722 |
lat_a, lat_b = self._dividir_latentes(latents_cpu)
|
| 723 |
lat_a1, lat_a2 = self._dividir_latentes(lat_a)
|
| 724 |
lat_b1, lat_b2 = self._dividir_latentes(lat_b)
|
|
|
|
| 731 |
partes_mp4 = []
|
| 732 |
par = 0
|
| 733 |
|
| 734 |
+
for part in latents_parts:
|
| 735 |
+
print(f"[DEBUG] Partição {par}: {tuple(part.shape)}")
|
|
|
|
| 736 |
par = par + 1
|
| 737 |
output_video_path = os.path.join(temp_dir, f"output_{used_seed}_{par}.mp4")
|
|
|
|
| 738 |
|
| 739 |
print("[DEBUG] Decodificando bloco de latentes com VAE → tensor de pixels...")
|
|
|
|
| 740 |
pixel_tensor = vae_manager_singleton.decode(
|
| 741 |
+
part.to(self.device, non_blocking=True),
|
| 742 |
decode_timestep=float(self.config.get("decode_timestep", 0.05))
|
| 743 |
)
|
| 744 |
log_tensor_info(pixel_tensor, "Pixel tensor (VAE saída)")
|
| 745 |
|
| 746 |
+
print("[DEBUG] Codificando MP4 a partir do tensor de pixels...")
|
| 747 |
video_encode_tool_singleton.save_video_from_tensor(
|
| 748 |
pixel_tensor,
|
| 749 |
output_video_path,
|
|
|
|
| 757 |
final_output_path = candidate
|
| 758 |
print(f"[DEBUG] MP4 parte {par} movido para {final_output_path}")
|
| 759 |
partes_mp4.append(final_output_path)
|
|
|
|
| 760 |
except Exception as e:
|
|
|
|
| 761 |
print(f"[DEBUG] Falha no move; usando tmp como final: {e}")
|
| 762 |
|
| 763 |
final_concat = os.path.join(results_dir, f"concat_fim_{used_seed}.mp4")
|
|
|
|
| 766 |
self._log_gpu_memory("Fim da Geração")
|
| 767 |
return final_concat, used_seed
|
| 768 |
|
|
|
|
| 769 |
except Exception as e:
|
| 770 |
print("[DEBUG] EXCEÇÃO NA GERAÇÃO:")
|
| 771 |
print("".join(traceback.format_exception(type(e), e, e.__traceback__)))
|
| 772 |
raise
|
| 773 |
finally:
|
| 774 |
try:
|
| 775 |
+
del latents, latents_low_res, latents_high_res, second_pass_result
|
|
|
|
|
|
|
|
|
|
|
|
|
| 776 |
except Exception:
|
| 777 |
pass
|
| 778 |
|
| 779 |
gc.collect()
|
| 780 |
+
if self.device == "cuda":
|
| 781 |
+
try:
|
| 782 |
torch.cuda.empty_cache()
|
| 783 |
+
torch.cuda.ipc_collect()
|
| 784 |
+
except Exception as e:
|
| 785 |
+
print(f"[DEBUG] Limpeza GPU no finally falhou: {e}")
|
|
|
|
|
|
|
|
|
|
| 786 |
|
| 787 |
try:
|
| 788 |
self.finalize(keep_paths=[])
|
|
|
|
| 790 |
print(f"[DEBUG] finalize() no finally falhou: {e}")
|
| 791 |
|
| 792 |
print("Criando instância do VideoService. O carregamento do modelo começará agora...")
|
| 793 |
+
video_generation_service = VideoService()
|