|
import os, glob |
|
import gradio as gr |
|
from PIL import Image |
|
try: |
|
import torch.cuda as cuda |
|
EP_is_visible = True if cuda.is_available() else False |
|
except: |
|
EP_is_visible = False |
|
|
|
from typing import List |
|
|
|
import modules.scripts as scripts |
|
from modules.upscaler import Upscaler, UpscalerData |
|
from modules import scripts, shared, images, scripts_postprocessing |
|
from modules.processing import ( |
|
Processed, |
|
StableDiffusionProcessing, |
|
StableDiffusionProcessingImg2Img, |
|
) |
|
from modules.face_restoration import FaceRestoration |
|
from modules.images import save_image |
|
try: |
|
from modules.paths_internal import models_path |
|
except: |
|
try: |
|
from modules.paths import models_path |
|
except: |
|
model_path = os.path.abspath("models") |
|
|
|
from scripts.reactor_logger import logger |
|
from scripts.reactor_swapper import ( |
|
EnhancementOptions, |
|
swap_face, |
|
check_process_halt, |
|
reset_messaged, |
|
build_face_model |
|
) |
|
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, get_image_path, set_Device, get_model_names, get_facemodels |
|
from scripts.reactor_globals import DEVICE, DEVICE_LIST |
|
|
|
|
|
MODELS_PATH = None |
|
|
|
def get_models(): |
|
global MODELS_PATH |
|
models_path_init = os.path.join(models_path, "insightface/*") |
|
models = glob.glob(models_path_init) |
|
models = [x for x in models if x.endswith(".onnx") or x.endswith(".pth")] |
|
models_names = [] |
|
for model in models: |
|
model_path = os.path.split(model) |
|
if MODELS_PATH is None: |
|
MODELS_PATH = model_path[0] |
|
model_name = model_path[1] |
|
models_names.append(model_name) |
|
return models_names |
|
|
|
|
|
class FaceSwapScript(scripts.Script): |
|
def title(self): |
|
return f"{app_title}" |
|
|
|
def show(self, is_img2img): |
|
return scripts.AlwaysVisible |
|
|
|
def ui(self, is_img2img): |
|
with gr.Accordion(f"{app_title}", open=False): |
|
|
|
def update_fm_list(selected: str): |
|
return gr.Dropdown.update( |
|
value=selected, choices=get_model_names(get_facemodels) |
|
) |
|
def update_upscalers_list(selected: str): |
|
return gr.Dropdown.update( |
|
value=selected, choices=[upscaler.name for upscaler in shared.sd_upscalers] |
|
) |
|
def update_models_list(selected: str): |
|
return gr.Dropdown.update( |
|
value=selected, choices=get_models() |
|
) |
|
|
|
|
|
with gr.Tab("Main"): |
|
with gr.Column(): |
|
img = gr.Image( |
|
type="pil", |
|
label="Source Image", |
|
) |
|
|
|
|
|
|
|
|
|
|
|
enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}") |
|
gr.Markdown("<br>") |
|
with gr.Row(): |
|
select_source = gr.Radio( |
|
["Image","Face Model"], |
|
value="Image", |
|
label="Select Source", |
|
type="index", |
|
scale=1, |
|
) |
|
face_models = get_model_names(get_facemodels) |
|
face_model = gr.Dropdown( |
|
choices=face_models, |
|
label="Choose Face Model", |
|
value="None", |
|
scale=2, |
|
) |
|
fm_update = gr.Button( |
|
value="🔄", |
|
variant="tool", |
|
) |
|
fm_update.click( |
|
update_fm_list, |
|
inputs=[face_model], |
|
outputs=[face_model], |
|
) |
|
setattr(face_model, "do_not_save_to_config", True) |
|
save_original = gr.Checkbox( |
|
False, |
|
label="Save Original", |
|
info="Save the original image(s) made before swapping; If you use \"img2img\" - this option will affect with \"Swap in generated\" only" |
|
) |
|
mask_face = gr.Checkbox( |
|
False, |
|
label="Face Mask Correction", |
|
info="Apply this option if you see some pixelation around face contours" |
|
) |
|
gr.Markdown("<br>") |
|
gr.Markdown("Source Image (above):") |
|
with gr.Row(): |
|
source_faces_index = gr.Textbox( |
|
value="0", |
|
placeholder="Which face(s) to use as Source (comma separated)", |
|
label="Comma separated face number(s); Example: 0,2,1", |
|
) |
|
gender_source = gr.Radio( |
|
["No", "Female Only", "Male Only"], |
|
value="No", |
|
label="Gender Detection (Source)", |
|
type="index", |
|
) |
|
gr.Markdown("<br>") |
|
gr.Markdown("Target Image (result):") |
|
with gr.Row(): |
|
faces_index = gr.Textbox( |
|
value="0", |
|
placeholder="Which face(s) to Swap into Target (comma separated)", |
|
label="Comma separated face number(s); Example: 1,0,2", |
|
) |
|
gender_target = gr.Radio( |
|
["No", "Female Only", "Male Only"], |
|
value="No", |
|
label="Gender Detection (Target)", |
|
type="index", |
|
) |
|
gr.Markdown("<br>") |
|
with gr.Row(): |
|
face_restorer_name = gr.Radio( |
|
label="Restore Face", |
|
choices=["None"] + [x.name() for x in shared.face_restorers], |
|
value=shared.face_restorers[0].name(), |
|
type="value", |
|
) |
|
with gr.Column(): |
|
face_restorer_visibility = gr.Slider( |
|
0, 1, 1, step=0.1, label="Restore Face Visibility" |
|
) |
|
codeformer_weight = gr.Slider( |
|
0, 1, 0.5, step=0.1, label="CodeFormer Weight", info="0 = maximum effect, 1 = minimum effect" |
|
) |
|
gr.Markdown("<br>") |
|
swap_in_source = gr.Checkbox( |
|
False, |
|
label="Swap in source image", |
|
visible=is_img2img, |
|
) |
|
swap_in_generated = gr.Checkbox( |
|
True, |
|
label="Swap in generated image", |
|
visible=is_img2img, |
|
) |
|
|
|
|
|
with gr.Tab("Upscale"): |
|
restore_first = gr.Checkbox( |
|
True, |
|
label="1. Restore Face -> 2. Upscale (-Uncheck- if you want vice versa)", |
|
info="Postprocessing Order" |
|
) |
|
with gr.Row(): |
|
upscaler_name = gr.Dropdown( |
|
choices=[upscaler.name for upscaler in shared.sd_upscalers], |
|
label="Upscaler", |
|
value="None", |
|
info="Won't scale if you choose -Swap in Source- via img2img, only 1x-postprocessing will affect (texturing, denoising, restyling etc.)" |
|
) |
|
upscalers_update = gr.Button( |
|
value="🔄", |
|
variant="tool", |
|
) |
|
upscalers_update.click( |
|
update_upscalers_list, |
|
inputs=[upscaler_name], |
|
outputs=[upscaler_name], |
|
) |
|
gr.Markdown("<br>") |
|
with gr.Row(): |
|
upscaler_scale = gr.Slider(1, 8, 1, step=0.1, label="Scale by") |
|
upscaler_visibility = gr.Slider( |
|
0, 1, 1, step=0.1, label="Upscaler Visibility (if scale = 1)" |
|
) |
|
|
|
|
|
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", |
|
) |
|
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("Settings"): |
|
models = get_models() |
|
with gr.Row(visible=EP_is_visible): |
|
device = gr.Radio( |
|
label="Execution Provider", |
|
choices=DEVICE_LIST, |
|
value=DEVICE, |
|
type="value", |
|
info="If you already run 'Generate' - RESTART is required to apply. Click 'Save', (A1111) Extensions Tab -> 'Apply and restart UI' or (SD.Next) close the Server and start it again", |
|
scale=2, |
|
) |
|
save_device_btn = gr.Button("Save", scale=0) |
|
save = gr.Markdown("", visible=EP_is_visible) |
|
setattr(device, "do_not_save_to_config", True) |
|
save_device_btn.click( |
|
set_Device, |
|
inputs=[device], |
|
outputs=[save], |
|
) |
|
with gr.Row(): |
|
if len(models) == 0: |
|
logger.warning( |
|
"You should at least have one model in models directory, please read the doc here: https://github.com/Gourieff/sd-webui-reactor/" |
|
) |
|
model = gr.Dropdown( |
|
choices=models, |
|
label="Model not found, please download one and refresh the list" |
|
) |
|
else: |
|
model = gr.Dropdown( |
|
choices=models, label="Model", value=models[0] |
|
) |
|
models_update = gr.Button( |
|
value="🔄", |
|
variant="tool", |
|
) |
|
models_update.click( |
|
update_models_list, |
|
inputs=[model], |
|
outputs=[model], |
|
) |
|
console_logging_level = gr.Radio( |
|
["No log", "Minimum", "Default"], |
|
value="Minimum", |
|
label="Console Log Level", |
|
type="index" |
|
) |
|
gr.Markdown("<br>") |
|
with gr.Row(): |
|
source_hash_check = gr.Checkbox( |
|
True, |
|
label="Source Image Hash Check", |
|
info="Recommended to keep it ON. Processing is faster when Source Image is the same." |
|
) |
|
target_hash_check = gr.Checkbox( |
|
False, |
|
label="Target Image Hash Check", |
|
info="Affects if you use Extras tab or img2img with only 'Swap in source image' on." |
|
) |
|
|
|
gr.Markdown("<span style='display:block;text-align:right;padding:3px;font-size:0.666em'>by Eugene Gourieff</span>") |
|
|
|
return [ |
|
img, |
|
enable, |
|
source_faces_index, |
|
faces_index, |
|
model, |
|
face_restorer_name, |
|
face_restorer_visibility, |
|
restore_first, |
|
upscaler_name, |
|
upscaler_scale, |
|
upscaler_visibility, |
|
swap_in_source, |
|
swap_in_generated, |
|
console_logging_level, |
|
gender_source, |
|
gender_target, |
|
save_original, |
|
codeformer_weight, |
|
source_hash_check, |
|
target_hash_check, |
|
device, |
|
mask_face, |
|
select_source, |
|
face_model, |
|
] |
|
|
|
|
|
@property |
|
def upscaler(self) -> UpscalerData: |
|
for upscaler in shared.sd_upscalers: |
|
if upscaler.name == self.upscaler_name: |
|
return upscaler |
|
return None |
|
|
|
@property |
|
def face_restorer(self) -> FaceRestoration: |
|
for face_restorer in shared.face_restorers: |
|
if face_restorer.name() == self.face_restorer_name: |
|
return face_restorer |
|
return None |
|
|
|
@property |
|
def enhancement_options(self) -> EnhancementOptions: |
|
return EnhancementOptions( |
|
do_restore_first = self.restore_first, |
|
scale=self.upscaler_scale, |
|
upscaler=self.upscaler, |
|
face_restorer=self.face_restorer, |
|
upscale_visibility=self.upscaler_visibility, |
|
restorer_visibility=self.face_restorer_visibility, |
|
codeformer_weight=self.codeformer_weight, |
|
) |
|
|
|
def process( |
|
self, |
|
p: StableDiffusionProcessing, |
|
img, |
|
enable, |
|
source_faces_index, |
|
faces_index, |
|
model, |
|
face_restorer_name, |
|
face_restorer_visibility, |
|
restore_first, |
|
upscaler_name, |
|
upscaler_scale, |
|
upscaler_visibility, |
|
swap_in_source, |
|
swap_in_generated, |
|
console_logging_level, |
|
gender_source, |
|
gender_target, |
|
save_original, |
|
codeformer_weight, |
|
source_hash_check, |
|
target_hash_check, |
|
device, |
|
mask_face, |
|
select_source, |
|
face_model, |
|
): |
|
self.enable = enable |
|
if self.enable: |
|
|
|
logger.debug("*** Start process") |
|
|
|
reset_messaged() |
|
if check_process_halt(): |
|
return |
|
|
|
global MODELS_PATH |
|
self.source = img |
|
self.face_restorer_name = face_restorer_name |
|
self.upscaler_scale = upscaler_scale |
|
self.upscaler_visibility = upscaler_visibility |
|
self.face_restorer_visibility = face_restorer_visibility |
|
self.restore_first = restore_first |
|
self.upscaler_name = upscaler_name |
|
self.swap_in_source = swap_in_source |
|
self.swap_in_generated = swap_in_generated |
|
self.model = os.path.join(MODELS_PATH,model) |
|
self.console_logging_level = console_logging_level |
|
self.gender_source = gender_source |
|
self.gender_target = gender_target |
|
self.save_original = save_original |
|
self.codeformer_weight = codeformer_weight |
|
self.source_hash_check = source_hash_check |
|
self.target_hash_check = target_hash_check |
|
self.device = device |
|
self.mask_face = mask_face |
|
self.select_source = select_source |
|
self.face_model = face_model |
|
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": |
|
self.gender_target = 0 |
|
self.source_faces_index = [ |
|
int(x) for x in source_faces_index.strip(",").split(",") if x.isnumeric() |
|
] |
|
self.faces_index = [ |
|
int(x) for x in faces_index.strip(",").split(",") if x.isnumeric() |
|
] |
|
if len(self.source_faces_index) == 0: |
|
self.source_faces_index = [0] |
|
if len(self.faces_index) == 0: |
|
self.faces_index = [0] |
|
if self.save_original is None: |
|
self.save_original = False |
|
if self.source_hash_check is None: |
|
self.source_hash_check = True |
|
if self.target_hash_check is None: |
|
self.target_hash_check = False |
|
if self.mask_face is None: |
|
self.mask_face = False |
|
|
|
logger.debug("*** Set Device") |
|
set_Device(self.device) |
|
|
|
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.debug("*** Log patch") |
|
apply_logging_patch(console_logging_level) |
|
if isinstance(p, StableDiffusionProcessingImg2Img) and self.swap_in_source: |
|
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index) |
|
|
|
for i in range(len(p.init_images)): |
|
if len(p.init_images) > 1: |
|
logger.status("Swap in %s", i) |
|
result, output, swapped = swap_face( |
|
self.source, |
|
p.init_images[i], |
|
source_faces_index=self.source_faces_index, |
|
faces_index=self.faces_index, |
|
model=self.model, |
|
enhancement_options=self.enhancement_options, |
|
gender_source=self.gender_source, |
|
gender_target=self.gender_target, |
|
source_hash_check=self.source_hash_check, |
|
target_hash_check=self.target_hash_check, |
|
device=self.device, |
|
mask_face=self.mask_face, |
|
select_source=self.select_source, |
|
face_model = self.face_model, |
|
) |
|
p.init_images[i] = result |
|
|
|
|
|
|
|
|
|
|
|
if shared.state.interrupted or shared.state.skipped: |
|
return |
|
|
|
else: |
|
logger.error("Please provide a source face") |
|
return |
|
|
|
def postprocess(self, p: StableDiffusionProcessing, processed: Processed, *args): |
|
if self.enable: |
|
|
|
logger.debug("*** Check postprocess") |
|
|
|
reset_messaged() |
|
if check_process_halt(): |
|
return |
|
|
|
if self.save_original: |
|
|
|
postprocess_run: bool = True |
|
|
|
orig_images : List[Image.Image] = processed.images[processed.index_of_first_image:] |
|
orig_infotexts : List[str] = processed.infotexts[processed.index_of_first_image:] |
|
|
|
result_images: List = processed.images |
|
|
|
|
|
if self.swap_in_generated: |
|
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index) |
|
|
|
for i,(img,info) in enumerate(zip(orig_images, orig_infotexts)): |
|
if check_process_halt(): |
|
postprocess_run = False |
|
break |
|
if len(orig_images) > 1: |
|
logger.status("Swap in %s", i) |
|
result, output, swapped = swap_face( |
|
self.source, |
|
img, |
|
source_faces_index=self.source_faces_index, |
|
faces_index=self.faces_index, |
|
model=self.model, |
|
enhancement_options=self.enhancement_options, |
|
gender_source=self.gender_source, |
|
gender_target=self.gender_target, |
|
source_hash_check=self.source_hash_check, |
|
target_hash_check=self.target_hash_check, |
|
device=self.device, |
|
mask_face=self.mask_face, |
|
select_source=self.select_source, |
|
face_model = self.face_model, |
|
) |
|
if result is not None and swapped > 0: |
|
result_images.append(result) |
|
suffix = "-swapped" |
|
try: |
|
img_path = save_image(result, p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix) |
|
except: |
|
logger.error("Cannot save a result image - please, check SD WebUI Settings (Saving and Paths)") |
|
elif result is None: |
|
logger.error("Cannot create a result image") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if shared.opts.return_grid and len(result_images) > 2 and postprocess_run: |
|
grid = make_grid(result_images) |
|
result_images.insert(0, grid) |
|
try: |
|
save_image(grid, p.outpath_grids, "grid", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=info, short_filename=not shared.opts.grid_extended_filename, p=p, grid=True) |
|
except: |
|
logger.error("Cannot save a grid - please, check SD WebUI Settings (Saving and Paths)") |
|
|
|
processed.images = result_images |
|
|
|
|
|
def postprocess_batch(self, p, *args, **kwargs): |
|
if self.enable and not self.save_original: |
|
logger.debug("*** Check postprocess_batch") |
|
images = kwargs["images"] |
|
|
|
def postprocess_image(self, p, script_pp: scripts.PostprocessImageArgs, *args): |
|
if self.enable and self.swap_in_generated and not self.save_original: |
|
|
|
logger.debug("*** Check postprocess_image") |
|
|
|
current_job_number = shared.state.job_no + 1 |
|
job_count = shared.state.job_count |
|
if current_job_number == job_count: |
|
reset_messaged() |
|
if check_process_halt(): |
|
return |
|
|
|
|
|
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, |
|
image, |
|
source_faces_index=self.source_faces_index, |
|
faces_index=self.faces_index, |
|
model=self.model, |
|
enhancement_options=self.enhancement_options, |
|
gender_source=self.gender_source, |
|
gender_target=self.gender_target, |
|
source_hash_check=self.source_hash_check, |
|
target_hash_check=self.target_hash_check, |
|
device=self.device, |
|
mask_face=self.mask_face, |
|
select_source=self.select_source, |
|
face_model = self.face_model, |
|
) |
|
try: |
|
pp = scripts_postprocessing.PostprocessedImage(result) |
|
pp.info = {} |
|
p.extra_generation_params.update(pp.info) |
|
script_pp.image = pp.image |
|
|
|
|
|
|
|
|
|
|
|
|
|
except: |
|
logger.error("Cannot create a result image") |
|
|
|
|
|
class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): |
|
name = 'ReActor' |
|
order = 20000 |
|
|
|
def ui(self): |
|
with gr.Accordion(f"{app_title}", open=False): |
|
|
|
def update_fm_list(selected: str): |
|
return gr.Dropdown.update( |
|
value=selected, choices=get_model_names(get_facemodels) |
|
) |
|
def update_upscalers_list(selected: str): |
|
return gr.Dropdown.update( |
|
value=selected, choices=[upscaler.name for upscaler in shared.sd_upscalers] |
|
) |
|
def update_models_list(selected: str): |
|
return gr.Dropdown.update( |
|
value=selected, choices=get_models() |
|
) |
|
|
|
|
|
with gr.Tab("Main"): |
|
with gr.Column(): |
|
img = gr.Image(type="pil") |
|
enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}") |
|
|
|
with gr.Row(): |
|
select_source = gr.Radio( |
|
["Image","Face Model"], |
|
value="Image", |
|
label="Select Source", |
|
type="index", |
|
scale=1, |
|
) |
|
face_models = get_model_names(get_facemodels) |
|
face_model = gr.Dropdown( |
|
choices=face_models, |
|
label="Choose Face Model", |
|
value="None", |
|
scale=2, |
|
) |
|
fm_update = gr.Button( |
|
value="🔄", |
|
variant="tool", |
|
) |
|
fm_update.click( |
|
update_fm_list, |
|
inputs=[face_model], |
|
outputs=[face_model], |
|
) |
|
setattr(face_model, "do_not_save_to_config", True) |
|
mask_face = gr.Checkbox( |
|
False, |
|
label="Face Mask Correction", |
|
info="Apply this option if you see some pixelation around face contours" |
|
) |
|
gr.Markdown("Source Image (above):") |
|
with gr.Row(): |
|
source_faces_index = gr.Textbox( |
|
value="0", |
|
placeholder="Which face(s) to use as Source (comma separated)", |
|
label="Comma separated face number(s); Example: 0,2,1", |
|
) |
|
gender_source = gr.Radio( |
|
["No", "Female Only", "Male Only"], |
|
value="No", |
|
label="Gender Detection (Source)", |
|
type="index", |
|
) |
|
gr.Markdown("Target Image (result):") |
|
with gr.Row(): |
|
faces_index = gr.Textbox( |
|
value="0", |
|
placeholder="Which face(s) to Swap into Target (comma separated)", |
|
label="Comma separated face number(s); Example: 1,0,2", |
|
) |
|
gender_target = gr.Radio( |
|
["No", "Female Only", "Male Only"], |
|
value="No", |
|
label="Gender Detection (Target)", |
|
type="index", |
|
) |
|
with gr.Row(): |
|
face_restorer_name = gr.Radio( |
|
label="Restore Face", |
|
choices=["None"] + [x.name() for x in shared.face_restorers], |
|
value=shared.face_restorers[0].name(), |
|
type="value", |
|
) |
|
with gr.Column(): |
|
face_restorer_visibility = gr.Slider( |
|
0, 1, 1, step=0.1, label="Restore Face Visibility" |
|
) |
|
codeformer_weight = gr.Slider( |
|
0, 1, 0.5, step=0.1, label="CodeFormer Weight", info="0 = maximum effect, 1 = minimum effect" |
|
) |
|
|
|
|
|
with gr.Tab("Upscale"): |
|
restore_first = gr.Checkbox( |
|
True, |
|
label="1. Restore Face -> 2. Upscale (-Uncheck- if you want vice versa)", |
|
info="Postprocessing Order" |
|
) |
|
with gr.Row(): |
|
upscaler_name = gr.Dropdown( |
|
choices=[upscaler.name for upscaler in shared.sd_upscalers], |
|
label="Upscaler", |
|
value="None", |
|
info="Won't scale if you choose -Swap in Source- via img2img, only 1x-postprocessing will affect (texturing, denoising, restyling etc.)" |
|
) |
|
upscalers_update = gr.Button( |
|
value="🔄", |
|
variant="tool", |
|
) |
|
upscalers_update.click( |
|
update_upscalers_list, |
|
inputs=[upscaler_name], |
|
outputs=[upscaler_name], |
|
) |
|
with gr.Row(): |
|
upscaler_scale = gr.Slider(1, 8, 1, step=0.1, label="Scale by") |
|
upscaler_visibility = gr.Slider( |
|
0, 1, 1, step=0.1, label="Upscaler Visibility (if scale = 1)" |
|
) |
|
|
|
|
|
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", |
|
) |
|
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("Settings"): |
|
models = get_models() |
|
with gr.Row(visible=EP_is_visible): |
|
device = gr.Radio( |
|
label="Execution Provider", |
|
choices=DEVICE_LIST, |
|
value=DEVICE, |
|
type="value", |
|
info="If you already run 'Generate' - RESTART is required to apply. Click 'Save', (A1111) Extensions Tab -> 'Apply and restart UI' or (SD.Next) close the Server and start it again", |
|
scale=2, |
|
) |
|
save_device_btn = gr.Button("Save", scale=0) |
|
save = gr.Markdown("", visible=EP_is_visible) |
|
setattr(device, "do_not_save_to_config", True) |
|
save_device_btn.click( |
|
set_Device, |
|
inputs=[device], |
|
outputs=[save], |
|
) |
|
with gr.Row(): |
|
if len(models) == 0: |
|
logger.warning( |
|
"You should at least have one model in models directory, please read the doc here: https://github.com/Gourieff/sd-webui-reactor/" |
|
) |
|
model = gr.Dropdown( |
|
choices=models, |
|
label="Model not found, please download one and refresh the list", |
|
) |
|
else: |
|
model = gr.Dropdown( |
|
choices=models, label="Model", value=models[0] |
|
) |
|
models_update = gr.Button( |
|
value="🔄", |
|
variant="tool", |
|
) |
|
models_update.click( |
|
update_models_list, |
|
inputs=[model], |
|
outputs=[model], |
|
) |
|
console_logging_level = gr.Radio( |
|
["No log", "Minimum", "Default"], |
|
value="Minimum", |
|
label="Console Log Level", |
|
type="index", |
|
) |
|
|
|
gr.Markdown("<span style='display:block;text-align:right;padding-right:3px;font-size:0.666em;margin: -9px 0'>by Eugene Gourieff</span>") |
|
|
|
args = { |
|
'img': img, |
|
'enable': enable, |
|
'source_faces_index': source_faces_index, |
|
'faces_index': faces_index, |
|
'model': model, |
|
'face_restorer_name': face_restorer_name, |
|
'face_restorer_visibility': face_restorer_visibility, |
|
'restore_first': restore_first, |
|
'upscaler_name': upscaler_name, |
|
'upscaler_scale': upscaler_scale, |
|
'upscaler_visibility': upscaler_visibility, |
|
'console_logging_level': console_logging_level, |
|
'gender_source': gender_source, |
|
'gender_target': gender_target, |
|
'codeformer_weight': codeformer_weight, |
|
'device': device, |
|
'mask_face': mask_face, |
|
'select_source': select_source, |
|
'face_model': face_model, |
|
} |
|
return args |
|
|
|
@property |
|
def upscaler(self) -> UpscalerData: |
|
for upscaler in shared.sd_upscalers: |
|
if upscaler.name == self.upscaler_name: |
|
return upscaler |
|
return None |
|
|
|
@property |
|
def face_restorer(self) -> FaceRestoration: |
|
for face_restorer in shared.face_restorers: |
|
if face_restorer.name() == self.face_restorer_name: |
|
return face_restorer |
|
return None |
|
|
|
@property |
|
def enhancement_options(self) -> EnhancementOptions: |
|
return EnhancementOptions( |
|
do_restore_first=self.restore_first, |
|
scale=self.upscaler_scale, |
|
upscaler=self.upscaler, |
|
face_restorer=self.face_restorer, |
|
upscale_visibility=self.upscaler_visibility, |
|
restorer_visibility=self.face_restorer_visibility, |
|
codeformer_weight=self.codeformer_weight, |
|
) |
|
|
|
def process(self, pp: scripts_postprocessing.PostprocessedImage, **args): |
|
if args['enable']: |
|
reset_messaged() |
|
if check_process_halt(): |
|
return |
|
|
|
global MODELS_PATH |
|
self.source = args['img'] |
|
self.face_restorer_name = args['face_restorer_name'] |
|
self.upscaler_scale = args['upscaler_scale'] |
|
self.upscaler_visibility = args['upscaler_visibility'] |
|
self.face_restorer_visibility = args['face_restorer_visibility'] |
|
self.restore_first = args['restore_first'] |
|
self.upscaler_name = args['upscaler_name'] |
|
self.model = os.path.join(MODELS_PATH, args['model']) |
|
self.console_logging_level = args['console_logging_level'] |
|
self.gender_source = args['gender_source'] |
|
self.gender_target = args['gender_target'] |
|
self.codeformer_weight = args['codeformer_weight'] |
|
self.device = args['device'] |
|
self.mask_face = args['mask_face'] |
|
self.select_source = args['select_source'] |
|
self.face_model = args['face_model'] |
|
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": |
|
self.gender_target = 0 |
|
self.source_faces_index = [ |
|
int(x) for x in args['source_faces_index'].strip(",").split(",") if x.isnumeric() |
|
] |
|
self.faces_index = [ |
|
int(x) for x in args['faces_index'].strip(",").split(",") if x.isnumeric() |
|
] |
|
if len(self.source_faces_index) == 0: |
|
self.source_faces_index = [0] |
|
if len(self.faces_index) == 0: |
|
self.faces_index = [0] |
|
if self.mask_face is None: |
|
self.mask_face = False |
|
|
|
current_job_number = shared.state.job_no + 1 |
|
job_count = shared.state.job_count |
|
if current_job_number == job_count: |
|
reset_messaged() |
|
|
|
set_Device(self.device) |
|
|
|
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): |
|
apply_logging_patch(self.console_logging_level) |
|
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index) |
|
image: Image.Image = pp.image |
|
result, output, swapped = swap_face( |
|
self.source, |
|
image, |
|
source_faces_index=self.source_faces_index, |
|
faces_index=self.faces_index, |
|
model=self.model, |
|
enhancement_options=self.enhancement_options, |
|
gender_source=self.gender_source, |
|
gender_target=self.gender_target, |
|
source_hash_check=True, |
|
target_hash_check=True, |
|
device=self.device, |
|
mask_face=self.mask_face, |
|
select_source=self.select_source, |
|
face_model=self.face_model, |
|
) |
|
try: |
|
pp.info["ReActor"] = True |
|
pp.image = result |
|
logger.status("---Done!---") |
|
except Exception: |
|
logger.error("Cannot create a result image") |
|
else: |
|
logger.error("Please provide a source face") |
|
|