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

V.1.2.0 rc #19

Merged
merged 7 commits into from
Aug 3, 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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# 1.2.0 :

This version changes quite a few things.

+ The upscaled inswapper options are now moved to each face unit. This makes it possible to fine-tune the settings for each face.

+ Upscaled inswapper configuration in sd now concerns default values in each unit's interface.

+ Pre- and post-inpainting is now possible for each face. Here too, default options are set in the main sd settings.

+ Codeformer is no longer the default in post-processing. Don't be surprised if you get bad results by default. You can set it to default in the application's global settings

Bug fixes :

+ The problem of saving the grid should be solved.
+ The downscaling problem for inpainting should be solved.
+ Change model download logic and add checksum. This should prevent some bugs.

In terms of the API, it is now possible to create a remote checkpoint and use it in units. See the example in client_api or the tests in the tests directory.

# 1.1.2 :

+ Switch face checkpoint format from pkl to safetensors
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ 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 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.
+ **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.

More on this here : https://glucauze.github.io/sd-webui-faceswaplab/

### Known problems (wontfix):

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

### 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 Down
121 changes: 92 additions & 29 deletions client_api/api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import List, Tuple, Optional
import numpy as np
import requests
import safetensors


class InpaintingWhen(Enum):
Expand All @@ -18,6 +19,54 @@ class InpaintingWhen(Enum):
AFTER_ALL = "After All"


class InpaintingOptions(BaseModel):
inpainting_denoising_strengh: float = Field(
description="Inpainting denoising strenght", default=0, lt=1, ge=0
)
inpainting_prompt: str = Field(
description="Inpainting denoising strenght",
examples=["Portrait of a [gender]"],
default="Portrait of a [gender]",
)
inpainting_negative_prompt: str = Field(
description="Inpainting denoising strenght",
examples=[
"Deformed, blurry, bad anatomy, disfigured, poorly drawn face, mutation"
],
default="",
)
inpainting_steps: int = Field(
description="Inpainting steps",
examples=["Portrait of a [gender]"],
ge=1,
le=150,
default=20,
)
inpainting_sampler: str = Field(
description="Inpainting sampler", examples=["Euler"], default="Euler"
)
inpainting_model: str = Field(
description="Inpainting model", examples=["Current"], default="Current"
)


class InswappperOptions(BaseModel):
face_restorer_name: str = Field(
description="face restorer name", default="CodeFormer"
)
restorer_visibility: float = Field(
description="face restorer visibility", default=1, le=1, ge=0
)
codeformer_weight: float = Field(
description="face restorer codeformer weight", default=1, le=1, ge=0
)
upscaler_name: str = Field(description="upscaler name", default=None)
improved_mask: bool = Field(description="Use Improved Mask", default=False)
color_corrections: bool = Field(description="Use Color Correction", default=False)
sharpen: bool = Field(description="Sharpen Image", default=False)
erosion_factor: float = Field(description="Erosion Factor", default=1, le=10, ge=0)


class FaceSwapUnit(BaseModel):
# The image given in reference
source_img: str = Field(
Expand Down Expand Up @@ -82,6 +131,21 @@ class FaceSwapUnit(BaseModel):
default=0,
)

pre_inpainting: Optional[InpaintingOptions] = Field(
description="Inpainting options",
default=None,
)

swapping_options: Optional[InswappperOptions] = Field(
description="PostProcessing & Mask options",
default=None,
)

post_inpainting: Optional[InpaintingOptions] = Field(
description="Inpainting options",
default=None,
)

def get_batch_images(self) -> List[Image.Image]:
images = []
if self.batch_images:
Expand All @@ -104,39 +168,15 @@ class PostProcessingOptions(BaseModel):
upscaler_visibility: float = Field(
description="upscaler visibility", default=1, le=1, ge=0
)

