Files
Meisoftcoltd-comfyui-sequen…/image.py
T
Meisoftcoltd 73581ac582 refactor: clean up repository to v1.0.0 and implement recursive self-queuing architecture
- Delete deprecated modules: `sequence.py`, `paths.py`, `batch.py`, `debug.py`.
- Clean `image.py`, retaining only SessionImageReceiver, SessionImageSender, and auxiliary function.
- Clean `video.py`, retaining only IncrementalVideoStitcher and WanFrameValidator.
- Clean `loop.py`, retaining only SequentialLoopStart and SequentialLoopTrigger.
- Rewrite `__init__.py` mapping explicitly to the 6 core components and bump version to 1.0.0.
2026-03-22 19:25:11 +00:00

104 lines
3.4 KiB
Python

import os
import random
import numpy as np
import torch
from PIL import Image
import folder_paths
from . import register_node
# Variable global para almacenar el último fotograma de la sesión
global_session_image = None
def tensor_to_temp_image(tensor_image, prefix="session_img"):
"""Convierte un tensor de ComfyUI [1, H, W, C] a PNG temporal para la UI."""
temp_dir = folder_paths.get_temp_directory()
filename = f"{prefix}_{random.randint(10000, 99999)}.png"
filepath = os.path.join(temp_dir, filename)
# Extraemos el primer (y único) frame del tensor y lo convertimos
img_array = 255. * tensor_image[0].cpu().numpy()
img = Image.fromarray(np.clip(img_array, 0, 255).astype(np.uint8))
img.save(filepath)
return {"filename": filename, "subfolder": "", "type": "temp"}
@register_node
class SessionImageReceiver:
"""Proporciona la imagen inicial o la última generada, detectando el inicio automáticamente."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"initial_image": ("IMAGE",),
"current_loop_index": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("current_image",)
OUTPUT_NODE = True
FUNCTION = "get_image"
CATEGORY = "🔁 Sequential Batcher/Image"
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN")
def get_image(self, initial_image, current_loop_index):
global global_session_image
# Extracción segura por si en el futuro se usa INPUT_IS_LIST
loop_idx = current_loop_index[0] if isinstance(current_loop_index, list) else current_loop_index
# LÓGICA NATIVA: Si es el primer ciclo (index 0), reiniciamos
is_first_cycle = (loop_idx == 0)
if is_first_cycle or global_session_image is None:
global_session_image = initial_image
print(f"[Sequential Batcher] Receiver: Ciclo {loop_idx}. Iniciando sesión con la imagen original.")
else:
print(f"[Sequential Batcher] Receiver: Ciclo {loop_idx}. Usando el último fotograma del ciclo anterior.")
ui_image = tensor_to_temp_image(global_session_image, "receiver")
return {"ui": {"images": [ui_image]}, "result": (global_session_image, )}
@register_node
class SessionImageSender:
"""Extrae, guarda en memoria y MUESTRA el último fotograma de un lote de vídeo."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"generated_images": ("IMAGE",),
},
}
RETURN_TYPES = ()
RETURN_NAMES = ()
OUTPUT_NODE = True
FUNCTION = "set_image"
CATEGORY = "🔁 Sequential Batcher/Image"
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN")
def set_image(self, generated_images):
global global_session_image
# Extraemos solo el último elemento preservando la dimensión del lote [1, H, W, C]
last_frame = generated_images[-1:].clone()
global_session_image = last_frame
print(f"[Sequential Batcher] Sender: Último fotograma capturado con éxito.")
# Generar vista previa para la UI
ui_image = tensor_to_temp_image(last_frame, "sender")
# Solo actualizamos la UI, no hay salida de cables ("result" no es necesario si RETURN_TYPES es vacío)
return {"ui": {"images": [ui_image]}}