Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.2.2 : Install speed fix (wip) #46

Merged
merged 8 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ While FaceSwapLab is still under development, it has reached a good level of sta

In short:

+ **Ethical Guideline:** This extension should not be forked to create a public, easy way to bypass NSFW filtering. If you modify it for this purpose, keep it private, or you'll be banned.
+ **Ethical Guideline:** NSFW is now configurable due to performance issue. Please don't use this to do harm.
+ **License:** This software is distributed under the terms of the GNU Affero General Public License (AGPL), version 3 or later.
+ **Model License:** This software uses InsightFace's pre-trained models, which are available for non-commercial research purposes only.

Expand All @@ -24,6 +24,35 @@ More on this here : https://glucauze.github.io/sd-webui-faceswaplab/

+ Older versions of gradio don't work well with the extension. See this bug : https://github.com/glucauze/sd-webui-faceswaplab/issues/5

## Quick Start

### Simple

1. Put a face in the reference.
2. Select a face number.
3. Select "Enable."
4. Select "CodeFormer" in **Global Post-Processing** tab.

Once you're happy with some results but want to improve, the next steps are to:

+ Use advanced settings in face units (which are not as complex as they might seem, it's basically fine tuning post-processing for each faces).
+ Use pre/post inpainting to tweak the image a bit for more natural results.

### Better

1. Put a face in the reference.
2. Select a face number.
3. Select "Enable."

4. In **Post-Processing** accordeon:
+ Select "CodeFormer"
+ Select "LDSR" or a faster model "003_realSR_BSRGAN_DFOWMFC_s64w8_SwinIR-L_x4_GAN" in upscaler. See [here for a list of upscalers](https://github.com/glucauze/sd-webui-faceswaplab/discussions/29).
+ Use sharpen, color_correction and improved mask

5. Disable "CodeFormer" in **Global Post-Processing** tab (otherwise it will be applied twice)

Don't hesitate to share config in the [discussion section](https://github.com/glucauze/sd-webui-faceswaplab/discussions).

### Features

+ **Face Unit Concept**: Similar to controlNet, the program introduces the concept of a face unit. You can configure up to 10 units (3 units are the default setting) in the program settings (sd).
Expand All @@ -32,6 +61,8 @@ More on this here : https://glucauze.github.io/sd-webui-faceswaplab/

+ **Batch Processing**

+ **GPU**

+ **Inpainting Fixes** : supports “only masked” and mask inpainting.

+ **Performance Improvements**: The overall performance of the software has been enhanced.
Expand Down Expand Up @@ -62,7 +93,7 @@ More on this here : https://glucauze.github.io/sd-webui-faceswaplab/

+ **Upscaled Inswapper**: The program now includes an upscaled inswapper option, which improves results by incorporating upsampling, sharpness adjustment, and color correction before face is merged to the original image.

+ **API with typing support** :
+ **API with typing support**


## Installation
Expand Down
2 changes: 1 addition & 1 deletion check.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
autoflake --in-place --remove-unused-variables -r --remove-all-unused-imports .
mypy --install-types
mypy --non-interactive --install-types
pre-commit run --all-files
15 changes: 15 additions & 0 deletions docs/documentation.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ Once you're happy with some results but want to improve, the next steps are to:
+ Use advanced settings in face units (which are not as complex as they might seem, it's basically fine tuning post-processing for each faces).
+ Use pre/post inpainting to tweak the image a bit for more natural results.

### Getting better results

1. Put a face in the reference.
2. Select a face number.
3. Select "Enable."

4. In **Post-Processing** accordeon:
+ Select "CodeFormer"
+ Select "LDSR" or a faster model "003_realSR_BSRGAN_DFOWMFC_s64w8_SwinIR-L_x4_GAN" in upscaler. See [here for a list of upscalers](https://github.com/glucauze/sd-webui-faceswaplab/discussions/29).
+ Use sharpen, color_correction and improved mask

5. Disable "CodeFormer" in **Global Post-Processing** tab (otherwise it will be applied twice)

Don't hesitate to share config in the [discussion section](https://github.com/glucauze/sd-webui-faceswaplab/discussions).

## Main Interface

Here is the interface for FaceSwap Lab. It is available in the form of an accordion in both img2img and txt2img.
Expand Down
4 changes: 1 addition & 3 deletions docs/index.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ While FaceSwapLab is still under development, it has reached a good level of sta

In short:

+ **Ethical Guideline:** This extension should not be forked to create a public, easy way to circumvent NSFW filtering.
+ **Ethical Guideline:** This extension is **not intended to facilitate the creation of not safe for work (NSFW) or non-consensual deepfake content**. Its purpose is to bring consistency to image creation, making it easier to repair existing images, or bring characters back to life.
+ **License:** This software is distributed under the terms of the GNU Affero General Public License (AGPL), version 3 or later.
+ **Model License:** This software uses InsightFace's pre-trained models, which are available for non-commercial research purposes only.

### Ethical Guideline

This extension is **not intended to facilitate the creation of not safe for work (NSFW) or non-consensual deepfake content**. Its purpose is to bring consistency to image creation, making it easier to repair existing images, or bring characters back to life.

While the code for this extension is licensed under the AGPL in compliance with models and other source materials, it's important to stress that **we strongly discourage any attempts to fork this project to create an uncensored version**. Any modifications to the code to enable the production of such content would be contrary to the ethical guidelines we advocate for.

We will comply with European regulations regarding this type of software. As required by law, the code may include both visible and invisible watermarks. If your local laws prohibit the use of this extension, you should not use it.

From an ethical perspective, the main goal of this extension is to generate consistent images by swapping faces. It's important to note that we've done our best to integrate censorship features. However, when users can access the source code, they might bypass these censorship measures. That's why we urge users to use this extension responsibly and avoid any malicious use. We emphasize the importance of respecting people's privacy and consent when swapping faces in images. We discourage any activities that could harm others, invade their privacy, or negatively affect their well-being.
Expand Down
15 changes: 10 additions & 5 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@


def check_install() -> None:
use_gpu = getattr(
shared.cmd_opts, "faceswaplab_gpu", False
) or shared.opts.data.get("faceswaplab_use_gpu", False)
use_gpu = not getattr(shared.cmd_opts, "use-cpu", False)

if use_gpu and sys.platform != "darwin":
print("Faceswaplab : Use GPU requirements")
req_file = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "requirements-gpu.txt"
)
else:
print("Faceswaplab : Use CPU requirements")
req_file = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "requirements.txt"
)
Expand All @@ -36,6 +36,8 @@ def is_installed(package: str) -> bool:
required_version = parse(package.split(">=")[1])
return installed_version >= required_version
else:
if package_name == "opencv-python":
return launch.is_installed(package_name) or launch.is_installed("cv2")
return launch.is_installed(package_name)

print("Checking faceswaplab requirements")
Expand All @@ -54,9 +56,12 @@ def is_installed(package: str) -> bool:
except Exception as e:
print(e)
print(
f"Warning: Failed to install {package}, faceswaplab will not work."
f"Warning: Failed to install {package}, faceswaplab may not work. Try to restart server or install dependencies manually."
)
raise e


check_install()
import timeit

check_time = timeit.timeit(check_install, number=1)
print(check_time)
1 change: 1 addition & 0 deletions models.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"analyzerName":"intellisense-members-lstm-pylance","languageName":"python","identity":{"modelId":"E61945A9A512ED5E1A3EE3F1A2365B88F8FE","outputId":"E4E9EADA96734F01970E616FAB2FAC19","modifiedTimeUtc":"2020-08-11T14:06:50.811Z"},"filePath":"E61945A9A512ED5E1A3EE3F1A2365B88F8FE_E4E9EADA96734F01970E616FAB2FAC19","lastAccessTimeUtc":"2023-08-14T21:58:14.988Z"}]
1 change: 1 addition & 0 deletions requirements-gpu.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dill
ifnude
insightface==0.7.3
onnx>=1.14.0
protobuf>=3.20.2
opencv-python
pandas
pydantic
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
protobuf>=3.20.2
cython
dill
ifnude
Expand Down
46 changes: 2 additions & 44 deletions scripts/configure.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,15 @@
import os
from tqdm import tqdm
import traceback
import urllib.request
from scripts.faceswaplab_utils.faceswaplab_logging import logger
from scripts.faceswaplab_globals import *
from packaging import version
import pkg_resources
import hashlib
from scripts.faceswaplab_utils.models_utils import check_model

ALREADY_DONE = False


def check_install() -> None:
# Very ugly hack :( due to sdnext optimization not calling install.py every time if git log has not changed
import importlib.util
import sys
import os

current_dir = os.path.dirname(os.path.realpath(__file__))
check_install_path = os.path.join(current_dir, "..", "install.py")
spec = importlib.util.spec_from_file_location("check_install", check_install_path)
check_install = importlib.util.module_from_spec(spec)
sys.modules["check_install"] = check_install
spec.loader.exec_module(check_install)
check_install.check_install() # type: ignore
#### End of ugly hack :( !


def is_sha1_matching(file_path: str, expected_sha1: str) -> bool:
sha1_hash = hashlib.sha1(usedforsecurity=False)
try:
with open(file_path, "rb") as file:
for byte_block in iter(lambda: file.read(4096), b""):
sha1_hash.update(byte_block)
if sha1_hash.hexdigest() == expected_sha1:
return True
else:
return False
except Exception as e:
logger.error(
"Failed to check model hash, check the model is valid or has been downloaded adequately : %e",
e,
)
traceback.print_exc()
return False


def check_configuration() -> None:
global ALREADY_DONE

Expand Down Expand Up @@ -83,13 +47,7 @@ def download(url: str, path: str) -> None:

if not os.path.exists(model_path):
download(model_url, model_path)

