Files
2024-11-20 08:43:43 +08:00

280 lines
11 KiB
Python

# !/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import torch
import numpy as np
from diffusers import AutoencoderKL, DDPMScheduler
from .src.referencenet.referencenet_unet_2d_condition import ReferenceNetModel
from .src.referencenet.unet_2d_condition import UNet2DConditionModel
from .src.pipelines.pipeline_referencenet import StableDiffusionReferenceNetPipeline
from .node_utils import download_weights,load_images_list,pil2narry,tensor_to_pil,tensor_upscale
from comfy.model_management import unload_all_models
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__))
# make face anon folder
weigths_face_anon_current_path = os.path.join(folder_paths.models_dir, "Face_anon_simple")
if not os.path.exists(weigths_face_anon_current_path):
os.makedirs(weigths_face_anon_current_path)
face_anon_unet_path = os.path.join(weigths_face_anon_current_path, "unet")
if not os.path.exists(face_anon_unet_path):
os.makedirs(face_anon_unet_path)
face_anon_refer_path = os.path.join(weigths_face_anon_current_path, "referencenet")
if not os.path.exists(face_anon_refer_path):
os.makedirs(face_anon_refer_path)
face_anon_cond_path = os.path.join(weigths_face_anon_current_path, "conditioning_referencenet")
if not os.path.exists(face_anon_cond_path):
os.makedirs(face_anon_cond_path)
try:
folder_paths.add_model_folder_path("Face_anon_simple", weigths_face_anon_current_path, False)
except:
folder_paths.add_model_folder_path("Face_anon_simple", weigths_face_anon_current_path)
class Face_Anon_Simple_LoadModel:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"repo": ("STRING", {"default": "hkung/face-anon-simple"}),
"vae":(["none"] + folder_paths.get_filename_list("vae"),),
"lowvram": ("BOOLEAN", {"default": True},),
}
}
RETURN_TYPES = ("FACEANON_PIPE",)
RETURN_NAMES = ("model",)
FUNCTION = "main_loader"
CATEGORY = "Face_Anon_Simple"
def main_loader(self,repo,vae,lowvram):
# base model
if repo=="hkung/face-anon-simple":
unet = UNet2DConditionModel.from_pretrained(
repo, subfolder="unet", use_safetensors=True
)
referencenet = ReferenceNetModel.from_pretrained(
repo, subfolder="referencenet", use_safetensors=True
)
conditioning_referencenet = ReferenceNetModel.from_pretrained(
repo, subfolder="conditioning_referencenet", use_safetensors=True
)
print("***use repo,load checkpoints successfully ***")
else:
try:
unet = UNet2DConditionModel.from_pretrained(
weigths_face_anon_current_path, subfolder="unet", use_safetensors=True
)
referencenet = ReferenceNetModel.from_pretrained(
weigths_face_anon_current_path, subfolder="referencenet", use_safetensors=True
)
conditioning_referencenet = ReferenceNetModel.from_pretrained(
weigths_face_anon_current_path, subfolder="conditioning_referencenet", use_safetensors=True
)
print(f"***use local {weigths_face_anon_current_path},load checkpoints successfully ***")
except:
print("***miss checkpoints ,auto dowload from hub***")
download_weights(weigths_face_anon_current_path,"hkung/face-anon-simple",subfolder="unet",pt_name="diffusion_pytorch_model.safetensors")
download_weights(weigths_face_anon_current_path, "hkung/face-anon-simple", subfolder="unet",
pt_name="config.json")
download_weights(weigths_face_anon_current_path, "hkung/face-anon-simple", subfolder="conditioning_referencenet",
pt_name="diffusion_pytorch_model.safetensors")
download_weights(weigths_face_anon_current_path, "hkung/face-anon-simple", subfolder="conditioning_referencenet",
pt_name="config.json")
download_weights(weigths_face_anon_current_path, "hkung/face-anon-simple", subfolder="referencenet",
pt_name="diffusion_pytorch_model.safetensors")
download_weights(weigths_face_anon_current_path, "hkung/face-anon-simple", subfolder="referencenet",
pt_name="config.json")
unet = UNet2DConditionModel.from_pretrained(
weigths_face_anon_current_path, subfolder="unet", use_safetensors=True
)
referencenet = ReferenceNetModel.from_pretrained(
weigths_face_anon_current_path, subfolder="referencenet", use_safetensors=True
)
conditioning_referencenet = ReferenceNetModel.from_pretrained(
weigths_face_anon_current_path, subfolder="conditioning_referencenet", use_safetensors=True
)
print(f"***download {weigths_face_anon_current_path},load checkpoints successfully ***")
# pre vae
if vae=="none":
raise "**** need choice a vae checkpoinst! ****"
vae_id = folder_paths.get_full_path("vae", vae)
vae_config = os.path.join(current_path, "sd_repo", "vae")
VAE = AutoencoderKL.from_single_file(vae_id, config=vae_config, use_safetensors=True)
print("***load vae successfully ***")
scheduler = DDPMScheduler.from_pretrained(os.path.join(current_path, "sd_repo"), subfolder="scheduler", use_safetensors=True)
pipe = StableDiffusionReferenceNetPipeline(
unet=unet,
referencenet=referencenet,
conditioning_referencenet=conditioning_referencenet,
vae=VAE,
scheduler=scheduler,
)
if lowvram:
pipe.enable_model_cpu_offload()
model={"pipe":pipe,"lowvram":lowvram}
return (model,)
class Face_Anon_Simple_Align:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"clip_vision": ("CLIP_VISION",),
"image": ("IMAGE",), # [B,H,W,C], C=3
"width": ("INT", {"default": 512, "min": 128, "max": 4096, "step": 64, "display": "number"}),
"height": ("INT", {"default": 512, "min": 128, "max": 4096, "step": 64, "display": "number"}),
"align": ("BOOLEAN", {"default": False},),
},
"optional": {
"cond_image": ("IMAGE",), # [B,H,W,C], C=3
}
}
RETURN_TYPES = ("CONDITIONING","FACEANON_ALIGN",)
RETURN_NAMES = ("condition","face_align",)
FUNCTION = "main"
CATEGORY = "Face_Anon_Simple"
def main(self,clip_vision,image,width,height, align,**kwargs):
D, _, _, _ = image.size()
if D > 1:
original_image = list(torch.chunk(image, chunks=D))[0]
else:
original_image = tensor_upscale(image,width,height)
cond=clip_vision.encode_image(original_image)["image_embeds"]
original_image=tensor_to_pil(original_image)
cond_image = kwargs.get("cond_image")
Swap_faces=False
if isinstance(cond_image,torch.Tensor):
E, _, _, _ = cond_image.size()
if E > 1:
cond_image = list(torch.chunk(cond_image, chunks=E))[0]
else:
cond_image = tensor_upscale(cond_image, width, height)
cond_cond=clip_vision.encode_image(cond_image)["image_embeds"]
cond_image = tensor_to_pil(cond_image)
Swap_faces=True
else:
cond_cond=cond
cond_image=original_image
condition={"cond":cond,"cond_cond":cond_cond,"original_image":original_image,"cond_image":cond_image,"Swap_faces":Swap_faces}
unload_all_models()
if align:
import face_alignment
face_align = face_alignment.FaceAlignment(
face_alignment.LandmarksType.TWO_D, face_detector="sfd"
)
else:
face_align=None
return (condition,face_align)
class Face_Anon_Simple_Sampler:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("FACEANON_PIPE",),
"condition": ("CONDITIONING",), # [B,H,W,C], C=3
"seed": ("INT", {"default": 0, "min": 0, "max":MAX_SEED}),
"cfg": ("FLOAT", {"default": 4.0, "min": 0.0, "max": 10.0, "step": 0.1, "round": 0.01}),
"steps": ("INT", {"default": 30, "min": 1, "max": 1000}),
"anonymization_degree": ("FLOAT", {"default": 1.25, "min": 0.01, "max": 10.0, "step": 0.01}),
},
"optional":{
"face_align": ("FACEANON_ALIGN",),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "main"
CATEGORY = "Face_Anon_Simple"
def main(self, model,condition,seed,cfg, steps,anonymization_degree,**kwargs):
conditioning_image=condition.get("cond_image")
original_image=condition.get("original_image")
image_emb = condition.get("cond")
cond_imag_emb = condition.get("cond_cond")
Swap_faces= condition.get("Swap_faces")
pipe=model.get("pipe")
lowvram=model.get("lowvram")
if not lowvram:
pipe.to(device)
face_align = kwargs.get("face_align")
if face_align:
from .node_utils import anonymize_faces_in_image
images = anonymize_faces_in_image(
image=original_image,
face_alignment=face_align,
pipe=pipe,
generator=torch.manual_seed(seed),
face_image_size=512,
num_inference_steps=steps,
guidance_scale=cfg,
anonymization_degree=anonymization_degree,
image_emb=image_emb,
cond_imag_emb=cond_imag_emb if Swap_faces else image_emb,
)
else:
images = pipe(
source_image=original_image,
conditioning_image=conditioning_image if Swap_faces else original_image,
num_inference_steps=steps,
guidance_scale=cfg,
generator=torch.manual_seed(seed),
anonymization_degree=anonymization_degree,
image_emb=image_emb,
cond_imag_emb=cond_imag_emb if Swap_faces else image_emb,
).images[0]
if not lowvram:
pipe.maybe_free_model_hooks()
torch.cuda.empty_cache()
images=load_images_list(images) if isinstance(images,list) else pil2narry(images)
return (images,)
NODE_CLASS_MAPPINGS = {
"Face_Anon_Simple_LoadModel":Face_Anon_Simple_LoadModel,
"Face_Anon_Simple_Align":Face_Anon_Simple_Align,
"Face_Anon_Simple_Sampler": Face_Anon_Simple_Sampler,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Face_Anon_Simple_LoadModel":"Face_Anon_Simple_LoadModel",
"Face_Anon_Simple_Align":"Face_Anon_Simple_Align",
"Face_Anon_Simple_Sampler": "Face_Anon_Simple_Sampler",
}