7 Commits

Author SHA1 Message Date
Tran Xen 42d1c75b68 Merge pull request #96 from glucauze/v1.2.7
v1.2.7 

    remove dill
    add warnings in model install and checking
    add gender selection in build
2023-09-10 16:06:47 +02:00
Tran Xen bba4845dbf add gender selection in build 2023-09-09 16:25:06 +02:00
Tran Xen c94d2aab4d v1.2.7 2023-09-09 10:51:36 +02:00
Tran Xen 0d0242e0ac Merge pull request #83 from glucauze/v1.2.6
fix inpainting in auto dev version
2023-08-31 14:46:25 +02:00
Tran Xen bf8ef5e9ec fix inpainting in auto dev version 2023-08-30 17:08:33 +02:00
Tran Xen 806086b533 Merge pull request #71 from glucauze/v1.2.5
add seed selection
2023-08-25 10:48:03 +02:00
Tran Xen 38f46d3f01 add seed selection 2023-08-24 23:56:03 +02:00
19 changed files with 194 additions and 182 deletions
+10
View File
@@ -1,3 +1,13 @@
# 1.2.5
Allow seed selection in inpainting.
# 1.2.4
Fix default settings by marking only managed field as do_not_save.
See the discussion here : https://github.com/glucauze/sd-webui-faceswaplab/issues/62
# 1.2.3
Speed up ui : change the way default settings are manage by not storing them in ui-config.json
+26
View File
@@ -32,6 +32,32 @@ More on this here : https://glucauze.github.io/sd-webui-faceswaplab/
## Quick Start
Here are some gifs to explain (non cherry picked, just random pictures) :
## Simple Usage (roop like)
This use codeformer on all faces (including non swapped)
[simple.webm](https://github.com/glucauze/sd-webui-faceswaplab/assets/137925069/de00b685-d441-44f9-bae3-71cd7abef113)
## Advanced options
This is use to improve results. This use upscaling and codeformer only on swapped faces
[advanced.webm](https://github.com/glucauze/sd-webui-faceswaplab/assets/137925069/50630311-bd25-487f-871b-0a44eecd435d)
## Inpainting
This add inpainting on faces :
[inpainting.webm](https://github.com/glucauze/sd-webui-faceswaplab/assets/137925069/3d3508e9-5be4-4566-8c41-8301b2d08355)
## Build and use checkpoints :
[build.webm](https://github.com/glucauze/sd-webui-faceswaplab/assets/137925069/e84e9a3c-840d-4536-9fbb-09ed256406d7)
### Simple
1. Put a face in the reference.
+1
View File
@@ -48,6 +48,7 @@ class InpaintingOptions(BaseModel):
inpainting_model: str = Field(
description="Inpainting model", examples=["Current"], default="Current"
)
inpainting_seed: int = Field(description="Inpainting Seed", ge=-1, default=-1)
class InswappperOptions(BaseModel):
Binary file not shown.
+8 -9
View File
@@ -7,13 +7,6 @@ from packaging.version import parse
def check_install() -> None:
use_gpu = True
try:
from modules import shared
use_gpu = not getattr(shared.cmd_opts, "use-cpu", False)
except:
# On some platform previous lines may failed (modules.shared not initialized), just ignore and use GPU requirements
pass
if use_gpu and sys.platform != "darwin":
print("Faceswaplab : Use GPU requirements")
@@ -69,5 +62,11 @@ def check_install() -> None:
import timeit
check_time = timeit.timeit(check_install, number=1)
print(check_time)
try:
check_time = timeit.timeit(check_install, number=1)
print(check_time)
except Exception as e:
print("FaceswapLab install failed", e)
print(
"You can try to install dependencies manually by activating venv and installing requirements.txt or requirements-gpu.txt"
)
-1
View File
@@ -1,5 +1,4 @@
cython
dill
ifnude
insightface==0.7.3
onnx>=1.14.0
-1
View File
@@ -1,6 +1,5 @@
protobuf>=3.20.2
cython
dill
ifnude
insightface==0.7.3
onnx>=1.14.0
+20 -15
View File
@@ -25,21 +25,26 @@ def check_configuration() -> None:
model_path = os.path.join(models_dir, model_name)
def download(url: str, path: str) -> None:
request = urllib.request.urlopen(url)
total = int(request.headers.get("Content-Length", 0))
with tqdm(
total=total,
desc="Downloading inswapper model",
unit="B",
unit_scale=True,
unit_divisor=1024,
) as progress:
urllib.request.urlretrieve(
url,
path,
reporthook=lambda count, block_size, total_size: progress.update(
block_size
),
try:
request = urllib.request.urlopen(url)
total = int(request.headers.get("Content-Length", 0))
with tqdm(
total=total,
desc="Downloading inswapper model",
unit="B",
unit_scale=True,
unit_divisor=1024,
) as progress:
urllib.request.urlretrieve(
url,
path,
reporthook=lambda count, block_size, total_size: progress.update(
block_size
),
)
except:
logger.error(
"Failed to download inswapper_128.onnx model, please download it manually and put it in the (<sdwebui>/models/faceswaplab/inswapper_128.onnx) directory"
)
os.makedirs(models_dir, exist_ok=True)
+1 -1
View File
@@ -16,7 +16,7 @@ REFERENCE_PATH = os.path.join(
)
# Defining the version flag for the application
VERSION_FLAG: str = "v1.2.4"
VERSION_FLAG: str = "v1.2.7"
# Defining the path for 'sd-webui-faceswaplab' inside the 'extensions' directory
EXTENSION_PATH = os.path.join("extensions", "sd-webui-faceswaplab")
@@ -12,6 +12,7 @@ class InpaintingOptions:
inpainting_steps: int = 20
inpainting_sampler: str = "Euler"
inpainting_model: str = "Current"
inpainting_seed: int = -1
@staticmethod
def from_gradio(components: List[gr.components.Component]) -> "InpaintingOptions":
@@ -38,4 +39,5 @@ class InpaintingOptions:
inpainting_steps=dto.inpainting_steps,
inpainting_sampler=dto.inpainting_sampler,
inpainting_model=dto.inpainting_model,
inpainting_seed=dto.inpainting_seed,
)
+3 -11
View File
@@ -52,6 +52,7 @@ inpainting_steps : {options.inpainting_steps}
)
i2i_kwargs = {
"init_images": [img],
"sampler_name": options.inpainting_sampler,
"do_not_save_samples": True,
"steps": options.inpainting_steps,
@@ -63,17 +64,8 @@ inpainting_steps : {options.inpainting_steps}
"prompt": prompt,
"negative_prompt": negative_prompt,
"denoising_strength": options.inpainting_denoising_strengh,
"seed": options.inpainting_seed,
}
# Remove the following as they are not always supported on all platform :
# "override_settings": {
# "return_mask_composite": False,
# "save_images_before_face_restoration": False,
# "save_images_before_highres_fix": False,
# "save_images_before_color_correction": False,
# "save_mask": False,
# "save_mask_composite": False,
# "samples_save": False,
# },
current_model_checkpoint = shared.opts.sd_model_checkpoint
if options.inpainting_model and options.inpainting_model != "Current":
@@ -81,7 +73,7 @@ inpainting_steps : {options.inpainting_steps}
shared.opts.sd_model_checkpoint = options.inpainting_model
sd_models.select_checkpoint
sd_models.load_model()
i2i_p = StableDiffusionProcessingImg2Img([img], **i2i_kwargs)
i2i_p = StableDiffusionProcessingImg2Img(**i2i_kwargs)
i2i_processed = processing.process_images(i2i_p)
if options.inpainting_model and options.inpainting_model != "Current":
# Restore checkpoint
@@ -14,7 +14,6 @@ from scripts.faceswaplab_utils import imgutils
from scripts.faceswaplab_utils.models_utils import get_swap_models
import traceback
import dill as pickle # will be removed in future versions
from scripts.faceswaplab_swapping import swapper
from pprint import pformat
import re
@@ -40,6 +39,7 @@ def sanitize_name(name: str) -> str:
def build_face_checkpoint_and_save(
images: List[PILImage],
name: str,
gender: Gender = Gender.AUTO,
overwrite: bool = False,
path: Optional[str] = None,
) -> Optional[PILImage]:
@@ -65,7 +65,7 @@ def build_face_checkpoint_and_save(
logger.error("No source faces found")
return None
blended_face: Optional[Face] = swapper.blend_faces(faces)
blended_face: Optional[Face] = swapper.blend_faces(faces, gender=gender)
preview_path = os.path.join(
scripts.basedir(), "extensions", "sd-webui-faceswaplab", "references"
)
@@ -174,20 +174,13 @@ def load_face(name: str) -> Optional[Face]:
if filename.endswith(".pkl"):
logger.warning(
"Pkl files for faces are deprecated to enhance safety, they will be unsupported in future versions."
"Pkl files for faces are deprecated to enhance safety, you need to convert them"
)
logger.warning("The file will be converted to .safetensors")
logger.warning(
"You can also use this script https://gist.github.com/glucauze/4a3c458541f2278ad801f6625e5b9d3d"
)
with open(filename, "rb") as file:
logger.info("Load pkl")
face = Face(pickle.load(file))
logger.warning(
"Convert to safetensors, you can remove the pkl version once you have ensured that the safetensor is working"
)
save_face(face, filename.replace(".pkl", ".safetensors"))
return face
return None
elif filename.endswith(".safetensors"):
face = {}
+12 -3
View File
@@ -33,7 +33,7 @@ from scripts.faceswaplab_postprocessing.postprocessing_options import (
PostProcessingOptions,
)
from scripts.faceswaplab_utils.models_utils import get_current_swap_model
from scripts.faceswaplab_utils.typing import CV2ImgU8, PILImage, Face
from scripts.faceswaplab_utils.typing import CV2ImgU8, Gender, PILImage, Face
from scripts.faceswaplab_inpainting.i2i_pp import img2img_diffusion
from modules import shared
import onnxruntime
@@ -559,7 +559,7 @@ def get_faces_from_img_files(images: List[PILImage]) -> List[Face]:
return faces
def blend_faces(faces: List[Face]) -> Optional[Face]:
def blend_faces(faces: List[Face], gender: Gender = Gender.AUTO) -> Optional[Face]:
"""
Blends the embeddings of multiple faces into a single face.
@@ -587,10 +587,19 @@ def blend_faces(faces: List[Face]) -> Optional[Face]:
# Compute the mean of all embeddings
blended_embedding = np.mean(embeddings, axis=0)
if gender == Gender.AUTO:
int_gender: int = faces[0].gender # type: ignore
else:
int_gender: int = gender.value
assert -1 < int_gender < 2, "wrong gender"
logger.info("Int Gender : %s", int_gender)
# Create a new Face object using the properties of the first face in the list
# Assign the blended embedding to the blended Face object
blended = ISFace(
embedding=blended_embedding, gender=faces[0].gender, age=faces[0].age
embedding=blended_embedding, gender=int_gender, age=faces[0].age
)
return blended
@@ -4,68 +4,71 @@ from modules import sd_models, sd_samplers
from scripts.faceswaplab_utils.sd_utils import get_sd_option
def face_inpainting_ui(
name: str, id_prefix: str = "faceswaplab", description: str = ""
) -> List[gr.components.Component]:
with gr.Accordion(name, open=False):
gr.Markdown(description)
inpainting_denoising_strength = gr.Slider(
0,
def face_inpainting_ui(id_prefix: str = "faceswaplab") -> List[gr.components.Component]:
inpainting_denoising_strength = gr.Slider(
0,
1,
0,
step=0.01,
elem_id=f"{id_prefix}_pp_inpainting_denoising_strength",
label="Denoising strenght",
)
inpainting_denoising_prompt = gr.Textbox(
get_sd_option(
"faceswaplab_pp_default_inpainting_prompt", "Portrait of a [gender]"
),
elem_id=f"{id_prefix}_pp_inpainting_denoising_prompt",
label="Inpainting prompt use [gender] instead of men or woman",
)
inpainting_denoising_negative_prompt = gr.Textbox(
get_sd_option("faceswaplab_pp_default_inpainting_negative_prompt", "blurry"),
elem_id=f"{id_prefix}_pp_inpainting_denoising_neg_prompt",
label="Inpainting negative prompt use [gender] instead of men or woman",
)
with gr.Row():
samplers_names = [s.name for s in sd_samplers.all_samplers]
inpainting_sampler = gr.Dropdown(
choices=samplers_names,
value=[samplers_names[0]],
label="Inpainting Sampler",
elem_id=f"{id_prefix}_pp_inpainting_sampler",
)
inpainting_denoising_steps = gr.Slider(
1,
0,
step=0.01,
elem_id=f"{id_prefix}_pp_inpainting_denoising_strength",
label="Denoising strenght",
150,
20,
step=1,
label="Inpainting steps",
elem_id=f"{id_prefix}_pp_inpainting_steps",
)
inpainting_denoising_prompt = gr.Textbox(
get_sd_option(
"faceswaplab_pp_default_inpainting_prompt", "Portrait of a [gender]"
),
elem_id=f"{id_prefix}_pp_inpainting_denoising_prompt",
label="Inpainting prompt use [gender] instead of men or woman",
)
inpainting_denoising_negative_prompt = gr.Textbox(
get_sd_option(
"faceswaplab_pp_default_inpainting_negative_prompt", "blurry"
),
elem_id=f"{id_prefix}_pp_inpainting_denoising_neg_prompt",
label="Inpainting negative prompt use [gender] instead of men or woman",
)
with gr.Row():
samplers_names = [s.name for s in sd_samplers.all_samplers]
inpainting_sampler = gr.Dropdown(
choices=samplers_names,
value=[samplers_names[0]],
label="Inpainting Sampler",
elem_id=f"{id_prefix}_pp_inpainting_sampler",
)
inpainting_denoising_steps = gr.Slider(
1,
150,
20,
step=1,
label="Inpainting steps",
elem_id=f"{id_prefix}_pp_inpainting_steps",
)
inpaiting_model = gr.Dropdown(
choices=["Current"] + sd_models.checkpoint_tiles(),
default="Current",
label="sd model (experimental)",
elem_id=f"{id_prefix}_pp_inpainting_sd_model",
)
inpaiting_model = gr.Dropdown(
choices=["Current"] + sd_models.checkpoint_tiles(),
default="Current",
label="sd model (experimental)",
elem_id=f"{id_prefix}_pp_inpainting_sd_model",
)
inpaiting_seed = gr.Number(
label="Inpainting seed",
value=0,
minimum=0,
precision=0,
elem_id=f"{id_prefix}_pp_inpainting_seed",
)
gradio_components: List[gr.components.Component] = [
inpainting_denoising_strength,
inpainting_denoising_prompt,
inpainting_denoising_negative_prompt,
inpainting_denoising_steps,
inpainting_sampler,
inpaiting_model,
]
gradio_components: List[gr.components.Component] = [
inpainting_denoising_strength,
inpainting_denoising_prompt,
inpainting_denoising_negative_prompt,
inpainting_denoising_steps,
inpainting_sampler,
inpaiting_model,
inpaiting_seed,
]
for component in gradio_components:
setattr(component, "do_not_save_to_config", True)
for component in gradio_components:
setattr(component, "do_not_save_to_config", True)
return gradio_components
return gradio_components
@@ -1,9 +1,9 @@
from typing import List
import gradio as gr
import modules
from modules import shared, sd_models
from modules import shared
from scripts.faceswaplab_postprocessing.postprocessing_options import InpaintingWhen
from scripts.faceswaplab_utils.sd_utils import get_sd_option
from scripts.faceswaplab_ui.faceswaplab_inpainting_ui import face_inpainting_ui
def postprocessing_ui() -> List[gr.components.Component]:
@@ -66,9 +66,10 @@ def postprocessing_ui() -> List[gr.components.Component]:
label="Upscaler visibility (if scale = 1)",
elem_id="faceswaplab_pp_upscaler_visibility",
)
with gr.Accordion(f"Post Inpainting", open=True):
with gr.Accordion(label="Global-Inpainting (all faces)", open=False):
gr.Markdown(
"""Inpainting sends image to inpainting with a mask on face (once for each faces)."""
"Inpainting sends image to inpainting with a mask on face (once for each faces)."
)
inpainting_when = gr.Dropdown(
elem_id="faceswaplab_pp_inpainting_when",
@@ -76,52 +77,8 @@ def postprocessing_ui() -> List[gr.components.Component]:
value=[InpaintingWhen.BEFORE_RESTORE_FACE.value],
label="Enable/When",
)
inpainting_denoising_strength = gr.Slider(
0,
1,
0,
step=0.01,
elem_id="faceswaplab_pp_inpainting_denoising_strength",
label="Denoising strenght (will send face to img2img after processing)",
)
global_inpainting = face_inpainting_ui("faceswaplab_gpp")
inpainting_denoising_prompt = gr.Textbox(
get_sd_option(
"faceswaplab_pp_default_inpainting_prompt", "Portrait of a [gender]"
),
elem_id="faceswaplab_pp_inpainting_denoising_prompt",
label="Inpainting prompt use [gender] instead of men or woman",
)
inpainting_denoising_negative_prompt = gr.Textbox(
get_sd_option(
"faceswaplab_pp_default_inpainting_negative_prompt", "blurry"
),
elem_id="faceswaplab_pp_inpainting_denoising_neg_prompt",
label="Inpainting negative prompt use [gender] instead of men or woman",
)
with gr.Row():
samplers_names = [s.name for s in modules.sd_samplers.all_samplers] # type: ignore
inpainting_sampler = gr.Dropdown(
choices=samplers_names,
value=[samplers_names[0]],
label="Inpainting Sampler",
elem_id="faceswaplab_pp_inpainting_sampler",
)
inpainting_denoising_steps = gr.Slider(
1,
150,
20,
step=1,
label="Inpainting steps",
elem_id="faceswaplab_pp_inpainting_steps",
)
inpaiting_model = gr.Dropdown(
choices=["Current"] + sd_models.checkpoint_tiles(),
default="Current",
label="sd model (experimental)",
elem_id="faceswaplab_pp_inpainting_sd_model",
)
components = [
face_restorer_name,
face_restorer_visibility,
@@ -130,13 +87,7 @@ def postprocessing_ui() -> List[gr.components.Component]:
upscaler_scale,
upscaler_visibility,
inpainting_when,
inpainting_denoising_strength,
inpainting_denoising_prompt,
inpainting_denoising_negative_prompt,
inpainting_denoising_steps,
inpainting_sampler,
inpaiting_model,
]
] + global_inpainting
# Ask sd to not store in ui-config.json
for component in components:
+13 -3
View File
@@ -137,7 +137,7 @@ def analyse_faces(image: PILImage, det_threshold: float = 0.5) -> Optional[str]:
def build_face_checkpoint_and_save(
batch_files: List[gr.File], name: str, overwrite: bool
batch_files: List[gr.File], name: str, str_gender: str, overwrite: bool
) -> PILImage:
"""
Builds a face checkpoint using the provided image files, performs face swapping,
@@ -156,10 +156,13 @@ def build_face_checkpoint_and_save(
if not batch_files:
logger.error("No face found")
return None # type: ignore (Optional not really supported by old gradio)
gender = getattr(Gender, str_gender)
logger.info("Choosen gender : %s", gender)
images: list[PILImage] = [Image.open(file.name) for file in batch_files] # type: ignore
preview_image: PILImage | None = (
face_checkpoints.build_face_checkpoint_and_save(
images=images, name=name, overwrite=overwrite
images=images, name=name, overwrite=overwrite, gender=gender
)
)
except Exception as e:
@@ -266,6 +269,13 @@ def tools_ui() -> None:
label="Name of the character",
elem_id="faceswaplab_build_character_name",
)
build_gender = gr.Dropdown(
value=Gender.AUTO.name,
choices=[e.name for e in Gender],
placeholder="Gender of the character",
label="Gender of the character",
elem_id="faceswaplab_build_character_gender",
)
build_overwrite = gr.Checkbox(
False,
placeholder="overwrite",
@@ -387,7 +397,7 @@ def tools_ui() -> None:
compare_btn.click(compare, inputs=[img1, img2], outputs=[compare_result_text])
generate_checkpoint_btn.click(
build_face_checkpoint_and_save,
inputs=[build_batch_files, build_name, build_overwrite],
inputs=[build_batch_files, build_name, build_gender, build_overwrite],
outputs=[preview],
)
extract_btn.click(
+10 -10
View File
@@ -249,19 +249,19 @@ Otherwise, read the [doc](https://glucauze.github.io/sd-webui-faceswaplab/doc/)
elem_id=f"{id_prefix}_face{unit_num}_min_ref_similarity",
)
pre_inpainting = face_inpainting_ui(
name="Pre-Inpainting (Before swapping)",
id_prefix=f"{id_prefix}_face{unit_num}_preinpainting",
description="Pre-inpainting sends face to inpainting before swapping",
)
with gr.Accordion(label="Pre-Inpainting (before swapping)", open=False):
gr.Markdown("Pre-inpainting sends face to inpainting before swapping")
pre_inpainting = face_inpainting_ui(
id_prefix=f"{id_prefix}_face{unit_num}_preinpainting",
)
options = faceswap_unit_advanced_options(is_img2img, unit_num, id_prefix)
post_inpainting = face_inpainting_ui(
name="Post-Inpainting (After swapping)",
id_prefix=f"{id_prefix}_face{unit_num}_postinpainting",
description="Post-inpainting sends face to inpainting after swapping",
)
with gr.Accordion(label="Post-Inpainting (After swapping)", open=False):
gr.Markdown("Pre-inpainting sends face to inpainting before swapping")
post_inpainting = face_inpainting_ui(
id_prefix=f"{id_prefix}_face{unit_num}_postinpainting",
)
gradio_components: List[gr.components.Component] = (
[
+9 -3
View File
@@ -72,10 +72,16 @@ def get_current_swap_model() -> str:
models = get_swap_models()
model = models[0] if len(models) else None
logger.info("Try to use model : %s", model)
if not os.path.isfile(model): # type: ignore
logger.error("The model %s cannot be found or loaded", model)
try:
if not model or not os.path.isfile(model): # type: ignore
logger.error("The model %s cannot be found or loaded", model)
raise FileNotFoundError(
"No faceswap model found. Please add it to the faceswaplab directory. Ensure the model is in the proper directory (<sdwebui>/models/faceswaplab/inswapper_128.onnx)"
)
except:
raise FileNotFoundError(
"No faceswap model found. Please add it to the faceswaplab directory."
"No faceswap model found. Please add it to the faceswaplab directory. Ensure the model is in the proper directory (<sdwebui>/models/faceswaplab/inswapper_128.onnx)"
)
assert model is not None
return model
+7
View File
@@ -3,8 +3,15 @@ from numpy import uint8
from insightface.app.common import Face as IFace
from PIL import Image
import numpy as np
from enum import Enum
PILImage = Image.Image
CV2ImgU8 = np.ndarray[int, np.dtype[uint8]]
Face = IFace
BoxCoords = Tuple[int, int, int, int]
class Gender(Enum):
AUTO = -1
FEMALE = 0
MALE = 1