import gradio as gr import torch import dlib import numpy as np from PIL import Image from diffusers import UNet2DConditionModel as OriginalUNet2DConditionModel from pipeline_sd15 import StableDiffusionControlNetPipeline from diffusers import DPMSolverMultistepScheduler, ControlNetModel from detail_encoder.encoder_plus import detail_encoder from spiga_draw import * from spiga.inference.config import ModelConfig from spiga.inference.framework import SPIGAFramework from facelib import FaceDetector from modules import script_callbacks import os from huggingface_hub import snapshot_download torch.cuda.set_device(0) processor = SPIGAFramework(ModelConfig("300wpublic")) detector = FaceDetector(weight_path="./models/mobilenet0.25_Final.pth") def get_draw(pil_img, size): spigas = spiga_process(pil_img, detector) if not spigas: width, height = pil_img.size black_image_pil = Image.new('RGB', (width, height), color=(0, 0, 0)) return black_image_pil else: spigas_faces = spiga_segmentation(spigas, size=size) return spigas_faces #to download model model_id = "runwayml/stable-diffusion-v1-5" base_path = os.path.join(os.path.dirname(__file__), "..", "models") makeup_encoder_path = os.path.join(base_path, "pytorch_model.bin") id_encoder_path = os.path.join(base_path, "pytorch_model_1.bin") pose_encoder_path = os.path.join(base_path, "pytorch_model_2.bin") # URL for manual download manual_download_url = "https://huggingface.co/kigy1/Stable-Makeup" # Check if the file pytorch_model.bin exists if not os.path.isfile(makeup_encoder_path): try: # Try downloading the model snapshot_download(repo_id="kigy1/Stable-Makeup", local_dir=base_path) except Exception as e: print("First download attempt failed, trying alternative method.") try: # Try the alternative method snapshot_download(repo_id="kigy1/Stable-Makeup", local_dir=base_path, use_symlinks=False) except Exception as e: print("Both download attempts failed. Please download the model manually from the following URL put the three model inside sd-webui-Stable-Makeup\models:") print(manual_download_url) traceback.print_exc() else: print(f"The file {makeup_encoder_path} already exists. Skipping download.") Unet = OriginalUNet2DConditionModel.from_pretrained(model_id, subfolder="unet").to("cuda") id_encoder = ControlNetModel.from_unet(Unet) pose_encoder = ControlNetModel.from_unet(Unet) makeup_encoder = detail_encoder(Unet, "openai/clip-vit-large-patch14", "cuda", dtype=torch.float32) makeup_state_dict = torch.load(makeup_encoder_path) id_state_dict = torch.load(id_encoder_path) id_encoder.load_state_dict(id_state_dict, strict=False) pose_state_dict = torch.load(pose_encoder_path) pose_encoder.load_state_dict(pose_state_dict, strict=False) makeup_encoder.load_state_dict(makeup_state_dict, strict=False) pose_encoder.to("cuda") id_encoder.to("cuda") makeup_encoder.to("cuda") pipe = StableDiffusionControlNetPipeline.from_pretrained( model_id, safety_checker=None, unet=Unet, controlnet=[id_encoder, pose_encoder], torch_dtype=torch.float32).to("cuda") pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config) def model_call(id_image, makeup_image, num): try: detector = dlib.get_frontal_face_detector() image_array = np.array(id_image) face_locations = detector(image_array) if len(face_locations) > 0: top, right, bottom, left = face_locations[0].top(), face_locations[0].right(), face_locations[0].bottom(), face_locations[0].left() margin = 50 sstop = 50 top = max(0, top - margin) right = min(image_array.shape[1], right + margin) bottom = min(image_array.shape[0], bottom + margin) left = max(0, left - margin) ttop = 0 tright = 0 tbottom = 0 tleft = 0 while True: if (top <= 0) or (left <= 0) or (right >= image_array.shape[1]) or (bottom >= image_array.shape[0]): top = ttop right = tright bottom = tbottom left = tleft break else: margin = 20 sstop += 20 if sstop >= 200: break else: ttop = top tright = right tbottom = bottom tleft = left top = max(0, top - margin) right = min(image_array.shape[1], right + margin) bottom = min(image_array.shape[0], bottom + margin) left = max(0, left - margin) face_image_pil = Image.fromarray(id_image.astype('uint8'), 'RGB') head_image = face_image_pil.crop((left, top, right, bottom)) back_img_org = Image.fromarray(id_image.astype('uint8'), 'RGB') makeup_image_pil = Image.fromarray(makeup_image.astype('uint8'), 'RGB') head_image = head_image.resize((512, 512)) makeup_image_pil = makeup_image_pil.resize((512, 512)) back_img = back_img_org.resize((512, 512)) pose_image = get_draw(id_image, size=512) result_img = makeup_encoder.generate(id_image=[head_image, pose_image], makeup_image=makeup_image_pil, guidance_scale=num, pipe=pipe) backgr = makeup_encoder.generate(id_image=[back_img, pose_image], makeup_image=makeup_image_pil, guidance_scale=num, pipe=pipe) width, height = right - left, bottom - top head_image_original_size = result_img.resize((width, height)) finalsize = np.array(id_image) heightx, widthx, channelsx = finalsize.shape backgr_size = backgr.resize((widthx, heightx)) backgr_array = np.array(backgr_size) face_with_makeup = Image.fromarray(backgr_array.astype('uint8'), 'RGB') face_with_makeup.paste(head_image_original_size, (left, top)) return face_with_makeup except: id_image = Image.fromarray(id_image.astype('uint8'), 'RGB') org_size = id_image makeup_image = Image.fromarray(makeup_image.astype('uint8'), 'RGB') id_image = id_image.resize((512, 512)) makeup_image = makeup_image.resize((512, 512)) pose_image = get_draw(id_image, size=512) result_img = makeup_encoder.generate(id_image=[id_image, pose_image], makeup_image=makeup_image, guidance_scale=num, pipe=pipe) result_img = result_img.resize(org_size.size) return result_img css = ''' #face_image_upload, #makeup_image_upload, #output_image { height: 400px; max-height: 400px; max-width: 350px; } #face_image_upload [data-testid="image"], #face_image_upload [data-testid="image"] > div, #makeup_image_upload [data-testid="image"], #makeup_image_upload [data-testid="image"] > div, #output_image [data-testid="image"], #output_image [data-testid="image"] > div { height: 400px; max-height: 400px; max-width: 350px; } #slider_column { width: 20%; /* Adjust the width as needed */ } ''' def create_ui(): with gr.Blocks(css=css, analytics_enabled=False) as ui_component: with gr.Row(): with gr.Column(): with gr.Row(): image1 = gr.Image(label="Face Image", elem_id="face_image_upload").style(height=400) image2 = gr.Image(label="Makeup Image", elem_id="makeup_image_upload").style(height=400) with gr.Column(): number = gr.Slider(minimum=1.01, maximum=5, value=1.5, label="Makeup Guidance Scale") button = gr.Button("Run") with gr.Column(): output = gr.Image(type="pil", label="Output Image", elem_id="output_image").style(height=400) def process_images(id_image, makeup_image, num): return model_call(id_image, makeup_image, num) button.click(process_images, inputs=[image1, image2, number], outputs=output) ui_component.title = "Stable-Makeup" ui_component.description = "Upload 2 images to see the model output. 1.05-1.15 is suggested for light makeup and 2 for heavy makeup" return [(ui_component, "Stable-Makeup", "Stable-Makeup")] script_callbacks.on_ui_tabs(create_ui)