update readme

image
This commit is contained in:
ArtBot2023
2023-09-06 22:29:40 +08:00
parent 6a4b0eb614
commit fd7e83edfd
11 changed files with 79 additions and 11 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

+11 -11
View File
@@ -31,7 +31,7 @@ class GenerationParameterInput:
RETURN_NAMES = ("parameters", ) RETURN_NAMES = ("parameters", )
FUNCTION = "mux" FUNCTION = "mux"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def mux(self, seed, image_width, image_height, steps, cfg, sampler_name, scheduler, denoise, parameters={}): def mux(self, seed, image_width, image_height, steps, cfg, sampler_name, scheduler, denoise, parameters={}):
parameters["seed"] = seed parameters["seed"] = seed
@@ -56,7 +56,7 @@ class GenertaionParameterOutput:
RETURN_NAMES = ("parameters", "seed", "image_width", "image_height", "steps", "cfg", "sampler_name", "scheduler", "denoise", ) RETURN_NAMES = ("parameters", "seed", "image_width", "image_height", "steps", "cfg", "sampler_name", "scheduler", "denoise", )
FUNCTION = "demux" FUNCTION = "demux"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def demux(self, parameters): def demux(self, parameters):
seed = parameters["seed"] seed = parameters["seed"]
@@ -79,7 +79,7 @@ class LoadRetinaFace:
RETURN_TYPES = ("RETINAFACE", ) RETURN_TYPES = ("RETINAFACE", )
RETURN_NAMES = ("MODEL", ) RETURN_NAMES = ("MODEL", )
FUNCTION = "load" FUNCTION = "load"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def load(self): def load(self):
from facexlib.detection import init_detection_model from facexlib.detection import init_detection_model
return (init_detection_model("retinaface_resnet50", model_rootpath=self.models_dir), ) return (init_detection_model("retinaface_resnet50", model_rootpath=self.models_dir), )
@@ -102,7 +102,7 @@ class CropFace:
) )
RETURN_NAMES = ("face_image", "preview", "bbox") RETURN_NAMES = ("face_image", "preview", "bbox")
FUNCTION = "crop" FUNCTION = "crop"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def crop(self, model: RetinaFace, image: torch.Tensor, confidence: float, margin: int): def crop(self, model: RetinaFace, image: torch.Tensor, confidence: float, margin: int):
with torch.no_grad(): with torch.no_grad():
@@ -214,7 +214,7 @@ class UncropFace:
} }
RETURN_TYPES = ("IMAGE", ) RETURN_TYPES = ("IMAGE", )
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
FUNCTION = "uncrop" FUNCTION = "uncrop"
def uncrop(self, image: torch.Tensor, bbox: BBox, face: torch.Tensor, mask: torch.Tensor): def uncrop(self, image: torch.Tensor, bbox: BBox, face: torch.Tensor, mask: torch.Tensor):
bbox_face, bbox_mask = self.scale_face(face.squeeze(), mask, bbox[2]) bbox_face, bbox_mask = self.scale_face(face.squeeze(), mask, bbox[2])
@@ -265,7 +265,7 @@ class LoadBisenet:
RETURN_TYPES = ("BISENET", ) RETURN_TYPES = ("BISENET", )
FUNCTION = "load" FUNCTION = "load"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def load(self): def load(self):
from facexlib.parsing import init_parsing_model from facexlib.parsing import init_parsing_model
return (init_parsing_model("bisenet", model_rootpath=self.models_dir), ) return (init_parsing_model("bisenet", model_rootpath=self.models_dir), )
@@ -288,7 +288,7 @@ class SegFace:
) )
RETURN_NAMES = ("image", "mask") RETURN_NAMES = ("image", "mask")
FUNCTION = "segment" FUNCTION = "segment"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
# labels: 0 'background' # labels: 0 'background'
# 1 'skin', 2 'l_brow', 3 'r_brow', 4 'l_eye', 5 'r_eye', # 1 'skin', 2 'l_brow', 3 'r_brow', 4 'l_eye', 5 'r_eye',
@@ -340,7 +340,7 @@ class ImageFullBBox:
RETURN_TYPES = ("BBOX", ) RETURN_TYPES = ("BBOX", )
FUNCTION = "bbox" FUNCTION = "bbox"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def bbox(self, image: torch.Tensor): def bbox(self, image: torch.Tensor):
image = image.squeeze() image = image.squeeze()
return ((0,0,image.shape[1],image.shape[0]), ) return ((0,0,image.shape[1],image.shape[0]), )
@@ -358,7 +358,7 @@ class ColorBlend:
RETURN_TYPES = ("IMAGE", ) RETURN_TYPES = ("IMAGE", )
FUNCTION = "blend" FUNCTION = "blend"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def blend(self, blend_image: torch.Tensor, base_image: torch.Tensor, mode: Literal["Hue", "Saturation", "Color", "Luminosity"]): def blend(self, blend_image: torch.Tensor, base_image: torch.Tensor, mode: Literal["Hue", "Saturation", "Color", "Luminosity"]):
from .blend import color_blend from .blend import color_blend
return (cv2tensor(color_blend(base_image=tensor2cv(base_image), blend_image=tensor2cv(blend_image), mode=mode)), ) return (cv2tensor(color_blend(base_image=tensor2cv(base_image), blend_image=tensor2cv(blend_image), mode=mode)), )
@@ -377,7 +377,7 @@ class ExcludeFacialFeature:
RETURN_TYPES = ("IMAGE", ) RETURN_TYPES = ("IMAGE", )
FUNCTION = "exclude" FUNCTION = "exclude"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
annotation_name = ['background', annotation_name = ['background',
'skin', 'l_brow', 'r_brow', 'l_eye', 'r_eye', 'skin', 'l_brow', 'r_brow', 'l_eye', 'r_eye',
'eye_g', 'l_ear', 'r_ear', 'ear_r', 'nose', 'eye_g', 'l_ear', 'r_ear', 'ear_r', 'nose',
@@ -422,7 +422,7 @@ class MaskContour:
RETURN_TYPES = ("MASK", ) RETURN_TYPES = ("MASK", )
FUNCTION = "find_contour" FUNCTION = "find_contour"
CATEGORY = "ArtBot2023" CATEGORY = "CFaceSwap"
def find_contour(self, mask: torch.Tensor): def find_contour(self, mask: torch.Tensor):
mask_np: np.ndarray = mask.squeeze().cpu().numpy().astype('uint8') mask_np: np.ndarray = mask.squeeze().cpu().numpy().astype('uint8')
+67
View File
@@ -0,0 +1,67 @@
# CharacterFaceSwap
## Overview
Welcome to the Character Face Swap workflow! It is specialized for your target character face swap. Use LoRA and embeddings to enhance character concept in stable diffusion. Whether you're a fan of video games, anime, or photorealistic, swap face with your favorite characters in a realistic and seamless manner.
![compare](images/compare.png)
## Installation
### ControlNet
ControlNet ip2p model is used for visual conditioning, download [ip2p](https://huggingface.co/lllyasviel/ControlNet-v1-1/blob/main/control_v11e_sd15_ip2p.pth) and put it in `ComfyUI/models/controlnet`
### Character Face Swap
Recommend using [ComfyUI Manager](https://github.com/ltdrdata/ComfyUI-Manager).
Install then load workflow
For manual installation,
```bash
cd ComfyUI/custom_nodes/
git clone https://github.com/ArtBot2023/CharacterFaceSwap.git
cd CharacterFaceSwap
# if you use ported ComfyUI
../../../python_embeded/python -m pip install -r requirements
# otherwise
pip install -r requirements
```
In your ComfyUI, load workflows in `custom_nodes/CharacterFaceSwap/workflows`.
## How It Works
Choose Your Model: Choose Checkpoint and LoRA trained for your character.
<img alt="123" src="images/choose_model.png" width="300"/>
Upload Base Image and Character Face
<img alt="123" src="images/upload_face.png" width="300"/>
Text Prompt: write prompts to describe target face, use LoRA keywords and embeddings.
<img alt="123" src="images/prompt.png" width="300"/>
Generate Character Face: You can check character face generation in Preview. Download Face with Seam, and Seam Mask.
![Alt text](images/preview_face.png)
Seam Fix Inpainting: Use webui inpainting to fix seam. Check [FAQ](#faq)
<img src="images/fix_seam.png" width="300">
Upload Seamless Face: Upload inpainting result to Seamless Face, and Queue Prompt again. Done!
![Alt text](images/seamless_face.png)
## FAQ
**Q**: Why not use ComfyUI for inpainting?
**A**: ComfyUI currently have issue about inpainting models, see [issue](https://github.com/comfyanonymous/ComfyUI/issues/1186) for detail. If anyone find a solution, please notify me.
+1
View File
@@ -0,0 +1 @@
facexlib >= 0.2.5
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB