diff --git a/README.md b/README.md index 424fe4a..ea68037 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ logo - ![Version](https://img.shields.io/badge/version-0.6.1_beta1-green?style=for-the-badge&labelColor=darkgreen) + ![Version](https://img.shields.io/badge/version-0.6.1_beta2-green?style=for-the-badge&labelColor=darkgreen) Support Me on Boosty @@ -40,8 +40,18 @@ ## What's new in the latest updates -### 0.6.0 ALPHA1 +### 0.6.1 BETA2 +- 'Save orignal' option works fine now when you select 'Multiple Images' or 'Source Folder' +- Random Mode for 'Source Folder' + +0.6.1-whatsnew-01 + +### 0.6.0 + +- New Logo +- Adaptation to A1111 1.7.0 (appropriate GFPGAN loader) +- New URL for the main model file - UI reworked - You can now load several source images (with reference faces) or set the path to the folder containing faces images diff --git a/README_RU.md b/README_RU.md index a2f4e78..3cbe555 100644 --- a/README_RU.md +++ b/README_RU.md @@ -2,7 +2,7 @@ logo - ![Version](https://img.shields.io/badge/версия-0.6.1_beta1-green?style=for-the-badge&labelColor=darkgreen) + ![Version](https://img.shields.io/badge/версия-0.6.1_beta2-green?style=for-the-badge&labelColor=darkgreen) Поддержать проект на Boosty @@ -39,8 +39,18 @@ ## Что нового в последних обновлениях -### 0.6.0 ALPHA1 +### 0.6.1 BETA2 +- Опция 'Save orignal' теперь работает правильно, когда вы выбираете 'Multiple Images' или 'Source Folder' +- Добавлен режим выбора случайного изображения для 'Source Folder' + +0.6.1-whatsnew-01 + +### 0.6.0 + +- Новый логотип +- Адаптация к версии A1111 1.7.0 (правильная загрузка GFPGAN) +- Новая ссылка для файла основной модели - UI переработан - Появилась возможность загружать несколько исходных изображений с лицами или задавать путь к папке, содержащей такие изображения diff --git a/example/api_example.py b/example/api_example.py index 9a0caf0..3c9e3c4 100644 --- a/example/api_example.py +++ b/example/api_example.py @@ -47,6 +47,8 @@ args=[ 1, #22 Select Source, 0 - Image, 1 - Face Model, 2 - Source Folder "elena.safetensors", #23 Filename of the face model (from "models/reactor/faces"), e.g. elena.safetensors, don't forger to set #22 to 1 "C:\PATH_TO_FACES_IMAGES", #24 The path to the folder containing source faces images, don't forger to set #22 to 2 + None, #25 skip it for API + True, #26 Randomly select an image from the path ] # The args for ReActor can be found by diff --git a/example/api_external.curl b/example/api_external.curl index f324f0e..4033d84 100644 --- a/example/api_external.curl +++ b/example/api_external.curl @@ -22,5 +22,7 @@ curl -X POST \ "device": "CUDA", "mask_face": 1, "select_source": 1, - "face_model": "elena.safetensors" + "face_model": "elena.safetensors", + "source_folder": "C:/faces", + "random_image": 1 }' diff --git a/example/api_external.json b/example/api_external.json index c4b32a4..542da69 100644 --- a/example/api_external.json +++ b/example/api_external.json @@ -18,5 +18,7 @@ "device": "CUDA", "mask_face": 1, "select_source": 1, - "face_model": "elena.safetensors" + "face_model": "elena.safetensors", + "source_folder": "C:/faces", + "random_image": 1 } \ No newline at end of file diff --git a/reactor_ui/reactor_main_ui.py b/reactor_ui/reactor_main_ui.py index 5b2b9a2..bbbd248 100644 --- a/reactor_ui/reactor_main_ui.py +++ b/reactor_ui/reactor_main_ui.py @@ -8,7 +8,7 @@ from scripts.reactor_swapper import ( ) from modules import shared -SAVE_ORIGINAL: bool = False +# SAVE_ORIGINAL: bool = False def update_fm_list(selected: str): return gr.Dropdown.update( @@ -18,16 +18,17 @@ def update_fm_list(selected: str): # TAB MAIN def show(is_img2img: bool, show_br: bool = True, **msgs): - def on_select_source(selected: bool, evt: gr.SelectData): - global SAVE_ORIGINAL + # def on_select_source(selected: bool, evt: gr.SelectData): + def on_select_source(evt: gr.SelectData): + # global SAVE_ORIGINAL if evt.index == 2: - if SAVE_ORIGINAL != selected: - SAVE_ORIGINAL = selected + # if SAVE_ORIGINAL != selected: + # SAVE_ORIGINAL = selected return { control_col_1: gr.Column.update(visible=False), control_col_2: gr.Column.update(visible=False), control_col_3: gr.Column.update(visible=True), - save_original: gr.Checkbox.update(value=False,visible=False), + # save_original: gr.Checkbox.update(value=False,visible=False), imgs_hash_clear: gr.Button.update(visible=True) } if evt.index == 0: @@ -35,7 +36,7 @@ def show(is_img2img: bool, show_br: bool = True, **msgs): control_col_1: gr.Column.update(visible=True), control_col_2: gr.Column.update(visible=False), control_col_3: gr.Column.update(visible=False), - save_original: gr.Checkbox.update(value=SAVE_ORIGINAL,visible=show_br), + # save_original: gr.Checkbox.update(value=SAVE_ORIGINAL,visible=show_br), imgs_hash_clear: gr.Button.update(visible=False) } if evt.index == 1: @@ -43,7 +44,7 @@ def show(is_img2img: bool, show_br: bool = True, **msgs): control_col_1: gr.Column.update(visible=False), control_col_2: gr.Column.update(visible=True), control_col_3: gr.Column.update(visible=False), - save_original: gr.Checkbox.update(value=SAVE_ORIGINAL,visible=show_br), + # save_original: gr.Checkbox.update(value=SAVE_ORIGINAL,visible=show_br), imgs_hash_clear: gr.Button.update(visible=False) } @@ -96,23 +97,31 @@ def show(is_img2img: bool, show_br: bool = True, **msgs): ) with gr.Column(visible=False) as control_col_3: gr.Markdown("Clear Hash if you see the previous face was swapped instead of the new one") - source_folder = gr.Textbox( - value="", - placeholder="Paste here the path to the folder containing source faces images", - label=f"Source Folder{msgs['extra_multiple_source']}", - ) + with gr.Row(): + source_folder = gr.Textbox( + value="", + placeholder="Paste here the path to the folder containing source faces images", + label=f"Source Folder{msgs['extra_multiple_source']}", + scale=2, + ) + random_image = gr.Checkbox( + False, + label="Random Image", + info="Randomly select an image from the path", + scale=1, + ) setattr(face_model, "do_not_save_to_config", True) if is_img2img: save_original = gr.Checkbox( False, label="Save Original (Swap in generated only)", - info="Save the original image(s) made before swapping (it always saves Original when you use Multiple Images or Folder)" + info="Save the original image(s) made before swapping" ) else: save_original = gr.Checkbox( False, label="Save Original", - info="Save the original image(s) made before swapping (it always saves Original when you use Multiple Images or Folder)", + info="Save the original image(s) made before swapping", visible=show_br ) # imgs.upload(on_files_upload_uncheck_so,[save_original],[save_original],show_progress=False) @@ -177,6 +186,7 @@ def show(is_img2img: bool, show_br: bool = True, **msgs): label="Swap in generated image", visible=is_img2img, ) - select_source.select(on_select_source,[save_original],[control_col_1,control_col_2,control_col_3,save_original,imgs_hash_clear],show_progress=False) + # select_source.select(on_select_source,[save_original],[control_col_1,control_col_2,control_col_3,save_original,imgs_hash_clear],show_progress=False) + select_source.select(on_select_source,None,[control_col_1,control_col_2,control_col_3,imgs_hash_clear],show_progress=False) - return 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 + return 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 diff --git a/scripts/reactor_api.py b/scripts/reactor_api.py index 4d9ce48..6e0b69c 100644 --- a/scripts/reactor_api.py +++ b/scripts/reactor_api.py @@ -1,7 +1,7 @@ ''' Thanks SpenserCai for the original version of the roop api script ----------------------------------- ---- ReActor External API v1.0.2 --- +--- ReActor External API v1.0.3 --- ----------------------------------- ''' import os, glob @@ -75,7 +75,8 @@ def reactor_api(_: gr.Blocks, app: FastAPI): mask_face: int = Body(0,title="Face Mask Correction, 1 - True, 0 - False"), select_source: int = Body(0,title="Select Source, 0 - Image, 1 - Face Model, 2 - Source Folder"), 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") + 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") ): s_image = api.decode_base64_to_image(source_image) if select_source == 0 else None t_image = api.decode_base64_to_image(target_image) @@ -85,11 +86,12 @@ def reactor_api(_: gr.Blocks, app: FastAPI): gender_t = gender_target restore_first_bool = True if restore_first == 1 else False mask_face = True if mask_face == 1 else False + random_image = False if random_image == 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) 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) + 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) if save_to_file == 1: if result_file_path == "": result_file_path = default_file_path() diff --git a/scripts/reactor_faceswap.py b/scripts/reactor_faceswap.py index 1a45fab..61adcf0 100644 --- a/scripts/reactor_faceswap.py +++ b/scripts/reactor_faceswap.py @@ -63,7 +63,7 @@ class FaceSwapScript(scripts.Script): msgs: dict = { "extra_multiple_source": "", } - 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 = ui_main.show(is_img2img=is_img2img, **msgs) + 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 UPSCALE restore_first, upscaler_name, upscaler_scale, upscaler_visibility = ui_upscale.show() @@ -103,6 +103,7 @@ class FaceSwapScript(scripts.Script): face_model, source_folder, imgs, + random_image, ] @@ -161,6 +162,7 @@ class FaceSwapScript(scripts.Script): face_model, source_folder, imgs, + random_image, ): self.enable = enable if self.enable: @@ -195,6 +197,7 @@ class FaceSwapScript(scripts.Script): self.face_model = face_model self.source_folder = source_folder self.source_imgs = imgs + self.random_image = random_image 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": @@ -217,9 +220,14 @@ class FaceSwapScript(scripts.Script): self.target_hash_check = False if self.mask_face is None: self.mask_face = False + if self.random_image is None: + self.random_image = False logger.debug("*** Set Device") set_Device(self.device) + + if (self.save_original is None or not self.save_original) and (self.select_source == 2 or self.source_imgs is not None): + p.do_not_save_samples = True if ((self.source is not None or self.source_imgs 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) or ((self.source_folder is not None and self.source_folder != "") and self.select_source == 2): logger.debug("*** Log patch") @@ -247,6 +255,7 @@ class FaceSwapScript(scripts.Script): face_model = self.face_model, source_folder = None, source_imgs = None, + random_image = False, ) 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") @@ -311,17 +320,23 @@ class FaceSwapScript(scripts.Script): face_model = self.face_model, source_folder = self.source_folder, source_imgs = self.source_imgs, + random_image = self.random_image, ) 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: - result_images.extend(result) + # result_images.extend(result) + if self.save_original: + result_images.extend(result) + else: + result_images = result suffix = "-swapped" for i,x in enumerate(result): try: - img_path = save_image(result[i], p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix) + img_path = save_image(result[i], 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 len(result) == 0: logger.error("Cannot create a result image") @@ -330,7 +345,7 @@ class FaceSwapScript(scripts.Script): 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) + 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: @@ -390,6 +405,7 @@ class FaceSwapScript(scripts.Script): face_model = self.face_model, source_folder = None, source_imgs = None, + random_image = False, ) try: pp = scripts_postprocessing.PostprocessedImage(result) @@ -425,7 +441,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): msgs: dict = { "extra_multiple_source": " | Сomparison grid as a result", } - 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 = ui_main.show(is_img2img=False, show_br=False, **msgs) + 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 UPSCALE restore_first, upscaler_name, upscaler_scale, upscaler_visibility = ui_upscale.show(show_br=False) @@ -460,6 +476,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): 'face_model': face_model, 'source_folder': source_folder, 'imgs': imgs, + 'random_image': random_image, } return args @@ -514,6 +531,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): self.face_model = args['face_model'] self.source_folder = args['source_folder'] self.source_imgs = args['imgs'] + self.random_image = args['random_image'] 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": @@ -530,6 +548,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): self.faces_index = [0] if self.mask_face is None: self.mask_face = False + if self.random_image is None: + self.random_image = False current_job_number = shared.state.job_no + 1 job_count = shared.state.job_count @@ -565,6 +585,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): face_model=self.face_model, source_folder=self.source_folder, source_imgs=self.source_imgs, + random_image=self.random_image, ) 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: diff --git a/scripts/reactor_helpers.py b/scripts/reactor_helpers.py index 0290425..c3384bd 100644 --- a/scripts/reactor_helpers.py +++ b/scripts/reactor_helpers.py @@ -1,4 +1,4 @@ -import os, glob +import os, glob, random from collections import Counter from PIL import Image from math import isqrt, ceil @@ -213,5 +213,10 @@ def get_images_from_folder(path: str): images = glob.glob(images_path) return [Image.open(x) for x in images if x.endswith(('jpg', 'png', 'jpeg', 'webp', 'bmp'))] +def get_random_image_from_folder(path: str): + images = get_images_from_folder(path) + random_image_index = random.randint(0, len(images) - 1) + return [images[random_image_index]] + def get_images_from_list(imgs: List): return [Image.open(os.path.abspath(x.name)) for x in imgs] diff --git a/scripts/reactor_swapper.py b/scripts/reactor_swapper.py index c92cae4..6d771ee 100644 --- a/scripts/reactor_swapper.py +++ b/scripts/reactor_swapper.py @@ -17,6 +17,7 @@ from scripts.reactor_helpers import ( save_face_model, load_face_model, get_images_from_folder, + get_random_image_from_folder, get_images_from_list, set_SDNEXT ) @@ -335,6 +336,7 @@ def swap_face( face_model: str = "None", source_folder: str = "", source_imgs: Union[List, None] = None, + random_image: bool = False, ): global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH, PROVIDERS, SOURCE_FACES_LIST, SOURCE_IMAGE_LIST_HASH @@ -376,7 +378,11 @@ def swap_face( result = [] - source_images = get_images_from_folder(source_folder) if select_source == 2 else get_images_from_list(source_imgs) + if random_image and select_source == 2: + source_images = get_random_image_from_folder(source_folder) + logger.status("Processing with Random Image from the folder") + else: + source_images = get_images_from_folder(source_folder) if select_source == 2 else get_images_from_list(source_imgs) if len(source_images) > 0: source_img_ff = [] diff --git a/scripts/reactor_version.py b/scripts/reactor_version.py index b6a089e..e119b0b 100644 --- a/scripts/reactor_version.py +++ b/scripts/reactor_version.py @@ -1,5 +1,5 @@ app_title = "ReActor" -version_flag = "v0.6.1-b1" +version_flag = "v0.6.1-b2" from scripts.reactor_logger import logger, get_Run, set_Run from scripts.reactor_globals import DEVICE