Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42d1c75b68 | |||
| bba4845dbf | |||
| c94d2aab4d | |||
| 0d0242e0ac | |||
| 806086b533 | |||
| 9977168136 | |||
| c4badb7894 | |||
| a6ccd6fd40 | |||
| abdcf4a494 | |||
| 32eeed8bd7 | |||
| d1a82d520d | |||
| 272dca83b8 |
+8
-2
@@ -62,5 +62,11 @@ def check_install() -> None:
|
|||||||
|
|
||||||
import timeit
|
import timeit
|
||||||
|
|
||||||
check_time = timeit.timeit(check_install, number=1)
|
try:
|
||||||
print(check_time)
|
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,5 +1,4 @@
|
|||||||
cython
|
cython
|
||||||
dill
|
|
||||||
ifnude
|
ifnude
|
||||||
insightface==0.7.3
|
insightface==0.7.3
|
||||||
onnx>=1.14.0
|
onnx>=1.14.0
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
protobuf>=3.20.2
|
protobuf>=3.20.2
|
||||||
cython
|
cython
|
||||||
dill
|
|
||||||
ifnude
|
ifnude
|
||||||
insightface==0.7.3
|
insightface==0.7.3
|
||||||
onnx>=1.14.0
|
onnx>=1.14.0
|
||||||
|
|||||||
+20
-15
@@ -25,21 +25,26 @@ def check_configuration() -> None:
|
|||||||
model_path = os.path.join(models_dir, model_name)
|
model_path = os.path.join(models_dir, model_name)
|
||||||
|
|
||||||
def download(url: str, path: str) -> None:
|
def download(url: str, path: str) -> None:
|
||||||
request = urllib.request.urlopen(url)
|
try:
|
||||||
total = int(request.headers.get("Content-Length", 0))
|
request = urllib.request.urlopen(url)
|
||||||
with tqdm(
|
total = int(request.headers.get("Content-Length", 0))
|
||||||
total=total,
|
with tqdm(
|
||||||
desc="Downloading inswapper model",
|
total=total,
|
||||||
unit="B",
|
desc="Downloading inswapper model",
|
||||||
unit_scale=True,
|
unit="B",
|
||||||
unit_divisor=1024,
|
unit_scale=True,
|
||||||
) as progress:
|
unit_divisor=1024,
|
||||||
urllib.request.urlretrieve(
|
) as progress:
|
||||||
url,
|
urllib.request.urlretrieve(
|
||||||
path,
|
url,
|
||||||
reporthook=lambda count, block_size, total_size: progress.update(
|
path,
|
||||||
block_size
|
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)
|
os.makedirs(models_dir, exist_ok=True)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ REFERENCE_PATH = os.path.join(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Defining the version flag for the application
|
# Defining the version flag for the application
|
||||||
VERSION_FLAG: str = "v1.2.5"
|
VERSION_FLAG: str = "v1.2.7"
|
||||||
# Defining the path for 'sd-webui-faceswaplab' inside the 'extensions' directory
|
# Defining the path for 'sd-webui-faceswaplab' inside the 'extensions' directory
|
||||||
EXTENSION_PATH = os.path.join("extensions", "sd-webui-faceswaplab")
|
EXTENSION_PATH = os.path.join("extensions", "sd-webui-faceswaplab")
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ from scripts.faceswaplab_utils import imgutils
|
|||||||
from scripts.faceswaplab_utils.models_utils import get_swap_models
|
from scripts.faceswaplab_utils.models_utils import get_swap_models
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import dill as pickle # will be removed in future versions
|
|
||||||
from scripts.faceswaplab_swapping import swapper
|
from scripts.faceswaplab_swapping import swapper
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
import re
|
import re
|
||||||
@@ -40,6 +39,7 @@ def sanitize_name(name: str) -> str:
|
|||||||
def build_face_checkpoint_and_save(
|
def build_face_checkpoint_and_save(
|
||||||
images: List[PILImage],
|
images: List[PILImage],
|
||||||
name: str,
|
name: str,
|
||||||
|
gender: Gender = Gender.AUTO,
|
||||||
overwrite: bool = False,
|
overwrite: bool = False,
|
||||||
path: Optional[str] = None,
|
path: Optional[str] = None,
|
||||||
) -> Optional[PILImage]:
|
) -> Optional[PILImage]:
|
||||||
@@ -65,7 +65,7 @@ def build_face_checkpoint_and_save(
|
|||||||
logger.error("No source faces found")
|
logger.error("No source faces found")
|
||||||
return None
|
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(
|
preview_path = os.path.join(
|
||||||
scripts.basedir(), "extensions", "sd-webui-faceswaplab", "references"
|
scripts.basedir(), "extensions", "sd-webui-faceswaplab", "references"
|
||||||
)
|
)
|
||||||
@@ -174,20 +174,13 @@ def load_face(name: str) -> Optional[Face]:
|
|||||||
|
|
||||||
if filename.endswith(".pkl"):
|
if filename.endswith(".pkl"):
|
||||||
logger.warning(
|
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("The file will be converted to .safetensors")
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"You can also use this script https://gist.github.com/glucauze/4a3c458541f2278ad801f6625e5b9d3d"
|
"You can also use this script https://gist.github.com/glucauze/4a3c458541f2278ad801f6625e5b9d3d"
|
||||||
)
|
)
|
||||||
with open(filename, "rb") as file:
|
return None
|
||||||
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
|
|
||||||
|
|
||||||
elif filename.endswith(".safetensors"):
|
elif filename.endswith(".safetensors"):
|
||||||
face = {}
|
face = {}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from scripts.faceswaplab_postprocessing.postprocessing_options import (
|
|||||||
PostProcessingOptions,
|
PostProcessingOptions,
|
||||||
)
|
)
|
||||||
from scripts.faceswaplab_utils.models_utils import get_current_swap_model
|
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 scripts.faceswaplab_inpainting.i2i_pp import img2img_diffusion
|
||||||
from modules import shared
|
from modules import shared
|
||||||
import onnxruntime
|
import onnxruntime
|
||||||
@@ -559,7 +559,7 @@ def get_faces_from_img_files(images: List[PILImage]) -> List[Face]:
|
|||||||
return faces
|
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.
|
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
|
# Compute the mean of all embeddings
|
||||||
blended_embedding = np.mean(embeddings, axis=0)
|
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
|
# Create a new Face object using the properties of the first face in the list
|
||||||
# Assign the blended embedding to the blended Face object
|
# Assign the blended embedding to the blended Face object
|
||||||
blended = ISFace(
|
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
|
return blended
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ def analyse_faces(image: PILImage, det_threshold: float = 0.5) -> Optional[str]:
|
|||||||
|
|
||||||
|
|
||||||
def build_face_checkpoint_and_save(
|
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:
|
) -> PILImage:
|
||||||
"""
|
"""
|
||||||
Builds a face checkpoint using the provided image files, performs face swapping,
|
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:
|
if not batch_files:
|
||||||
logger.error("No face found")
|
logger.error("No face found")
|
||||||
return None # type: ignore (Optional not really supported by old gradio)
|
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
|
images: list[PILImage] = [Image.open(file.name) for file in batch_files] # type: ignore
|
||||||
preview_image: PILImage | None = (
|
preview_image: PILImage | None = (
|
||||||
face_checkpoints.build_face_checkpoint_and_save(
|
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:
|
except Exception as e:
|
||||||
@@ -266,6 +269,13 @@ def tools_ui() -> None:
|
|||||||
label="Name of the character",
|
label="Name of the character",
|
||||||
elem_id="faceswaplab_build_character_name",
|
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(
|
build_overwrite = gr.Checkbox(
|
||||||
False,
|
False,
|
||||||
placeholder="overwrite",
|
placeholder="overwrite",
|
||||||
@@ -387,7 +397,7 @@ def tools_ui() -> None:
|
|||||||
compare_btn.click(compare, inputs=[img1, img2], outputs=[compare_result_text])
|
compare_btn.click(compare, inputs=[img1, img2], outputs=[compare_result_text])
|
||||||
generate_checkpoint_btn.click(
|
generate_checkpoint_btn.click(
|
||||||
build_face_checkpoint_and_save,
|
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],
|
outputs=[preview],
|
||||||
)
|
)
|
||||||
extract_btn.click(
|
extract_btn.click(
|
||||||
|
|||||||
@@ -72,10 +72,16 @@ def get_current_swap_model() -> str:
|
|||||||
models = get_swap_models()
|
models = get_swap_models()
|
||||||
model = models[0] if len(models) else None
|
model = models[0] if len(models) else None
|
||||||
logger.info("Try to use model : %s", model)
|
logger.info("Try to use model : %s", model)
|
||||||
if not os.path.isfile(model): # type: ignore
|
try:
|
||||||
logger.error("The model %s cannot be found or loaded", model)
|
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(
|
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
|
assert model is not None
|
||||||
return model
|
return model
|
||||||
|
|||||||
@@ -3,8 +3,15 @@ from numpy import uint8
|
|||||||
from insightface.app.common import Face as IFace
|
from insightface.app.common import Face as IFace
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
PILImage = Image.Image
|
PILImage = Image.Image
|
||||||
CV2ImgU8 = np.ndarray[int, np.dtype[uint8]]
|
CV2ImgU8 = np.ndarray[int, np.dtype[uint8]]
|
||||||
Face = IFace
|
Face = IFace
|
||||||
BoxCoords = Tuple[int, int, int, int]
|
BoxCoords = Tuple[int, int, int, int]
|
||||||
|
|
||||||
|
|
||||||
|
class Gender(Enum):
|
||||||
|
AUTO = -1
|
||||||
|
FEMALE = 0
|
||||||
|
MALE = 1
|
||||||
|
|||||||
Reference in New Issue
Block a user