From eb6d361ab5c8b5539ee5d22ab8fe1cce59137e4f Mon Sep 17 00:00:00 2001 From: Gourieff <777@lovemet.ru> Date: Fri, 11 Aug 2023 12:43:18 +0700 Subject: [PATCH] UPDATE: Gender Detection; FIX: install cond ">=" + VersionUP (beta) --- README.md | 2 +- example/api_example.py | 2 ++ install.py | 17 ++++++--- requirements.txt | 2 +- scripts/faceswap.py | 43 +++++++++++++++++++---- scripts/swapper.py | 80 +++++++++++++++++++++++++++++++++++------- scripts/version.py | 2 +- 7 files changed, 121 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 02511a0..a3fdfa3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ReActor 0.3.0 for StableDiffusion +# ReActor 0.3.1b for StableDiffusion ### The Fast and Simple "[roop-based](https://github.com/s0md3v/sd-webui-roop)" FaceSwap Extension with a lot of improvements and without NSFW filter (uncensored, use it on your own [responsibility](#disclaimer)) > Ex "Roop-GE" (GE - Gourieff Edition, aka "NSFW-Roop"), the extension was renamed with the version 0.3.0
diff --git a/example/api_example.py b/example/api_example.py index 4dcdf19..1867c20 100644 --- a/example/api_example.py +++ b/example/api_example.py @@ -36,6 +36,8 @@ args=[ False, #11 Swap in source image True, #12 Swap in generated image 1, #13 Console Log Level (0 - min, 1 - med or 2 - max) + 0, #14 Gender Detection (Source) (0 - No, 1 - Female Only, 2 - Male Only) + 0, #15 Gender Detection (Target) (0 - No, 1 - Female Only, 2 - Male Only) ] # The args for ReActor can be found by diff --git a/install.py b/install.py index 8f9f599..80ff624 100644 --- a/install.py +++ b/install.py @@ -3,6 +3,7 @@ import os, sys import pkg_resources from tqdm import tqdm import urllib.request +from packaging import version as pv req_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "requirements.txt") @@ -16,20 +17,21 @@ def run_pip(*args): subprocess.run([sys.executable, "-m", "pip", "install", *args]) def is_installed ( - package: str, version: str | None = None + package: str, version: str | None = None, strict: bool = True ): has_package = None try: has_package = pkg_resources.get_distribution(package) if has_package is not None: installed_version = has_package.version - if installed_version != version: + if (installed_version != version and strict == True) or (pv.parse(installed_version) < pv.parse(version) and strict == False): return False else: return True else: return False - except: + except Exception as e: + print(f"Error: {e}") return False def download(url, path): @@ -44,16 +46,20 @@ if not os.path.exists(models_dir): if not os.path.exists(model_path): download(model_url, model_path) -print("Checking ReActor (ex Roop-GE) requirements...", end=' ') +print("Checking ReActor requirements...", end=' ') with open(req_file) as file: install_count = 0 + strict = True for package in file: package_version = None try: package = package.strip() if "==" in package: package_version = package.split('==')[1] - if not is_installed(package,package_version): + elif ">=" in package: + package_version = package.split('>=')[1] + strict = False + if not is_installed(package,package_version,strict): install_count += 1 run_pip(package) except Exception as e: @@ -61,6 +67,7 @@ with open(req_file) as file: print(f"\nERROR: Failed to install {package} - ReActor won't start") raise e if install_count > 0: + print(f'install_count={install_count}') print(f'\n--- PLEASE, RESTART the Server! ---\n') else: print('Ok') diff --git a/requirements.txt b/requirements.txt index 059457a..b4c4fa8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ insightface==0.7.3 onnx==1.14.0 onnxruntime==1.15.0 -opencv-python==4.7.0.72 +opencv-python>=4.7.0.72 diff --git a/scripts/faceswap.py b/scripts/faceswap.py index 09fb04b..2d70b17 100644 --- a/scripts/faceswap.py +++ b/scripts/faceswap.py @@ -42,21 +42,39 @@ class FaceSwapScript(scripts.Script): return scripts.AlwaysVisible def ui(self, is_img2img): - with gr.Accordion(f"{app_title} (ex Roop-GE)", open=False): + with gr.Accordion(f"{app_title}", open=False): with gr.Column(): img = gr.inputs.Image(type="pil") enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple \"roop-based\" FaceSwap Extension - {version_flag}") + gr.Markdown("---") + 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) from swap-source image (above); Example: 0,2,1", + 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("---") + gr.Markdown("Target Image (result):") + with gr.Row(): faces_index = gr.Textbox( value="0", - placeholder="Which face to swap (comma separated)", - label="Comma separated face number(s) for target image (result); Example: 1,0,2", + 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("---") with gr.Row(): face_restorer_name = gr.Radio( label="Restore Face", @@ -81,7 +99,7 @@ class FaceSwapScript(scripts.Script): upscaler_visibility = gr.Slider( 0, 1, 1, step=0.1, label="Upscaler Visibility (if scale = 1)" ) - + gr.Markdown("---") swap_in_source = gr.Checkbox( False, label="Swap in source image", @@ -113,6 +131,7 @@ class FaceSwapScript(scripts.Script): label="Console Log Level", type="index", ) + gr.Markdown("---") return [ img, @@ -128,7 +147,9 @@ class FaceSwapScript(scripts.Script): upscaler_visibility, swap_in_source, swap_in_generated, - console_logging_level + console_logging_level, + gender_source, + gender_target, ] @@ -174,6 +195,8 @@ class FaceSwapScript(scripts.Script): swap_in_source, swap_in_generated, console_logging_level, + gender_source, + gender_target, ): global MODELS_PATH self.source = img @@ -187,6 +210,8 @@ class FaceSwapScript(scripts.Script): 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.source_faces_index = [ int(x) for x in source_faces_index.strip(",").split(",") if x.isnumeric() ] @@ -212,6 +237,8 @@ class FaceSwapScript(scripts.Script): faces_index=self.faces_index, model=self.model, upscale_options=self.upscale_options, + gender_source=self.gender_source, + gender_target=self.gender_target, ) p.init_images[i] = result else: @@ -233,6 +260,8 @@ class FaceSwapScript(scripts.Script): faces_index=self.faces_index, model=self.model, upscale_options=self.upscale_options, + gender_source=self.gender_source, + gender_target=self.gender_target, ) try: pp = scripts_postprocessing.PostprocessedImage(result) diff --git a/scripts/swapper.py b/scripts/swapper.py index 2482e90..82f9830 100644 --- a/scripts/swapper.py +++ b/scripts/swapper.py @@ -60,7 +60,7 @@ def getAnalysisModel(): global ANALYSIS_MODEL if ANALYSIS_MODEL is None: ANALYSIS_MODEL = insightface.app.FaceAnalysis( - name="buffalo_l", providers=providers + name="buffalo_l", providers=providers # note: allowed_modules=['detection', 'genderage'] ) return ANALYSIS_MODEL @@ -129,19 +129,57 @@ def upscale_image(image: Image, upscale_options: UpscaleOptions): return result_image -def get_face_single(img_data: np.ndarray, face_index=0, det_size=(640, 640)): +def get_face_gender( + face, + face_index, + gender_condition, + operated: str +): + gender = [ + x.sex + for x in face + ] + gender.reverse() + face_gender = gender[face_index] + logger.info("%s Face %s: Detected Gender -%s-", operated, face_index, face_gender) + if (gender_condition == 1 and face_gender == "F") or (gender_condition == 2 and face_gender == "M"): + logger.info("OK - Detected Gender matches Condition") + try: + return sorted(face, key=lambda x: x.bbox[0])[face_index], 0 + except IndexError: + return None, 0 + else: + logger.info("WRONG - Detected Gender doesn't match Condition") + return sorted(face, key=lambda x: x.bbox[0])[face_index], 1 + + +def reget_face_single(img_data, det_size, face_index): + det_size_half = (det_size[0] // 2, det_size[1] // 2) + return get_face_single(img_data, face_index=face_index, det_size=det_size_half) + + +def get_face_single(img_data: np.ndarray, face_index=0, det_size=(640, 640), gender_source=0, gender_target=0): face_analyser = copy.deepcopy(getAnalysisModel()) face_analyser.prepare(ctx_id=0, det_size=det_size) face = face_analyser.get(img_data) + if gender_source != 0: + if len(face) == 0 and det_size[0] > 320 and det_size[1] > 320: + return reget_face_single(img_data, det_size, face_index) + return get_face_gender(face,face_index,gender_source,"Source") + + if gender_target != 0: + if len(face) == 0 and det_size[0] > 320 and det_size[1] > 320: + return reget_face_single(img_data, det_size, face_index) + return get_face_gender(face,face_index,gender_target,"Target") + if len(face) == 0 and det_size[0] > 320 and det_size[1] > 320: - det_size_half = (det_size[0] // 2, det_size[1] // 2) - return get_face_single(img_data, face_index=face_index, det_size=det_size_half) + return reget_face_single(img_data, det_size, face_index) try: - return sorted(face, key=lambda x: x.bbox[0])[face_index] + return sorted(face, key=lambda x: x.bbox[0])[face_index], 0 except IndexError: - return None + return None, 0 def swap_face( @@ -151,6 +189,8 @@ def swap_face( source_faces_index: List[int] = [0], faces_index: List[int] = [0], upscale_options: Union[UpscaleOptions, None] = None, + gender_source: int = 0, + gender_target: int = 0, ): result_image = target_img if model is not None: @@ -170,9 +210,11 @@ def swap_face( source_img = cv2.cvtColor(np.array(source_img), cv2.COLOR_RGB2BGR) target_img = cv2.cvtColor(np.array(target_img), cv2.COLOR_RGB2BGR) - source_face = get_face_single(source_img, face_index=source_faces_index[0]) + + source_face, wrong_gender = get_face_single(source_img, face_index=source_faces_index[0], gender_source=gender_source) + if len(source_faces_index) != 0 and len(source_faces_index) != 1 and len(source_faces_index) != len(faces_index): - logger.info(f'Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.') + logger.info(f'Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.') elif source_face is not None: result = target_img model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), model) @@ -182,15 +224,29 @@ def swap_face( for face_num in faces_index: if len(source_faces_index) > 1 and source_face_idx > 0: - source_face = get_face_single(source_img, face_index=source_faces_index[source_face_idx]) + source_face, wrong_gender = get_face_single(source_img, face_index=source_faces_index[source_face_idx], gender_source=gender_source) source_face_idx += 1 - if source_face is not None: - target_face = get_face_single(target_img, face_index=face_num) - if target_face is not None: + if source_face is not None and wrong_gender == 0: + target_face, wrong_gender = get_face_single(target_img, face_index=face_num, gender_target=gender_target) + if target_face is not None and wrong_gender == 0: result = face_swapper.get(result, target_face, source_face) + elif wrong_gender == 1: + wrong_gender = 0 + if source_face_idx == len(source_faces_index): + result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB)) + if upscale_options is not None: + result_image = upscale_image(result_image, upscale_options) + return result_image else: logger.info(f"No target face found for {face_num}") + elif wrong_gender == 1: + wrong_gender = 0 + if source_face_idx == len(source_faces_index): + result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB)) + if upscale_options is not None: + result_image = upscale_image(result_image, upscale_options) + return result_image else: logger.info(f"No source face found for face number {source_face_idx}.") diff --git a/scripts/version.py b/scripts/version.py index 5ad1e16..0c385a6 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,5 +1,5 @@ app_title = "ReActor" -version_flag = "v0.3.0" +version_flag = "v0.3.1b" from scripts.logger import logger, get_Run, set_Run