if not is_sha1_matching(model_path, EXPECTED_INSWAPPER_SHA1):
logger.error(
"Suspicious sha1 for model %s, check the model is valid or has been downloaded adequately. Should be %s",
model_path,
EXPECTED_INSWAPPER_SHA1,
)
check_model()

gradio_version = pkg_resources.get_distribution("gradio").version

Expand Down
23 changes: 12 additions & 11 deletions scripts/faceswaplab.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from scripts.configure import check_configuration
from scripts.faceswaplab_utils.sd_utils import get_sd_option

check_configuration()

Expand All @@ -12,7 +13,7 @@
from scripts.faceswaplab_swapping import swapper
from scripts.faceswaplab_ui import faceswaplab_tab, faceswaplab_unit_ui
from scripts.faceswaplab_utils import faceswaplab_logging, imgutils, models_utils
from scripts.faceswaplab_utils.models_utils import get_current_model
from scripts.faceswaplab_utils.models_utils import get_current_swap_model
from scripts.faceswaplab_utils.typing import *
from scripts.faceswaplab_utils.ui_utils import dataclasses_from_flat_list
from scripts.faceswaplab_utils.faceswaplab_logging import logger, save_img_debug
Expand Down Expand Up @@ -76,7 +77,7 @@ def __init__(self) -> None:

@property
def units_count(self) -> int:
return opts.data.get("faceswaplab_units_count", 3)
return get_sd_option("faceswaplab_units_count", 3)

@property
def enabled(self) -> bool:
Expand All @@ -85,7 +86,7 @@ def enabled(self) -> bool:

@property
def keep_original_images(self) -> bool:
return opts.data.get("faceswaplab_keep_original", False)
return get_sd_option("faceswaplab_keep_original", False)

@property
def swap_in_generated_units(self) -> List[FaceSwapUnitSettings]:
Expand All @@ -99,7 +100,7 @@ def title(self) -> str:
return f"faceswaplab"

def show(self, is_img2img: bool) -> bool:
return scripts.AlwaysVisible
return scripts.AlwaysVisible # type: ignore

