fc-simple / app.py
ekhatskevich
deal with mask
3d23955
import cv2
import numpy as np
from PIL import Image
import os
import gradio as gr
from huggingface_hub import hf_hub_download, snapshot_download
def resolve_hf_path(path):
if isinstance(path, str) and path.startswith("hf://"):
parts = path[len("hf://"):].split("@")
if len(parts) == 1:
repo_id = parts[0]
filename = None
elif len(parts) == 2:
repo_id, filename = parts
else:
raise ValueError(f"Invalid HF URI format: {path}")
token = os.environ.get("HUGGINGFACE_HUB_TOKEN")
if token is None:
raise ValueError("HUGGINGFACE_HUB_TOKEN environment variable not set!")
# If filename is provided, download that file; otherwise, download the whole repo snapshot.
local_path = hf_hub_download(repo_id=repo_id, filename=filename, token=token) if filename else snapshot_download(repo_id=repo_id, token=token)
return local_path
return path
os.environ["FLUX_FILL_PATH"] = "hf://black-forest-labs/FLUX.1-Fill-dev"
os.environ["PORTRAIT_MODEL_PATH"] = "ms://iic/ACE_Plus@portrait/comfyui_portrait_lora64.safetensors"
os.environ["SUBJECT_MODEL_PATH"] = "ms://iic/ACE_Plus@subject/comfyui_subject_lora16.safetensors"
os.environ["LOCAL_MODEL_PATH"] = "ms://iic/ACE_Plus@local_editing/comfyui_local_lora16.safetensors"
os.environ["ACE_PLUS_FFT_MODEL"] = "hf://ali-vilab/ACE_Plus@ace_plus_fft.safetensors"
flux_full = resolve_hf_path(os.environ["FLUX_FILL_PATH"])
ace_plus_fft_model_path = resolve_hf_path(os.environ["ACE_PLUS_FFT_MODEL"])
# Update the environment variables with the resolved local file paths.
os.environ["ACE_PLUS_FFT_MODEL"] = ace_plus_fft_model_path
os.environ["FLUX_FILL_PATH"] = flux_full
from inference.ace_plus_inference import ACEInference
from scepter.modules.utils.config import Config
from modules.flux import FluxMRModiACEPlus
from inference.registry import INFERENCES
config_path = os.path.join("config", "ace_plus_fft.yaml")
cfg = Config(load=True, cfg_file=config_path)
# Instantiate the ACEInference object.
ace_infer = ACEInference(cfg)
def create_face_mask(pil_image):
"""
Create a binary mask (PIL Image) from a PIL image by detecting the face region.
The mask will be white (255) on the detected face area and black (0) elsewhere.
"""
try:
# Convert PIL image to a numpy array in RGB format
image_np = np.array(pil_image.convert("RGB"))
# Convert to grayscale for face detection
gray = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
# Load the Haar cascade for face detection (make sure opencv data is installed)
cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(cascade_path)
# Detect faces in the image
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
# Create an empty mask with the same dimensions as the image
mask = np.zeros_like(gray, dtype=np.uint8)
# For each detected face, draw a white rectangle (or a more refined shape)
for (x, y, w, h) in faces:
# Optionally expand the bounding box slightly
padding = 0.2
x1 = max(0, int(x - w * padding))
y1 = max(0, int(y - h * padding))
x2 = min(gray.shape[1], int(x + w * (1 + padding)))
y2 = min(gray.shape[0], int(y + h * (1 + padding)))
mask[y1:y2, x1:x2] = 255
return Image.fromarray(mask)
except Exception as e:
print(f"Error: {e}")
raise ValueError('A very specific bad thing happened.')
def face_swap_app(target_img, face_img):
if target_img is None or face_img is None:
raise ValueError("Both a target image and a face image must be provided.")
# (Optional) Ensure images are in RGB
target_img = target_img.convert("RGB")
face_img = face_img.convert("RGB")
edit_mask = create_face_mask(face_img)
output_img, edit_image, change_image, mask, seed = ace_infer(
reference_image=target_img,
edit_image=face_img,
edit_mask=edit_mask,
prompt="Face swap",
output_height=1024,
output_width=1024,
sampler='flow_euler',
sample_steps=28,
guide_scale=50,
seed=-1
)
return output_img
# Create the Gradio interface.
iface = gr.Interface(
fn=face_swap_app,
inputs=[
gr.Image(type="pil", label="Target Image"),
gr.Image(type="pil", label="Face Image")
],
outputs=gr.Image(type="pil", label="Swapped Face Output"),
title="ACE++ Face Swap Demo",
description="Upload a target image and a face image to swap the face using the ACE++ model."
)
if __name__ == "__main__":
iface.launch()