Files

149 lines
6.6 KiB
Python

import torch
from comfy import model_management
import traceback
from . import logger # Import the central logger
from .modules.utils import comfy_image_to_cv2, cv2_to_comfy_image, KEEP_MODEL_CONFIGS
from .modules.keep_model_loader import KEEPModelLoader, KEEPModelPack
from .modules.keep_processor import KEEPFaceProcessor
GLOBAL_KEEP_MODEL_LOADER = None
def get_keep_model_loader():
global GLOBAL_KEEP_MODEL_LOADER
if GLOBAL_KEEP_MODEL_LOADER is None:
GLOBAL_KEEP_MODEL_LOADER = KEEPModelLoader()
return GLOBAL_KEEP_MODEL_LOADER
class KEEP_ModelLoaderNode:
_MODEL_TYPES = list(KEEP_MODEL_CONFIGS.keys())
@classmethod
def INPUT_TYPES(s):
detection_models = ['retinaface_resnet50', 'retinaface_mobile0.25', 'YOLOv5l', 'YOLOv5n']
return {
"required": {
"model": (s._MODEL_TYPES, {"default": s._MODEL_TYPES[0] if s._MODEL_TYPES else 'KEEP'}),
"detection_model": (detection_models, {"default": 'retinaface_resnet50'}),
},
"optional": {
"bg_upscale_model": ("UPSCALE_MODEL",),
"face_upscale_model": ("UPSCALE_MODEL",),
}
}
RETURN_TYPES = ("KEEP_MODEL_PACK",)
RETURN_NAMES = ("keep_model_pack",)
FUNCTION = "load_model_pack"
CATEGORY = "ComfyUI-KEEP"
def load_model_pack(self, model, detection_model, bg_upscale_model=None, face_upscale_model=None):
loader = get_keep_model_loader()
model_pack = loader.load_keep_model_pack(
model_type_str=model,
detection_model_str=detection_model,
bg_upscale_model=bg_upscale_model,
face_upscale_model=face_upscale_model
)
return (model_pack,)
class KEEP_FaceUpscaleImageNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"keep_model": ("KEEP_MODEL_PACK",),
"final_upscale_factor": ("FLOAT", {"default": 1.0, "min": 0.5, "max": 4.0, "step": 0.1, "tooltip": "The final upscaling factor for the output image. The image will be resized to this scale after processing."}),
"has_aligned_face": ("BOOLEAN", {"default": False, "tooltip": "Check if the input image is an already aligned 512x512 face."}),
"only_center_face": ("BOOLEAN", {"default": True, "tooltip": "If the image has multiple faces, only process the one closest to the center."}),
"draw_bounding_box": ("BOOLEAN", {"default": False, "tooltip": "Draw a bounding box around the detected face on the output image."}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "upscale_face_image"
CATEGORY = "ComfyUI-KEEP"
def upscale_face_image(self, image: torch.Tensor, keep_model: KEEPModelPack,
final_upscale_factor, has_aligned_face, only_center_face, draw_bounding_box):
if not isinstance(keep_model, KEEPModelPack):
logger.error(f"Invalid KEEP Model Pack provided. Expected KEEPModelPack, got {type(keep_model)}")
return (None,)
try:
keep_model.load_device()
cv2_img_single = comfy_image_to_cv2(image[0].unsqueeze(0))
processor = KEEPFaceProcessor(keep_model)
processed_cv2_img = processor.process_image(
cv2_image_orig=cv2_img_single,
final_upscale_factor=final_upscale_factor,
has_aligned=has_aligned_face,
only_center_face=only_center_face,
draw_box=draw_bounding_box
)
output_image_single = cv2_to_comfy_image(processed_cv2_img)
return (output_image_single,)
except Exception as e:
logger.error(f"Error processing single image: {e}")
traceback.print_exc()
return (None,)
finally:
keep_model.offload()
class KEEP_ProcessImageSequenceNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"images": ("IMAGE",),
"keep_model": ("KEEP_MODEL_PACK",),
"final_upscale_factor": ("FLOAT", {"default": 1.0, "min": 0.5, "max": 4.0, "step": 0.1, "tooltip": "The final upscaling factor for the output frames. They will be resized to this scale after processing."}),
"has_aligned_frames": ("BOOLEAN", {"default": False, "tooltip": "Check if the input frames are already aligned 512x512 faces."}),
"only_center_face": ("BOOLEAN", {"default": True, "tooltip": "If frames have multiple faces, only process the one closest to the center."}),
"draw_bounding_box": ("BOOLEAN", {"default": False, "tooltip": "Draw a bounding box around the detected face on the output frames."}),
"max_clip_length": ("INT", {"default": 20, "min":1, "max":100, "step":1, "tooltip": "Maximum number of frames to process in a single batch to manage VRAM."}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("processed_images",)
FUNCTION = "process_sequence"
CATEGORY = "ComfyUI-KEEP"
def process_sequence(self, images: torch.Tensor, keep_model: KEEPModelPack,
final_upscale_factor, has_aligned_frames, only_center_face, draw_bounding_box,
max_clip_length):
if not isinstance(keep_model, KEEPModelPack):
logger.error(f"Invalid KEEP Model Pack provided. Expected KEEPModelPack, got {type(keep_model)}")
return (None,)
try:
keep_model.load_device()
processor = KEEPFaceProcessor(keep_model)
processed_images_tensor = processor.process_image_sequence(
image_sequence_tensor=images,
final_upscale_factor=final_upscale_factor,
has_aligned_frames=has_aligned_frames,
only_center_face=only_center_face,
draw_box=draw_bounding_box,
max_clip_length=max_clip_length
)
return (processed_images_tensor,)
except Exception as e:
logger.error(f"Error during image sequence processing: {e}")
traceback.print_exc()
return (None,)
finally:
keep_model.offload()
NODE_CLASS_MAPPINGS = {
"KEEP_ModelLoader": KEEP_ModelLoaderNode,
"KEEP_FaceUpscaleImage": KEEP_FaceUpscaleImageNode,
"KEEP_ProcessImageSequence": KEEP_ProcessImageSequenceNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"KEEP_ModelLoader": "Load KEEP Models",
"KEEP_FaceUpscaleImage": "KEEP Single Image",
"KEEP_ProcessImageSequence": "KEEP Image Sequence",
}