def ui(self, is_img2img: bool) -> List[gr.components.Component]:
with gr.Accordion(f"FaceSwapLab {VERSION_FLAG}", open=False):
Expand Down Expand Up @@ -147,7 +148,7 @@ def process(
(img, None) for img in p.init_images
]
new_inits = swapper.process_images_units(
get_current_model(),
get_current_swap_model(),
self.swap_in_source_units,
images=init_images,
force_blend=True,
Expand Down Expand Up @@ -181,7 +182,7 @@ def postprocess(
for i, (img, info) in enumerate(zip(orig_images, orig_infotexts)):
batch_index = i % p.batch_size
swapped_images = swapper.process_images_units(
get_current_model(),
get_current_swap_model(),
self.swap_in_generated_units,
images=[(img, info)],
)
Expand Down Expand Up @@ -213,8 +214,8 @@ def postprocess(
swp_img,
p.outpath_samples,
"",
p.all_seeds[batch_index],
p.all_prompts[batch_index],
p.all_seeds[batch_index], # type: ignore
p.all_prompts[batch_index], # type: ignore
opts.samples_format,
info=new_info,
p=p,
Expand All @@ -231,16 +232,16 @@ def postprocess(
text = processed.infotexts[0]
infotexts.insert(0, text)
if opts.enable_pnginfo:
grid.info["parameters"] = text
grid.info["parameters"] = text # type: ignore
images.insert(0, grid)

if opts.grid_save:
save_image(
grid,
p.outpath_grids,
"swapped-grid",
p.all_seeds[0],
p.all_prompts[0],
p.all_seeds[0], # type: ignore
p.all_prompts[0], # type: ignore
opts.grid_format,
info=text,
short_filename=not opts.grid_extended_filename,
Expand Down
5 changes: 3 additions & 2 deletions scripts/faceswaplab_api/faceswaplab_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
PostProcessingOptions,
)
from client_api import api_utils
from scripts.faceswaplab_utils.face_checkpoints_utils import (
from scripts.faceswaplab_swapping.face_checkpoints import (
build_face_checkpoint_and_save,
)
from scripts.faceswaplab_utils.typing import PILImage


def encode_to_base64(image: Union[str, Image.Image, np.ndarray]) -> str: # type: ignore
Expand Down Expand Up @@ -99,7 +100,7 @@ async def swap_face(
pp_options = None
units = get_faceswap_units_settings(request.units)

swapped_images = swapper.batch_process(
swapped_images: Optional[List[PILImage]] = swapper.batch_process(
[src_image], None, units=units, postprocess_options=pp_options
)

Expand Down
12 changes: 9 additions & 3 deletions scripts/faceswaplab_globals.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import os
from modules import scripts

# Defining the absolute path for the 'faceswaplab' directory inside 'models' directory
MODELS_DIR = os.path.abspath(os.path.join("models", "faceswaplab"))
# Defining the absolute path for the 'analysers' directory inside 'MODELS_DIR'
ANALYZER_DIR = os.path.abspath(os.path.join(MODELS_DIR, "analysers"))
# Defining the absolute path for the 'parser' directory inside 'MODELS_DIR'
FACE_PARSER_DIR = os.path.abspath(os.path.join(MODELS_DIR, "parser"))
# Defining the absolute path for the 'faces' directory inside 'MODELS_DIR'
FACES_DIR = os.path.abspath(os.path.join(MODELS_DIR, "faces"))

# Constructing the path for 'references' directory inside the 'extensions' and 'sd-webui-faceswaplab' directories, based on the base directory of scripts
REFERENCE_PATH = os.path.join(
scripts.basedir(), "extensions", "sd-webui-faceswaplab", "references"
)

VERSION_FLAG: str = "v1.2.1"
# Defining the version flag for the application
VERSION_FLAG: str = "v1.2.2"
# Defining the path for 'sd-webui-faceswaplab' inside the 'extensions' directory
EXTENSION_PATH = os.path.join("extensions", "sd-webui-faceswaplab")

# The NSFW score threshold. If any part of the image has a score greater than this threshold, the image will be considered NSFW.
NSFW_SCORE_THRESHOLD: float = 0.7
# Defining the expected SHA1 hash value for 'INSWAPPER'
EXPECTED_INSWAPPER_SHA1 = "17a64851eaefd55ea597ee41e5c18409754244c5"
6 changes: 3 additions & 3 deletions scripts/faceswaplab_inpainting/faceswaplab_inpainting.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import List
from typing import List, Optional
import gradio as gr
from client_api import api_utils

Expand All @@ -15,10 +15,10 @@ class InpaintingOptions:

@staticmethod
def from_gradio(components: List[gr.components.Component]) -> "InpaintingOptions":
return InpaintingOptions(*components)
return InpaintingOptions(*components) # type: ignore

@staticmethod
def from_api_dto(dto: api_utils.InpaintingOptions) -> "InpaintingOptions":
def from_api_dto(dto: Optional[api_utils.InpaintingOptions]) -> "InpaintingOptions":
"""
Converts a InpaintingOptions object from an API DTO (Data Transfer Object).

Expand Down
Loading
Loading