Files
2025-02-12 15:32:15 +08:00

211 lines
9.3 KiB
Python

# !/usr/bin/env python
# -*- coding: UTF-8 -*-
import numpy as np
import os
import torch
from .infer import main_loader,main_sampler
from .node_utils import nomarl_upscale,tensor_upscale
import folder_paths
MAX_SEED = np.iinfo(np.int32).max
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
current_path = os.path.dirname(os.path.abspath(__file__))
weigths_SVFR_current_path = os.path.join(folder_paths.models_dir, "SVFR")
if not os.path.exists(weigths_SVFR_current_path):
os.makedirs(weigths_SVFR_current_path)
folder_paths.add_model_folder_path("SVFR", weigths_SVFR_current_path)
class SVFR_LoadModel:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
yolo_ckpt_list = [i for i in folder_paths.get_filename_list("SVFR") if
"yolo" in i]
insightface_ckpt_list = [i for i in folder_paths.get_filename_list("SVFR") if
"insightface" in i]
unet_ckpt_list = [i for i in folder_paths.get_filename_list("SVFR") if
"unet" in i]
id_ckpt_list = [i for i in folder_paths.get_filename_list("SVFR") if
"id" in i]
return {
"required": {
"checkpoints": (folder_paths.get_filename_list("checkpoints"),),
"vae": (folder_paths.get_filename_list("vae"),),
"unet": (["none"] + unet_ckpt_list,),
"yolo_ckpt": (["none"] + yolo_ckpt_list,),
"id_ckpt": (["none"] + id_ckpt_list,),
"insightface": (["none"] + insightface_ckpt_list,),
"dtype": (["fp16","bf16","fp32"],),
}
}
RETURN_TYPES = ("MODEL_SVFR",)
RETURN_NAMES = ("model",)
FUNCTION = "main_loader"
CATEGORY = "SVFR"
def main_loader(self,checkpoints,vae, unet, yolo_ckpt, id_ckpt, insightface,dtype):
I2V_repo=os.path.join(current_path, "svd_repo")
if dtype == "fp16":
weight_dtype = torch.float16
elif dtype == "fp32":
weight_dtype = torch.float32
else:
weight_dtype = torch.bfloat16
if unet == "none" or yolo_ckpt == "none" or id_ckpt == "none" or insightface == "none":
raise "need choice ckpt in menu"
else:
unet_path=folder_paths.get_full_path("SVFR",unet)
det_path=folder_paths.get_full_path("SVFR",yolo_ckpt)
id_path=folder_paths.get_full_path("SVFR",id_ckpt)
face_path=folder_paths.get_full_path("SVFR",insightface)
UNET= folder_paths.get_full_path("checkpoints",checkpoints)
vae=folder_paths.get_full_path("vae",vae)
pipe,id_linear,net_arcface,align_instance=main_loader(weight_dtype,I2V_repo,UNET,vae,unet_path,det_path,id_path,face_path,device,dtype)
print("****** Load model is done.******")
return (
{"pipe": pipe, "id_linear": id_linear, "net_arcface": net_arcface, "align_instance": align_instance, "weight_dtype": weight_dtype},)
class SVFR_Sampler:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",), # [B,H,W,C], C=3,B>1
"model": ("MODEL_SVFR",),
"seed": ("INT", {"default": 77, "min": 0, "max": MAX_SEED}),
"width": ("INT", {"default": 512, "min": 128, "max": 2048, "step": 64, "display": "number"}),
"height": ("INT", {"default": 512, "min": 128, "max": 2048, "step": 64, "display": "number"}),
"decode_chunk_size": ("INT", {"default":16, "min": 4, "max": 128, "step": 4,}),
"n_sample_frames": ("INT", {"default": 16, "min": 8, "max": 100, "step": 1,}),
"steps": ("INT", {"default": 50, "min": 1, "max": 4096, "step": 1, "display": "number"}),
"noise_aug_strength": ("FLOAT", {"default": 0.00, "min": 0.00, "max": 1.00, "step": 0.01, "round": 0.001}),
"overlap": ("INT", {"default":3, "min": 1, "max": 64}),
"min_appearance_guidance_scale": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 10.0, "step": 0.1, "round": 0.01}),
"max_appearance_guidance_scale": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 10.0, "step": 0.1, "round": 0.01}),
"i2i_noise_strength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 1.0, "step": 0.1, "round": 0.01}),
"infer_mode":(["bfr","colorization","inpainting","bfr_color","bfr_color_inpaint"],),
"save_video": ("BOOLEAN", {"default": False},),
"crop_face_region": ("BOOLEAN", {"default": True},),
},
"optional": {"mask": ("MASK",),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("images",)
FUNCTION = "sampler_main"
CATEGORY = "SVFR"
def sampler_main(self, image, model, seed, width, height,decode_chunk_size,n_sample_frames
, steps,noise_aug_strength, overlap,min_appearance_guidance_scale,max_appearance_guidance_scale, i2i_noise_strength,infer_mode,save_video,crop_face_region,**kwargs):
pipe = model.get("pipe")
id_linear = model.get("id_linear")
net_arcface = model.get("net_arcface")
align_instance = model.get("align_instance")
weight_dtype = model.get("weight_dtype")
mask=kwargs.get("mask")
if isinstance(mask,torch.Tensor): #缩放至图片尺寸
if mask.shape[-1]==64 and mask.shape[-2]==64:
raise "input mask is not a useful,looks like a default comfyUI mask"
if len(mask.shape) == 3: # 1,h,w
mask_array = mask.squeeze().mul(255).clamp(0, 255).byte().numpy()
elif len(mask.shape)==2 and mask.shape[0]!=1: # h,w
mask_array=mask.mul(255).clamp(0, 255).byte().numpy()
else:
raise "check input mask's shape"
mask_array=np.where(mask_array > 0, 255, 0)
else:
mask_array=None
video_len, _, _, _ = image.size()
if video_len < 8:
raise "input video has not much frames below 8 frame,change your input video!"
else:
tensor_list = list(torch.chunk(image, chunks=video_len))
input_frames_pil = [nomarl_upscale(i,width, height) for i in tensor_list] # tensor to pil
if infer_mode=="bfr":
task_ids=[0]
elif infer_mode=="colorization":
task_ids = [1]
elif infer_mode=="inpainting":
task_ids = [2]
elif infer_mode=="bfr_color":
task_ids = [0,1]
else:
task_ids = [0, 1,2 ]
if not isinstance(mask_array,np.ndarray) and 2 in task_ids:
raise "If use inpainting need link a mask or a batch mask in the front."
print("******** start infer *********")
images=main_sampler(pipe, align_instance, net_arcface, id_linear, folder_paths.get_output_directory(), weight_dtype,
seed,input_frames_pil,task_ids,mask_array,save_video,decode_chunk_size,noise_aug_strength,
min_appearance_guidance_scale,max_appearance_guidance_scale,
overlap,i2i_noise_strength,steps,n_sample_frames,device,crop_face_region)
#model.to("cpu")#显存不会自动释放,手动迁移,不然很容易OOM
torch.cuda.empty_cache()
return (images,)
class SVFR_img2mask:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",), # [B,H,W,C], C=3,B=1
"threshold": ("INT", {"default": 0, "min": 0, "max": 254, "step": 0, "display": "number"}),
"center_crop": ("BOOLEAN", {"default": False},),
"width": ("INT", {"default": 512, "min": 128, "max": 2048, "step": 64, "display": "number"}),
"height": ("INT", {"default": 512, "min": 128, "max": 2048, "step": 64, "display": "number"}),
}
}
RETURN_TYPES = ("MASK",)
RETURN_NAMES = ("mask",)
FUNCTION = "main"
CATEGORY = "SVFR"
def main(self, image,threshold,center_crop,width,height):
if center_crop:
image=tensor_upscale(image, width, height)
np_img=image.squeeze().mul(255).clamp(0, 255).byte().numpy()
np_img=np.mean(np_img, axis=2).astype(np.uint8)
black_threshold = 50 # 黑色阈值,小于这个值的像素被认为是黑色
white_threshold = 200 # 白色阈值,大于这个值的像素被认为是白色
black_pixels = np.sum(np_img < black_threshold) # 计算黑色像素的数量
white_pixels = np.sum(np_img > white_threshold) # 计算白色像素的数量
if black_pixels>white_pixels: #黑多白少,按白色为遮罩
out = np.where(np_img > threshold, 255, 0).astype(np.float32) / 255.0
else:
out = np.where(np_img > threshold, 0, 255).astype(np.float32) / 255.0
return (torch.from_numpy(out).unsqueeze(0),)
NODE_CLASS_MAPPINGS = {
"SVFR_LoadModel": SVFR_LoadModel,
"SVFR_Sampler": SVFR_Sampler,
"SVFR_img2mask":SVFR_img2mask,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"SVFR_LoadModel": "SVFR_LoadModel",
"SVFR_Sampler": "SVFR_Sampler",
"SVFR_img2mask":"SVFR_img2mask"
}