inpainting_denoising_strengh: float = Field(
description="Inpainting denoising strenght", default=0, lt=1, ge=0
)
inpainting_prompt: str = Field(
description="Inpainting denoising strenght",
examples=["Portrait of a [gender]"],
default="Portrait of a [gender]",
)
inpainting_negative_prompt: str = Field(
description="Inpainting denoising strenght",
examples=[
"Deformed, blurry, bad anatomy, disfigured, poorly drawn face, mutation"
],
default="",
)
inpainting_steps: int = Field(
description="Inpainting steps",
examples=["Portrait of a [gender]"],
ge=1,
le=150,
default=20,
)
inpainting_sampler: str = Field(
description="Inpainting sampler", examples=["Euler"], default="Euler"
)
inpainting_when: InpaintingWhen = Field(
description="When inpainting happens",
examples=[e.value for e in InpaintingWhen.__members__.values()],
default=InpaintingWhen.NEVER,
)
inpainting_model: str = Field(
description="Inpainting model", examples=["Current"], default="Current"

inpainting_options: Optional[InpaintingOptions] = Field(
description="Inpainting options",
default=None,
)


Expand All @@ -147,7 +187,7 @@ class FaceSwapRequest(BaseModel):
default=None,
)
units: List[FaceSwapUnit]
postprocessing: Optional[PostProcessingOptions]
postprocessing: Optional[PostProcessingOptions] = None


class FaceSwapResponse(BaseModel):
Expand Down Expand Up @@ -227,3 +267,26 @@ def compare_faces(
)

return float(result.text)


def safetensors_to_base64(file_path: str) -> str:
with open(file_path, "rb") as file:
file_bytes = file.read()
return "data:application/face;base64," + base64.b64encode(file_bytes).decode(
"utf-8"
)


def base64_to_safetensors(base64str: str, output_path: str) -> None:
try:
base64_data = base64str.split("base64,")[-1]
file_bytes = base64.b64decode(base64_data)
with open(output_path, "wb") as file:
file.write(file_bytes)
with safetensors.safe_open(output_path, framework="pt") as f:
print(output_path, "keys =", f.keys())
except Exception as e:
print("Error : failed to convert base64 string to safetensor", e)
import traceback

traceback.print_exc()
70 changes: 64 additions & 6 deletions client_api/faceswaplab_api_example.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
from typing import List
import requests
from api_utils import (
FaceSwapRequest,
FaceSwapUnit,
PostProcessingOptions,
FaceSwapResponse,
InswappperOptions,
base64_to_safetensors,
pil_to_base64,
PostProcessingOptions,
InpaintingWhen,
FaceSwapCompareRequest,
InpaintingOptions,
FaceSwapRequest,
FaceSwapResponse,
FaceSwapExtractRequest,
FaceSwapCompareRequest,
FaceSwapExtractResponse,
safetensors_to_base64,
)

address = "http://127.0.0.1:7860"

# This has been tested on Linux platforms. This might requires some minor adaptations for windows.


#############################
# FaceSwap
Expand All @@ -37,9 +44,11 @@
restorer_visibility=1,
upscaler_name="Lanczos",
scale=4,
inpainting_steps=30,
inpainting_denoising_strengh=0.1,
inpainting_when=InpaintingWhen.BEFORE_RESTORE_FACE,
inpainting_options=InpaintingOptions(
inpainting_steps=30,
inpainting_denoising_strengh=0.1,
),
)

# Prepare the request
Expand Down Expand Up @@ -91,3 +100,52 @@

for img in response.pil_images:
img.show()


#############################
# Build checkpoint

source_images: List[str] = [
pil_to_base64("../references/man.png"),
pil_to_base64("../references/woman.png"),
]

result = requests.post(
url=f"{address}/faceswaplab/build",
json=source_images,
headers={"Content-Type": "application/json; charset=utf-8"},
)

base64_to_safetensors(result.json(), output_path="test.safetensors")

#############################
# FaceSwap with local safetensors

# First face unit :
unit1 = FaceSwapUnit(
source_face=safetensors_to_base64(
"test.safetensors"
), # convert the checkpoint to base64
faces_index=(0,), # Replace first face
swapping_options=InswappperOptions(
face_restorer_name="CodeFormer",
upscaler_name="LDSR",
improved_mask=True,
sharpen=True,
color_corrections=True,
),
)

# Prepare the request
request = FaceSwapRequest(image=pil_to_base64("test_image.png"), units=[unit1])

# Face Swap
result = requests.post(
url=f"{address}/faceswaplab/swap_face",
data=request.json(),
headers={"Content-Type": "application/json; charset=utf-8"},
)
response = FaceSwapResponse.parse_obj(result.json())

for img in response.pil_images:
img.show()
5 changes: 5 additions & 0 deletions client_api/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
numpy==1.25.1
Pillow==10.0.0
pydantic==1.10.9
Requests==2.31.0
safetensors==0.3.1
Binary file added client_api/test.safetensors
Binary file not shown.
Binary file modified docs/assets/images/doc_mi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading