UPDATE: Blended Faces, CU12, Detection Tab
+VersionUP (0.7.0 alpha1) FR #143 #255 (partly) #280 (partly) #352 Issue #319
This commit is contained in:
parent
4a7a367ea1
commit
96d7a06291
11
README.md
11
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/raw/main/sd-webui-reactor/ReActor_logo_NEW_EN.png?raw=true" alt="logo" width="180px"/>
|
||||
|
||||

|
||||

|
||||
|
||||
<a href="https://boosty.to/artgourieff" target="_blank">
|
||||
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Support Me on Boosty"/>
|
||||
@ -40,6 +40,15 @@
|
||||
|
||||
## What's new in the latest updates
|
||||
|
||||
### 0.7.0 <sub><sup>ALPHA1
|
||||
|
||||
- You can now blend faces to build blended face models ("Tools->Face Models->Blend") - due to popular demand
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/0.7.0-whatsnew-01.jpg?raw=true" alt="0.7.0-whatsnew-01" width="100%"/><img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/0.7.0-whatsnew-02.jpg?raw=true" alt="0.7.0-whatsnew-02" width="100%"/>
|
||||
|
||||
- CUDA 12 Support in the Installer script for 1.17.0 ORT-GPU library
|
||||
- New tab "Detection" with "Threshold" and "Max Faces" parameters
|
||||
|
||||
### 0.6.1 <sub><sup>BETA3
|
||||
|
||||
- 'Force Upscale' option inside the 'Upscale' tab: ReActor will run the Upscaler even if there's no face is detected (FR https://github.com/Gourieff/sd-webui-reactor/issues/116)
|
||||
|
||||
11
README_RU.md
11
README_RU.md
@ -2,7 +2,7 @@
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/raw/main/sd-webui-reactor/ReActor_logo_NEW_RU.png?raw=true" alt="logo" width="180px"/>
|
||||
|
||||

|
||||

|
||||
|
||||
<a href="https://boosty.to/artgourieff" target="_blank">
|
||||
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Поддержать проект на Boosty"/>
|
||||
@ -39,6 +39,15 @@
|
||||
|
||||
## Что нового в последних обновлениях
|
||||
|
||||
### 0.7.0 <sub><sup>ALPHA1
|
||||
|
||||
- По многочисленным просьбам появилась возможность строить смешанные модели лиц ("Tools->Face Models->Blend")
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/0.7.0-whatsnew-01.jpg?raw=true" alt="0.7.0-whatsnew-01" width="100%"/><img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/0.7.0-whatsnew-02.jpg?raw=true" alt="0.7.0-whatsnew-02" width="100%"/>
|
||||
|
||||
- Поддержка CUDA 12 в скрипте установщика для библиотеки ORT-GPU версии 1.17.0
|
||||
- Новая вкладка "Detection" с параметрами "Threshold" и "Max Faces"
|
||||
|
||||
### 0.6.1 <sub><sup>BETA3
|
||||
|
||||
- Опция 'Force Upscale' внутри вкладки 'Upscale': апскейл выполнится, даже если не было обнаружено ни одного лица (FR https://github.com/Gourieff/sd-webui-reactor/issues/116)
|
||||
|
||||
@ -50,6 +50,8 @@ args=[
|
||||
None, #25 skip it for API
|
||||
True, #26 Randomly select an image from the path
|
||||
True, #27 Force Upscale even if no face found
|
||||
0.6, #28 Face Detection Threshold
|
||||
2, #29 Maximum number of faces to detect (0 is unlimited)
|
||||
]
|
||||
|
||||
# The args for ReActor can be found by
|
||||
|
||||
Binary file not shown.
11
install.py
11
install.py
@ -97,11 +97,14 @@ with open(req_file) as file:
|
||||
install_count = 0
|
||||
ort = "onnxruntime-gpu"
|
||||
import torch
|
||||
cuda_version = None
|
||||
try:
|
||||
if torch.cuda.is_available():
|
||||
cuda_version = torch.version.cuda
|
||||
print(f"CUDA {cuda_version}")
|
||||
if first_run or last_device is None:
|
||||
last_device = "CUDA"
|
||||
elif torch.backends.mps.is_available() or hasattr(torch,'dml'):
|
||||
elif torch.backends.mps.is_available() or hasattr(torch,'dml') or hasattr(torch,'privateuseone'):
|
||||
ort = "onnxruntime"
|
||||
# to prevent errors when ORT-GPU is installed but we want ORT instead:
|
||||
if first_run:
|
||||
@ -114,7 +117,11 @@ with open(req_file) as file:
|
||||
last_device = "CPU"
|
||||
with open(os.path.join(BASE_PATH, "last_device.txt"), "w") as txt:
|
||||
txt.write(last_device)
|
||||
if not is_installed(ort,"1.16.1",False):
|
||||
if cuda_version is not None and float(cuda_version)>=12: # CU12
|
||||
if not is_installed(ort,"1.17.0",False):
|
||||
install_count += 1
|
||||
pip_install(ort,"--extra-index-url", "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/")
|
||||
elif not is_installed(ort,"1.16.1",False):
|
||||
install_count += 1
|
||||
pip_install(ort, "-U")
|
||||
except Exception as e:
|
||||
|
||||
@ -2,3 +2,4 @@ import reactor_ui.reactor_upscale_ui as ui_upscale
|
||||
import reactor_ui.reactor_tools_ui as ui_tools
|
||||
import reactor_ui.reactor_settings_ui as ui_settings
|
||||
import reactor_ui.reactor_main_ui as ui_main
|
||||
import reactor_ui.reactor_detection_ui as ui_detection
|
||||
|
||||
54
reactor_ui/reactor_detection_ui.py
Normal file
54
reactor_ui/reactor_detection_ui.py
Normal file
@ -0,0 +1,54 @@
|
||||
import gradio as gr
|
||||
from scripts.reactor_swapper import (
|
||||
clear_faces,
|
||||
clear_faces_list,
|
||||
clear_faces_target,
|
||||
clear_faces_all
|
||||
)
|
||||
|
||||
# TAB DETECTION
|
||||
def show(show_br: bool = True):
|
||||
with gr.Tab("Detection"):
|
||||
with gr.Row():
|
||||
det_thresh = gr.Slider(
|
||||
minimum=0.1,
|
||||
maximum=1.0,
|
||||
value=0.5,
|
||||
step=0.01,
|
||||
label="Threshold",
|
||||
info="The higher the value, the more sensitive the detection is to what is considered a face (0.5 by default)",
|
||||
scale=2
|
||||
)
|
||||
det_maxnum = gr.Slider(
|
||||
minimum=0,
|
||||
maximum=20,
|
||||
value=0,
|
||||
step=1,
|
||||
label="Max Faces",
|
||||
info="Maximum number of faces to detect (0 is unlimited)",
|
||||
scale=1
|
||||
)
|
||||
# gr.Markdown("<br>", visible=show_br)
|
||||
gr.Markdown("Hashed images get processed with previously set detection parameters (the face is hashed with all available parameters to bypass the analyzer and speed up the process). Please clear the hash if you want to apply new detection settings.", visible=show_br)
|
||||
with gr.Row():
|
||||
imgs_hash_clear_single = gr.Button(
|
||||
value="Clear Source Images Hash (Single)",
|
||||
scale=1
|
||||
)
|
||||
imgs_hash_clear_multiple = gr.Button(
|
||||
value="Clear Source Images Hash (Multiple)",
|
||||
scale=1
|
||||
)
|
||||
imgs_hash_clear_target = gr.Button(
|
||||
value="Clear Target Image Hash",
|
||||
scale=1
|
||||
)
|
||||
imgs_hash_clear_all = gr.Button(
|
||||
value="Clear All Hash"
|
||||
)
|
||||
progressbar_area = gr.Markdown("")
|
||||
imgs_hash_clear_single.click(clear_faces,None,[progressbar_area])
|
||||
imgs_hash_clear_multiple.click(clear_faces_list,None,[progressbar_area])
|
||||
imgs_hash_clear_target.click(clear_faces_target,None,[progressbar_area])
|
||||
imgs_hash_clear_all.click(clear_faces_all,None,[progressbar_area])
|
||||
return det_thresh, det_maxnum
|
||||
@ -1,25 +1,61 @@
|
||||
import gradio as gr
|
||||
from scripts.reactor_swapper import build_face_model
|
||||
from scripts.reactor_swapper import build_face_model, blend_faces
|
||||
|
||||
# TAB TOOLS
|
||||
def show():
|
||||
with gr.Tab("Tools"):
|
||||
with gr.Tab("Face Models"):
|
||||
gr.Markdown("Load an image containing one person, name it and click 'Build and Save'")
|
||||
img_fm = gr.Image(
|
||||
type="pil",
|
||||
label="Load Image to build Face Model",
|
||||
)
|
||||
with gr.Row(equal_height=True):
|
||||
fm_name = gr.Textbox(
|
||||
value="",
|
||||
placeholder="Please type any name (e.g. Elena)",
|
||||
label="Face Model Name",
|
||||
|
||||
with gr.Tab("Single"):
|
||||
gr.Markdown("Load an image containing one person, name it and click 'Build and Save'")
|
||||
img_fm = gr.Image(
|
||||
type="pil",
|
||||
label="Load an Image to build -Face Model-",
|
||||
)
|
||||
with gr.Row(equal_height=True):
|
||||
fm_name = gr.Textbox(
|
||||
value="",
|
||||
placeholder="Please type any name (e.g. Elena)",
|
||||
label="Face Model Name",
|
||||
)
|
||||
save_fm_btn = gr.Button("Build and Save")
|
||||
save_fm = gr.Markdown("You can find saved models in 'models/reactor/faces'")
|
||||
save_fm_btn.click(
|
||||
build_face_model,
|
||||
inputs=[img_fm, fm_name],
|
||||
outputs=[save_fm],
|
||||
)
|
||||
|
||||
with gr.Tab("Blend"):
|
||||
gr.Markdown("Load a set of images containing any person, name it and click 'Build and Save'")
|
||||
with gr.Row():
|
||||
imgs_fm = gr.Files(
|
||||
label=f"Load Images to build -Blended Face Model-",
|
||||
file_types=["image"]
|
||||
)
|
||||
with gr.Column():
|
||||
compute_method = gr.Radio(
|
||||
["Mean", "Median", "Mode"],
|
||||
value="Mean",
|
||||
label="Compute Method",
|
||||
type="index",
|
||||
info="Mean (recommended) - Average value (best result 👍); Median* - Mid-point value (may be funny 😅); Mode - Most common value (may be scary 😨); *Mean and Median will be simillar if you load two images"
|
||||
)
|
||||
shape_check = gr.Checkbox(
|
||||
False,
|
||||
label="Check -Embedding Shape- on Similarity",
|
||||
info="(Experimental) Turn it ON if you want to skip the faces which are too much different from the first one in the list to prevent some probable 'shape mismatches'"
|
||||
)
|
||||
with gr.Row(equal_height=True):
|
||||
fm_name = gr.Textbox(
|
||||
value="",
|
||||
placeholder="Please type any name (e.g. Elena)",
|
||||
label="Face Model Name",
|
||||
)
|
||||
save_fm_btn = gr.Button("Build and Save")
|
||||
save_fm = gr.Markdown("You can find saved models in 'models/reactor/faces'")
|
||||
save_fm_btn.click(
|
||||
blend_faces,
|
||||
inputs=[imgs_fm, fm_name, compute_method, shape_check],
|
||||
outputs=[save_fm],
|
||||
)
|
||||
save_fm_btn = gr.Button("Build and Save")
|
||||
save_fm = gr.Markdown("You can find saved models in 'models/reactor/faces'")
|
||||
save_fm_btn.click(
|
||||
build_face_model,
|
||||
inputs=[img_fm, fm_name],
|
||||
outputs=[save_fm],
|
||||
)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'''
|
||||
Thanks SpenserCai for the original version of the roop api script
|
||||
-----------------------------------
|
||||
--- ReActor External API v1.0.4 ---
|
||||
--- ReActor External API v1.0.5 ---
|
||||
-----------------------------------
|
||||
'''
|
||||
import os, glob
|
||||
@ -14,7 +14,7 @@ from modules.api import api
|
||||
|
||||
import gradio as gr
|
||||
|
||||
from scripts.reactor_swapper import EnhancementOptions, swap_face
|
||||
from scripts.reactor_swapper import EnhancementOptions, swap_face, DetectionOptions
|
||||
from scripts.reactor_logger import logger
|
||||
|
||||
|
||||
@ -77,7 +77,9 @@ def reactor_api(_: gr.Blocks, app: FastAPI):
|
||||
face_model: str = Body("None",title="Filename of the face model (from 'models/reactor/faces'), e.g. elena.safetensors"),
|
||||
source_folder: str = Body("",title="The path to the folder containing source faces images"),
|
||||
random_image: int = Body(0,title="Randomly select an image from the path"),
|
||||
upscale_force: int = Body(0,title="Force Upscale even if no face found")
|
||||
upscale_force: int = Body(0,title="Force Upscale even if no face found"),
|
||||
det_thresh: float = Body(0.5,title="Face Detection Threshold"),
|
||||
det_maxnum: int = Body(0,title="Maximum number of faces to detect (0 is unlimited)"),
|
||||
):
|
||||
s_image = api.decode_base64_to_image(source_image) if select_source == 0 else None
|
||||
t_image = api.decode_base64_to_image(target_image)
|
||||
@ -90,10 +92,11 @@ def reactor_api(_: gr.Blocks, app: FastAPI):
|
||||
random_image = False if random_image == 0 else True
|
||||
upscale_force = False if upscale_force == 0 else True
|
||||
up_options = EnhancementOptions(do_restore_first=restore_first_bool, scale=scale, upscaler=get_upscaler(upscaler), upscale_visibility=upscale_visibility,face_restorer=get_face_restorer(face_restorer),restorer_visibility=restorer_visibility,codeformer_weight=codeformer_weight,upscale_force=upscale_force)
|
||||
det_options = DetectionOptions(det_thresh=det_thresh, det_maxnum=det_maxnum)
|
||||
use_model = get_full_model(model)
|
||||
if use_model is None:
|
||||
Exception("Model not found")
|
||||
result = swap_face(s_image, t_image, use_model, sf_index, f_index, up_options, gender_s, gender_t, True, True, device, mask_face, select_source, face_model, source_folder, None, random_image)
|
||||
result = swap_face(s_image, t_image, use_model, sf_index, f_index, up_options, gender_s, gender_t, True, True, device, mask_face, select_source, face_model, source_folder, None, random_image,det_options)
|
||||
if save_to_file == 1:
|
||||
if result_file_path == "":
|
||||
result_file_path = default_file_path()
|
||||
|
||||
@ -15,20 +15,27 @@ from modules.processing import (
|
||||
from modules.face_restoration import FaceRestoration
|
||||
from modules.images import save_image
|
||||
|
||||
from reactor_ui import ui_main, ui_upscale, ui_tools, ui_settings
|
||||
from reactor_ui import (
|
||||
ui_main,
|
||||
ui_upscale,
|
||||
ui_tools,
|
||||
ui_settings,
|
||||
ui_detection,
|
||||
)
|
||||
from scripts.reactor_logger import logger
|
||||
from scripts.reactor_swapper import (
|
||||
EnhancementOptions,
|
||||
swap_face,
|
||||
check_process_halt,
|
||||
EnhancementOptions,
|
||||
DetectionOptions,
|
||||
swap_face,
|
||||
check_process_halt,
|
||||
reset_messaged,
|
||||
)
|
||||
from scripts.reactor_version import version_flag, app_title
|
||||
from scripts.console_log_patch import apply_logging_patch
|
||||
from scripts.reactor_helpers import (
|
||||
make_grid,
|
||||
set_Device,
|
||||
get_SDNEXT
|
||||
make_grid,
|
||||
set_Device,
|
||||
get_SDNEXT,
|
||||
)
|
||||
from scripts.reactor_globals import SWAPPER_MODELS_PATH #, DEVICE, DEVICE_LIST
|
||||
|
||||
@ -65,6 +72,9 @@ class FaceSwapScript(scripts.Script):
|
||||
}
|
||||
img, imgs, select_source, face_model, source_folder, save_original, mask_face, source_faces_index, gender_source, faces_index, gender_target, face_restorer_name, face_restorer_visibility, codeformer_weight, swap_in_source, swap_in_generated, random_image = ui_main.show(is_img2img=is_img2img, **msgs)
|
||||
|
||||
# TAB DETECTION
|
||||
det_thresh, det_maxnum = ui_detection.show()
|
||||
|
||||
# TAB UPSCALE
|
||||
restore_first, upscaler_name, upscaler_scale, upscaler_visibility, upscale_force = ui_upscale.show()
|
||||
|
||||
@ -104,7 +114,9 @@ class FaceSwapScript(scripts.Script):
|
||||
source_folder,
|
||||
imgs,
|
||||
random_image,
|
||||
upscale_force
|
||||
upscale_force,
|
||||
det_thresh,
|
||||
det_maxnum
|
||||
]
|
||||
|
||||
|
||||
@ -134,6 +146,13 @@ class FaceSwapScript(scripts.Script):
|
||||
codeformer_weight=self.codeformer_weight,
|
||||
upscale_force=self.upscale_force
|
||||
)
|
||||
|
||||
@property
|
||||
def detection_options(self) -> DetectionOptions:
|
||||
return DetectionOptions(
|
||||
det_thresh=self.det_thresh,
|
||||
det_maxnum=self.det_maxnum
|
||||
)
|
||||
|
||||
def process(
|
||||
self,
|
||||
@ -166,6 +185,8 @@ class FaceSwapScript(scripts.Script):
|
||||
imgs,
|
||||
random_image,
|
||||
upscale_force,
|
||||
det_thresh,
|
||||
det_maxnum
|
||||
):
|
||||
self.enable = enable
|
||||
if self.enable:
|
||||
@ -202,6 +223,8 @@ class FaceSwapScript(scripts.Script):
|
||||
self.source_imgs = imgs
|
||||
self.random_image = random_image
|
||||
self.upscale_force = upscale_force
|
||||
self.det_thresh=det_thresh
|
||||
self.det_maxnum=det_maxnum
|
||||
if self.gender_source is None or self.gender_source == "No":
|
||||
self.gender_source = 0
|
||||
if self.gender_target is None or self.gender_target == "No":
|
||||
@ -266,6 +289,7 @@ class FaceSwapScript(scripts.Script):
|
||||
source_folder = None,
|
||||
source_imgs = None,
|
||||
random_image = False,
|
||||
detection_options=self.detection_options,
|
||||
)
|
||||
p.init_images[i] = result
|
||||
# result_path = get_image_path(p.init_images[i], p.outpath_samples, "", p.all_seeds[i], p.all_prompts[i], "txt", p=p, suffix="-swapped")
|
||||
@ -333,6 +357,7 @@ class FaceSwapScript(scripts.Script):
|
||||
source_folder = self.source_folder,
|
||||
source_imgs = self.source_imgs,
|
||||
random_image = self.random_image,
|
||||
detection_options=self.detection_options,
|
||||
)
|
||||
|
||||
if self.select_source == 2 or (self.select_source == 0 and self.source_imgs is not None and self.source is None):
|
||||
@ -413,7 +438,7 @@ class FaceSwapScript(scripts.Script):
|
||||
return
|
||||
|
||||
# if (self.source is not None and self.select_source == 0) or ((self.face_model is not None and self.face_model != "None") and self.select_source == 1):
|
||||
logger.status("! Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index)
|
||||
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index)
|
||||
image: Image.Image = script_pp.image
|
||||
result, output, swapped = swap_face(
|
||||
self.source,
|
||||
@ -433,6 +458,7 @@ class FaceSwapScript(scripts.Script):
|
||||
source_folder = None,
|
||||
source_imgs = None,
|
||||
random_image = False,
|
||||
detection_options=self.detection_options,
|
||||
)
|
||||
self.result = result
|
||||
try:
|
||||
@ -471,6 +497,9 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
||||
}
|
||||
img, imgs, select_source, face_model, source_folder, save_original, mask_face, source_faces_index, gender_source, faces_index, gender_target, face_restorer_name, face_restorer_visibility, codeformer_weight, swap_in_source, swap_in_generated, random_image = ui_main.show(is_img2img=False, show_br=False, **msgs)
|
||||
|
||||
# TAB DETECTION
|
||||
det_thresh, det_maxnum = ui_detection.show()
|
||||
|
||||
# TAB UPSCALE
|
||||
restore_first, upscaler_name, upscaler_scale, upscaler_visibility, upscale_force = ui_upscale.show(show_br=False)
|
||||
|
||||
@ -506,6 +535,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
||||
'imgs': imgs,
|
||||
'random_image': random_image,
|
||||
'upscale_force': upscale_force,
|
||||
'det_thresh': det_thresh,
|
||||
'det_maxnum': det_maxnum,
|
||||
}
|
||||
return args
|
||||
|
||||
@ -535,6 +566,13 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
||||
codeformer_weight=self.codeformer_weight,
|
||||
upscale_force=self.upscale_force,
|
||||
)
|
||||
|
||||
@property
|
||||
def detection_options(self) -> DetectionOptions:
|
||||
return DetectionOptions(
|
||||
det_thresh=self.det_thresh,
|
||||
det_maxnum=self.det_maxnum
|
||||
)
|
||||
|
||||
def process(self, pp: scripts_postprocessing.PostprocessedImage, **args):
|
||||
if args['enable']:
|
||||
@ -563,6 +601,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
||||
self.source_imgs = args['imgs']
|
||||
self.random_image = args['random_image']
|
||||
self.upscale_force = args['upscale_force']
|
||||
self.det_thresh = args['det_thresh']
|
||||
self.det_maxnum = args['det_maxnum']
|
||||
if self.gender_source is None or self.gender_source == "No":
|
||||
self.gender_source = 0
|
||||
if self.gender_target is None or self.gender_target == "No":
|
||||
@ -622,6 +662,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
||||
source_folder=self.source_folder,
|
||||
source_imgs=self.source_imgs,
|
||||
random_image=self.random_image,
|
||||
detection_options=self.detection_options,
|
||||
)
|
||||
if self.select_source == 2 or (self.select_source == 0 and self.source_imgs is not None and self.source is None):
|
||||
if len(result) > 0 and swapped > 0:
|
||||
|
||||
@ -6,6 +6,7 @@ from typing import List, Union
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from scipy import stats
|
||||
|
||||
import insightface
|
||||
from insightface.app.common import Face
|
||||
@ -66,6 +67,10 @@ class EnhancementOptions:
|
||||
codeformer_weight: float = 0.5
|
||||
upscale_force: bool = False
|
||||
|
||||
@dataclass
|
||||
class DetectionOptions:
|
||||
det_thresh: float = 0.5
|
||||
det_maxnum: int = 0
|
||||
|
||||
MESSAGED_STOPPED = False
|
||||
MESSAGED_SKIPPED = False
|
||||
@ -106,12 +111,33 @@ TARGET_IMAGE_HASH = None
|
||||
SOURCE_FACES_LIST = []
|
||||
SOURCE_IMAGE_LIST_HASH = []
|
||||
|
||||
def clear_faces():
|
||||
global SOURCE_FACES, SOURCE_IMAGE_HASH
|
||||
SOURCE_FACES = None
|
||||
SOURCE_IMAGE_HASH = None
|
||||
logger.status("Source Images Hash has been reset (for Single Source or Face Model)")
|
||||
|
||||
def clear_faces_list():
|
||||
global SOURCE_FACES_LIST, SOURCE_IMAGE_LIST_HASH
|
||||
SOURCE_FACES_LIST = []
|
||||
SOURCE_IMAGE_LIST_HASH = []
|
||||
logger.status("Source Images Hash has been reset (for Multiple or Folder Source)")
|
||||
|
||||
def clear_faces_target():
|
||||
global TARGET_FACES, TARGET_IMAGE_HASH
|
||||
TARGET_FACES = None
|
||||
TARGET_IMAGE_HASH = None
|
||||
logger.status("Target Images Hash has been reset")
|
||||
|
||||
def clear_faces_all():
|
||||
global SOURCE_FACES, SOURCE_IMAGE_HASH, SOURCE_FACES_LIST, SOURCE_IMAGE_LIST_HASH, TARGET_FACES, TARGET_IMAGE_HASH
|
||||
SOURCE_FACES = None
|
||||
SOURCE_IMAGE_HASH = None
|
||||
TARGET_FACES = None
|
||||
TARGET_IMAGE_HASH = None
|
||||
SOURCE_FACES_LIST = []
|
||||
SOURCE_IMAGE_LIST_HASH = []
|
||||
logger.status("All Images Hash has been reset")
|
||||
|
||||
def getAnalysisModel():
|
||||
global ANALYSIS_MODEL
|
||||
@ -270,13 +296,13 @@ def half_det_size(det_size):
|
||||
logger.status("Trying to halve 'det_size' parameter")
|
||||
return (det_size[0] // 2, det_size[1] // 2)
|
||||
|
||||
def analyze_faces(img_data: np.ndarray, det_size=(640, 640)):
|
||||
def analyze_faces(img_data: np.ndarray, det_size=(640, 640), det_thresh=0.5, det_maxnum=0):
|
||||
logger.info("Applied Execution Provider: %s", PROVIDERS[0])
|
||||
face_analyser = copy.deepcopy(getAnalysisModel())
|
||||
face_analyser.prepare(ctx_id=0, det_size=det_size)
|
||||
return face_analyser.get(img_data)
|
||||
face_analyser.prepare(ctx_id=0, det_thresh=det_thresh, det_size=det_size)
|
||||
return face_analyser.get(img_data, max_num=det_maxnum)
|
||||
|
||||
def get_face_single(img_data: np.ndarray, face, face_index=0, det_size=(640, 640), gender_source=0, gender_target=0):
|
||||
def get_face_single(img_data: np.ndarray, face, face_index=0, det_size=(640, 640), gender_source=0, gender_target=0, det_thresh=0.5, det_maxnum=0):
|
||||
|
||||
buffalo_path = os.path.join(models_path, "insightface/models/buffalo_l.zip")
|
||||
if os.path.exists(buffalo_path):
|
||||
@ -299,20 +325,20 @@ def get_face_single(img_data: np.ndarray, face, face_index=0, det_size=(640, 640
|
||||
if gender_source != 0:
|
||||
if len(face) == 0 and det_size[0] > 320 and det_size[1] > 320:
|
||||
det_size_half = half_det_size(det_size)
|
||||
return get_face_single(img_data, analyze_faces(img_data, det_size_half), face_index, det_size_half, gender_source, gender_target)
|
||||
return get_face_single(img_data, analyze_faces(img_data, det_size_half, det_thresh, det_maxnum), face_index, det_size_half, gender_source, gender_target, det_thresh, det_maxnum)
|
||||
faces, wrong_gender = get_face_gender(face,face_index,gender_source,"Source",gender_detected)
|
||||
return faces, wrong_gender, face_age, face_gender
|
||||
|
||||
if gender_target != 0:
|
||||
if len(face) == 0 and det_size[0] > 320 and det_size[1] > 320:
|
||||
det_size_half = half_det_size(det_size)
|
||||
return get_face_single(img_data, analyze_faces(img_data, det_size_half), face_index, det_size_half, gender_source, gender_target)
|
||||
return get_face_single(img_data, analyze_faces(img_data, det_size_half, det_thresh, det_maxnum), face_index, det_size_half, gender_source, gender_target, det_thresh, det_maxnum)
|
||||
faces, wrong_gender = get_face_gender(face,face_index,gender_target,"Target",gender_detected)
|
||||
return faces, wrong_gender, face_age, face_gender
|
||||
|
||||
if len(face) == 0 and det_size[0] > 320 and det_size[1] > 320:
|
||||
det_size_half = half_det_size(det_size)
|
||||
return get_face_single(img_data, analyze_faces(img_data, det_size_half), face_index, det_size_half, gender_source, gender_target)
|
||||
return get_face_single(img_data, analyze_faces(img_data, det_size_half, det_thresh, det_maxnum), face_index, det_size_half, gender_source, gender_target, det_thresh, det_maxnum)
|
||||
|
||||
try:
|
||||
return sorted(face, key=lambda x: x.bbox[0])[face_index], 0, face_age, face_gender
|
||||
@ -338,6 +364,7 @@ def swap_face(
|
||||
source_folder: str = "",
|
||||
source_imgs: Union[List, None] = None,
|
||||
random_image: bool = False,
|
||||
detection_options: Union[DetectionOptions, None] = None,
|
||||
):
|
||||
global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH, PROVIDERS, SOURCE_FACES_LIST, SOURCE_IMAGE_LIST_HASH
|
||||
|
||||
@ -413,15 +440,15 @@ def swap_face(
|
||||
|
||||
if len(SOURCE_FACES_LIST) == 0:
|
||||
logger.status(f"Analyzing Source Image {i}: {source_images_names[i]}...")
|
||||
source_faces = analyze_faces(source_image)
|
||||
source_faces = analyze_faces(source_image, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
SOURCE_FACES_LIST = [source_faces]
|
||||
elif len(SOURCE_FACES_LIST) == i and not source_image_same:
|
||||
logger.status(f"Analyzing Source Image {i}: {source_images_names[i]}...")
|
||||
source_faces = analyze_faces(source_image)
|
||||
source_faces = analyze_faces(source_image, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
SOURCE_FACES_LIST.append(source_faces)
|
||||
elif len(SOURCE_FACES_LIST) != i and not source_image_same:
|
||||
logger.status(f"Analyzing Source Image {i}: {source_images_names[i]}...")
|
||||
source_faces = analyze_faces(source_image)
|
||||
source_faces = analyze_faces(source_image, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
SOURCE_FACES_LIST[i] = source_faces
|
||||
elif source_image_same:
|
||||
logger.status("(Image %s) Using Hashed Source Face(s) Model...", i)
|
||||
@ -429,7 +456,7 @@ def swap_face(
|
||||
|
||||
else:
|
||||
logger.status(f"Analyzing Source Image {i}...")
|
||||
source_faces = analyze_faces(source_image)
|
||||
source_faces = analyze_faces(source_image, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
|
||||
if source_faces is not None:
|
||||
source_faces_ff.append(source_faces)
|
||||
@ -453,7 +480,7 @@ def swap_face(
|
||||
|
||||
if TARGET_FACES is None or not target_image_same:
|
||||
logger.status("Analyzing Target Image...")
|
||||
target_faces = analyze_faces(target_img)
|
||||
target_faces = analyze_faces(target_img, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
TARGET_FACES = target_faces
|
||||
elif target_image_same:
|
||||
logger.status("Using Hashed Target Face(s) Model...")
|
||||
@ -461,12 +488,12 @@ def swap_face(
|
||||
|
||||
else:
|
||||
logger.status("Analyzing Target Image...")
|
||||
target_faces = analyze_faces(target_img)
|
||||
target_faces = analyze_faces(target_img, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
|
||||
for i,source_faces in enumerate(source_faces_ff):
|
||||
|
||||
logger.status("(Image %s) Detecting Source Face, Index = %s", i, source_faces_index[0])
|
||||
source_face, wrong_gender, source_age, source_gender = get_face_single(source_img_ff[i], source_faces, face_index=source_faces_index[0], gender_source=gender_source)
|
||||
source_face, wrong_gender, source_age, source_gender = get_face_single(source_img_ff[i], source_faces, face_index=source_faces_index[0], gender_source=gender_source, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
|
||||
if source_age != "None" or source_gender != "None":
|
||||
logger.status("(Image %s) Detected: -%s- y.o. %s", i, source_age, source_gender)
|
||||
@ -476,7 +503,7 @@ def swap_face(
|
||||
|
||||
elif source_face is not None:
|
||||
|
||||
result_image, output, swapped = operate(source_img_ff[i],target_img,target_img_orig,model,source_faces_index,faces_index,source_faces,target_faces,gender_source,gender_target,source_face,wrong_gender,source_age,source_gender,output,swapped,mask_face,entire_mask_image,enhancement_options)
|
||||
result_image, output, swapped = operate(source_img_ff[i],target_img,target_img_orig,model,source_faces_index,faces_index,source_faces,target_faces,gender_source,gender_target,source_face,wrong_gender,source_age,source_gender,output,swapped,mask_face,entire_mask_image,enhancement_options,detection_options)
|
||||
|
||||
result.append(result_image)
|
||||
|
||||
@ -513,7 +540,7 @@ def swap_face(
|
||||
|
||||
if SOURCE_FACES is None or not source_image_same:
|
||||
logger.status("Analyzing Source Image...")
|
||||
source_faces = analyze_faces(source_img)
|
||||
source_faces = analyze_faces(source_img, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
SOURCE_FACES = source_faces
|
||||
elif source_image_same:
|
||||
logger.status("Using Hashed Source Face(s) Model...")
|
||||
@ -521,7 +548,7 @@ def swap_face(
|
||||
|
||||
else:
|
||||
logger.status("Analyzing Source Image...")
|
||||
source_faces = analyze_faces(source_img)
|
||||
source_faces = analyze_faces(source_img, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
|
||||
elif select_source == 1 and (face_model is not None and face_model != "None"):
|
||||
source_face_model = [load_face_model(face_model)]
|
||||
@ -555,7 +582,7 @@ def swap_face(
|
||||
|
||||
if TARGET_FACES is None or not target_image_same:
|
||||
logger.status("Analyzing Target Image...")
|
||||
target_faces = analyze_faces(target_img)
|
||||
target_faces = analyze_faces(target_img, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
TARGET_FACES = target_faces
|
||||
elif target_image_same:
|
||||
logger.status("Using Hashed Target Face(s) Model...")
|
||||
@ -563,11 +590,11 @@ def swap_face(
|
||||
|
||||
else:
|
||||
logger.status("Analyzing Target Image...")
|
||||
target_faces = analyze_faces(target_img)
|
||||
target_faces = analyze_faces(target_img, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
|
||||
logger.status("Detecting Source Face, Index = %s", source_faces_index[0])
|
||||
if select_source == 0 and source_img is not None:
|
||||
source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[0], gender_source=gender_source)
|
||||
source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[0], gender_source=gender_source, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
else:
|
||||
source_face = sorted(source_faces, key=lambda x: x.bbox[0])[source_faces_index[0]]
|
||||
wrong_gender = 0
|
||||
@ -585,7 +612,7 @@ def swap_face(
|
||||
|
||||
elif source_face is not None:
|
||||
|
||||
result_image, output, swapped = operate(source_img,target_img,target_img_orig,model,source_faces_index,faces_index,source_faces,target_faces,gender_source,gender_target,source_face,wrong_gender,source_age,source_gender,output,swapped,mask_face,entire_mask_image,enhancement_options)
|
||||
result_image, output, swapped = operate(source_img,target_img,target_img_orig,model,source_faces_index,faces_index,source_faces,target_faces,gender_source,gender_target,source_face,wrong_gender,source_age,source_gender,output,swapped,mask_face,entire_mask_image,enhancement_options,detection_options)
|
||||
|
||||
else:
|
||||
logger.status("No source face(s) in the provided Index")
|
||||
@ -599,7 +626,7 @@ def swap_face(
|
||||
|
||||
return result_image, [], 0
|
||||
|
||||
def build_face_model(image: Image.Image, name: str):
|
||||
def build_face_model(image: Image.Image, name: str, save_model: bool = True):
|
||||
if image is None:
|
||||
error_msg = "Please load an Image"
|
||||
logger.error(error_msg)
|
||||
@ -610,20 +637,77 @@ def build_face_model(image: Image.Image, name: str):
|
||||
return error_msg
|
||||
apply_logging_patch(1)
|
||||
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
||||
logger.status("Building Face Model...")
|
||||
if save_model:
|
||||
logger.status("Building Face Model...")
|
||||
face_model = analyze_faces(image)
|
||||
if face_model is not None and len(face_model) > 0:
|
||||
face_model_path = os.path.join(FACE_MODELS_PATH, name + ".safetensors")
|
||||
save_face_model(face_model[0],face_model_path)
|
||||
logger.status("--Done!--")
|
||||
done_msg = f"Face model has been saved to '{face_model_path}'"
|
||||
logger.status(done_msg)
|
||||
return done_msg
|
||||
if save_model:
|
||||
face_model_path = os.path.join(FACE_MODELS_PATH, name + ".safetensors")
|
||||
save_face_model(face_model[0],face_model_path)
|
||||
logger.status("--Done!--")
|
||||
done_msg = f"Face model has been saved to '{face_model_path}'"
|
||||
logger.status(done_msg)
|
||||
return done_msg
|
||||
else:
|
||||
return face_model[0]
|
||||
else:
|
||||
no_face_msg = "No face found, please try another image"
|
||||
logger.error(no_face_msg)
|
||||
return no_face_msg
|
||||
|
||||
def blend_faces(images_list: List, name: str, compute_method: int = 0, shape_check: bool = False):
|
||||
faces = []
|
||||
embeddings = []
|
||||
images: List[Image.Image] = []
|
||||
images, images_names = get_images_from_list(images_list)
|
||||
for i,image in enumerate(images):
|
||||
logger.status(f"Building Face Model for {images_names[i]}...")
|
||||
face = build_face_model(image,str(i),save_model=False)
|
||||
if isinstance(face, str):
|
||||
# logger.error(f"No faces found in {images_names[i]}, skipping")
|
||||
continue
|
||||
if shape_check:
|
||||
if i == 0:
|
||||
embedding_shape = face.embedding.shape
|
||||
elif face.embedding.shape != embedding_shape:
|
||||
logger.error(f"Embedding Shape Mismatch for {images_names[i]}, skipping")
|
||||
continue
|
||||
faces.append(face)
|
||||
embeddings.append(face.embedding)
|
||||
if len(faces) > 0:
|
||||
# if shape_check:
|
||||
# embedding_shape = embeddings[0].shape
|
||||
# for embedding in embeddings:
|
||||
# if embedding.shape != embedding_shape:
|
||||
# logger.error("Embedding Shape Mismatch")
|
||||
# break
|
||||
compute_method_name = "Mean" if compute_method == 0 else "Median" if compute_method == 1 else "Mode"
|
||||
logger.status(f"Blending with Compute Method {compute_method_name}...")
|
||||
blended_embedding = np.mean(embeddings, axis=0) if compute_method == 0 else np.median(embeddings, axis=0) if compute_method == 1 else stats.mode(embeddings, axis=0)[0].astype(np.float32)
|
||||
blended_face = Face(
|
||||
bbox=faces[0].bbox,
|
||||
kps=faces[0].kps,
|
||||
det_score=faces[0].det_score,
|
||||
landmark_3d_68=faces[0].landmark_3d_68,
|
||||
pose=faces[0].pose,
|
||||
landmark_2d_106=faces[0].landmark_2d_106,
|
||||
embedding=blended_embedding,
|
||||
gender=faces[0].gender,
|
||||
age=faces[0].age
|
||||
)
|
||||
if blended_face is not None:
|
||||
face_model_path = os.path.join(FACE_MODELS_PATH, name + ".safetensors")
|
||||
save_face_model(blended_face,face_model_path)
|
||||
logger.status("--Done!--")
|
||||
done_msg = f"Face model has been saved to '{face_model_path}'"
|
||||
logger.status(done_msg)
|
||||
return done_msg
|
||||
else:
|
||||
no_face_msg = "Something went wrong, please try another set of images"
|
||||
logger.error(no_face_msg)
|
||||
return no_face_msg
|
||||
return "No faces found"
|
||||
|
||||
|
||||
def operate(
|
||||
source_img,
|
||||
@ -645,6 +729,7 @@ def operate(
|
||||
mask_face,
|
||||
entire_mask_image,
|
||||
enhancement_options,
|
||||
detection_options,
|
||||
):
|
||||
result = target_img
|
||||
face_swapper = getFaceSwapModel(model)
|
||||
@ -656,7 +741,7 @@ def operate(
|
||||
return result_image, [], 0
|
||||
if len(source_faces_index) > 1 and source_face_idx > 0:
|
||||
logger.status("Detecting Source Face, Index = %s", source_faces_index[source_face_idx])
|
||||
source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[source_face_idx], gender_source=gender_source)
|
||||
source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[source_face_idx], gender_source=gender_source, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
if source_age != "None" or source_gender != "None":
|
||||
logger.status("Detected: -%s- y.o. %s", source_age, source_gender)
|
||||
|
||||
@ -667,7 +752,7 @@ def operate(
|
||||
|
||||
if source_face is not None and wrong_gender == 0:
|
||||
logger.status("Detecting Target Face, Index = %s", face_num)
|
||||
target_face, wrong_gender, target_age, target_gender = get_face_single(target_img, target_faces, face_index=face_num, gender_target=gender_target)
|
||||
target_face, wrong_gender, target_age, target_gender = get_face_single(target_img, target_faces, face_index=face_num, gender_target=gender_target, det_thresh=detection_options.det_thresh, det_maxnum=detection_options.det_maxnum)
|
||||
if target_age != "None" or target_gender != "None":
|
||||
logger.status("Detected: -%s- y.o. %s", target_age, target_gender)
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
app_title = "ReActor"
|
||||
version_flag = "v0.6.1"
|
||||
version_flag = "v0.7.0-a1"
|
||||
|
||||
from scripts.reactor_logger import logger, get_Run, set_Run
|
||||
from scripts.reactor_globals import DEVICE
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user