Spaces:
Running
Running
from PIL import Image, ImageOps, ImageEnhance | |
import mediapipe as mp | |
import numpy as np | |
import cv2 | |
import gradio as gr | |
import imageio | |
class FaceSwapper: | |
def __init__(self): | |
self.landmarks_a = None | |
self.landmarks_b = None | |
self.image_a = None | |
self.image_b = None | |
self.image_a_origin = None | |
self.image_b_origin = None | |
def get_face_landmarks(self, image): | |
with mp.solutions.face_mesh.FaceMesh( | |
static_image_mode=True, | |
max_num_faces=1, | |
refine_landmarks=True, | |
min_detection_confidence=0.5 | |
) as face_mesh: | |
results = face_mesh.process(cv2.cvtColor(np.array(image), cv2.COLOR_BGR2RGB)) | |
if not results.multi_face_landmarks: | |
return None | |
landmarks = results.multi_face_landmarks[0].landmark | |
w, h = image.size | |
face_landmarks = [(int(p.x * w), int(p.y * h)) for p in landmarks] | |
return face_landmarks | |
def load_images(self, image_a, image_b): | |
if image_a: | |
if image_a.convert("RGBA") != self.image_a_origin: | |
self.image_a = image_a.convert("RGBA") | |
self.image_a_origin = image_a.convert("RGBA") | |
self.landmarks_a = self.get_face_landmarks(self.image_a) | |
if image_b: | |
if image_b.convert("RGBA") != self.image_a_origin: | |
self.image_b = image_b.convert("RGBA") | |
self.image_b_origin = image_b.convert("RGBA") | |
self.landmarks_b = self.get_face_landmarks(self.image_b) | |
if not self.landmarks_a or not self.landmarks_b: | |
return None | |
def overlay_image(self, background, overlay, position): | |
x, y = position | |
if background.mode != 'RGBA': | |
background = background.convert('RGBA') | |
if overlay.mode != 'RGBA': | |
overlay = overlay.convert('RGBA') | |
overlay_mask = overlay.split()[3] | |
background.paste(overlay, (x, y), overlay_mask) | |
return background | |
def swap_faces(self, scale_factor=1.0, offset_x=0, offset_y=0, rotation_angle=0, margin_left=0.1, margin_right=0.1, | |
margin_top=0.1, margin_bottom=0.1, mirror_face_a=False): | |
if not self.landmarks_a or not self.image_a or not self.image_b: | |
raise ValueError("Image Load Error Or Face Not Deteceted") | |
min_x_a = min([p[0] for p in self.landmarks_a]) | |
max_x_a = max([p[0] for p in self.landmarks_a]) | |
min_y_a = min([p[1] for p in self.landmarks_a]) | |
max_y_a = max([p[1] for p in self.landmarks_a]) | |
width_a = max_x_a - min_x_a | |
height_a = max_y_a - min_y_a | |
face_a = self.image_a.crop(( | |
int(min_x_a - width_a * margin_left), | |
int(min_y_a - height_a * 0.85 * margin_top), | |
int(max_x_a + width_a * margin_right), | |
int(max_y_a + height_a * margin_bottom) | |
)) | |
if mirror_face_a: | |
face_a = ImageOps.mirror(face_a) | |
if not self.landmarks_b: | |
width_b, height_b = self.image_b.size | |
width_a, height_a = self.image_a.size | |
center_x_b = width_b // 2 | |
center_y_b = height_b // 5 | |
scale_w = width_b / width_a * scale_factor | |
scale_h = height_b / height_a * scale_factor | |
else: | |
min_x_b = min([p[0] for p in self.landmarks_b]) | |
max_x_b = max([p[0] for p in self.landmarks_b]) | |
min_y_b = min([p[1] for p in self.landmarks_b]) | |
max_y_b = max([p[1] for p in self.landmarks_b]) | |
width_b = max_x_b - min_x_b | |
height_b = max_y_b - min_y_b | |
center_x_b = min_x_b + width_b // 2 | |
center_y_b = min_y_b + height_b // 2 | |
scale_w = width_b / face_a.width * 1.4 * scale_factor | |
scale_h = height_b / face_a.height * 1.4 * scale_factor | |
scale = min(scale_w, scale_h) | |
new_width = int(face_a.width * scale) | |
new_height = int(face_a.height * scale) | |
face_a_resized = face_a.resize((new_width, new_height), Image.LANCZOS) | |
face_a_rotated = face_a_resized.rotate(rotation_angle, expand=True) | |
final_offset_x = center_x_b - face_a_rotated.width // 2 + offset_x | |
final_offset_y = center_y_b - face_a_rotated.height // 2 + offset_y | |
result_image = self.overlay_image(self.image_b.copy(), face_a_rotated, (final_offset_x, final_offset_y)) | |
return result_image | |
def adjust_colors(self, is_select_image_a, saturation, temperature, contrast, brightness): | |
image = self.image_a_origin if is_select_image_a else self.image_b_origin | |
if not image: | |
raise ValueError("Selected image is not loaded.") | |
# Convert to RGB if necessary | |
if image.mode != 'RGB': | |
image = image.convert('RGB') | |
# Adjust brightness | |
enhancer = ImageEnhance.Brightness(image) | |
image = enhancer.enhance(brightness) | |
# Adjust contrast | |
enhancer = ImageEnhance.Contrast(image) | |
image = enhancer.enhance(contrast) | |
# Adjust saturation | |
enhancer = ImageEnhance.Color(image) | |
image = enhancer.enhance(saturation) | |
# Adjust temperature by modifying color balance | |
r, g, b = image.split() # Handling three channels only | |
r = r.point(lambda i: i + temperature * 10) | |
b = b.point(lambda i: i - temperature * 10) | |
image = Image.merge("RGB", (r, g, b)) # Re-merge as RGB | |
if is_select_image_a: | |
self.image_a = image | |
else: | |
self.image_b = image | |
return image | |
# Function to swap faces | |
def process(image_a, image_b, scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, margin_top, | |
margin_bottom, mirror_face_a): | |
try: | |
face_swapper = FaceSwapper() | |
face_swapper.load_images(image_a, image_b) | |
if face_swapper.image_a and face_swapper.image_b: | |
result = face_swapper.swap_faces( | |
scale_factor=scale_factor, | |
offset_x=offset_x, | |
offset_y=offset_y, | |
rotation_angle=rotation_angle, | |
margin_left=margin_left, | |
margin_right=margin_right, | |
margin_top=margin_top, | |
margin_bottom=margin_bottom, | |
mirror_face_a=mirror_face_a | |
) | |
return result | |
except Exception as e: | |
return str(e) | |
def adujst_color_process(image_a, image_b, scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, margin_top, | |
margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b): | |
try: | |
face_swapper = FaceSwapper() | |
face_swapper.load_images(image_a, image_b) | |
face_swapper.adjust_colors(True, saturation, temperature, contrast, brightness) | |
face_swapper.adjust_colors(False, saturation_b, temperature_b, contrast_b, brightness_b) | |
if face_swapper.image_a and face_swapper.image_b: | |
result = face_swapper.swap_faces( | |
scale_factor=scale_factor, | |
offset_x=offset_x, | |
offset_y=offset_y, | |
rotation_angle=rotation_angle, | |
margin_left=margin_left, | |
margin_right=margin_right, | |
margin_top=margin_top, | |
margin_bottom=margin_bottom, | |
mirror_face_a=mirror_face_a | |
) | |
return result | |
except Exception as e: | |
return str(e) | |
# Function for image masking (demonstrative) | |
def dummy(img): | |
# Assuming img is a dictionary with keys 'composite', 'background', and 'layers' | |
composite_image = img["composite"] | |
imageio.imwrite("output_image.png", composite_image) | |
alpha_channel = img["layers"][0][:, :, 3] | |
mask = np.where(alpha_channel == 0, 0, 255).astype(np.uint8) | |
return mask | |
# Function to update the image in Tab 1 | |
def update_image(image): | |
return image | |
# Announcement text for tab 1 | |
announcement = """ | |
## BRAUNDRESS ENTRY: | |
BRAUNDRESS 入口: [Click Here](https://braundress.me/entry) | |
## Backup Inpaint Mask Maker: | |
备用蒙版工具: [Visit Here](https://huggingface.co/spaces/BraUndress/inpaint-mask-maker2) | |
## How to Use: | |
使用方法: [Learn More](https://telegra.ph/HowToUploadMaskOnBraundress-05-01) | |
Source: BraUndress | |
""" | |
# Announcement text for tab 2 | |
announcement_2 = """ | |
## Backup Inpaint Mask Maker: | |
备用蒙版工具: [Visit Here](https://huggingface.co/spaces/BraUndress/inpaint-mask-maker2) | |
## How to Use: | |
使用方法: [Learn More](https://telegra.ph/100-Similarity-Face-Swapped-with-Braundress-Mask-Upload-Mode-07-23) | |
Source: BraUndress | |
""" | |
# Create Gradio interface with Tabs | |
with gr.Blocks() as app: | |
tab1 = gr.Tab("Inpaint Mask Maker") | |
tab2 = gr.Tab("Face Swap") | |
with tab1: | |
with gr.Row(): | |
with gr.Column(): | |
announcement = gr.Markdown(announcement) | |
with gr.Column(): | |
with gr.Row(): | |
img = gr.ImageMask( | |
sources=["upload", "clipboard"], | |
transforms=[], | |
layers=False, | |
format="png", | |
label="base image", | |
show_label=True | |
) | |
img2 = gr.Image(label="mask image", show_label=True, format="png") | |
btn = gr.Button("Generate Mask") | |
btn.click(dummy, inputs=img, outputs=img2) | |
with tab2: | |
with gr.Row(): | |
with gr.Column(): | |
announcement_2 = gr.Markdown(announcement_2) | |
with gr.Row(): | |
img_input_a = gr.Image(type="pil", label="Input Face Image A(输入人脸图片)", height=300) | |
img_input_b = gr.Image(type="pil", label="Input Face Swapped Image B(输入换脸图片)", height=300) | |
with gr.Row(): | |
scale_factor = gr.Slider(0.1, 3.5, 1, 0.1, label="Face Scale Factor(人脸放大)") | |
offset_x = gr.Slider(-400, 400, 0, 1, label="Face Offset X(人脸水平横移)") | |
offset_y = gr.Slider(-400, 400, 0, 1, label="Face Offset Y(人脸垂直横移)") | |
rotation_angle = gr.Slider(-180, 180, 0, 1, label="Face Rotation Angle(人脸旋转)") | |
margin_left = gr.Slider(0, 1, 0.1, 0.01, label="Face Margin Left(人脸左边缘扩展)") | |
margin_right = gr.Slider(0, 1, 0.1, 0.01, label="Face Margin Right(人脸右边缘扩展)") | |
margin_top = gr.Slider(0, 1, 0.1, 0.01, label="Face Margin Top(人脸上边缘扩展)") | |
margin_bottom = gr.Slider(0, 1, 0, 0.01, label="Face Margin Bottom(人脸下边缘扩展)") | |
mirror_face_a = gr.Checkbox(label="Mirror Face A(人脸镜像)") | |
# btn_process = gr.Button("Swap Faces") | |
with gr.Row(): | |
image_select = gr.Radio(['Image A'], value='Image A', label="Adjust Color For Image A(图片A调整颜色)") | |
saturation = gr.Slider(0, 2.0, 1.0, 0.1, label="Saturation(饱和度)") | |
temperature = gr.Slider(-10, 10, 0, 0.1, label="Color Temperature(色温)") | |
contrast = gr.Slider(0, 2.0, 1.0, 0.1, label="Contrast(对比度)") | |
brightness = gr.Slider(0, 2.0, 1.0, 0.1, label="Brightness(亮度)") | |
with gr.Row(): | |
image_select_b = gr.Radio(['Image B'], value='Image B', label="Adjust Color For Image B(图片B调整颜色)") | |
saturation_b = gr.Slider(0, 2.0, 1.0, 0.1, label="Saturation(饱和度)") | |
temperature_b = gr.Slider(-10, 10, 0, 0.1, label="Color Temperature(色温)") | |
contrast_b = gr.Slider(0, 2.0, 1.0, 0.1, label="Contrast(对比度)") | |
brightness_b = gr.Slider(0, 2.0, 1.0, 0.1, label="Brightness(亮度)") | |
result_image = gr.Image(type="pil", label="Result Image", height=600) | |
# btn_process.click(process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, margin_top, margin_bottom, mirror_face_a], outputs=result_image) | |
scale_factor.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
offset_x.release(adujst_color_process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
offset_y.release(adujst_color_process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
rotation_angle.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
margin_left.release(adujst_color_process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
margin_right.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
margin_top.release(adujst_color_process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
margin_bottom.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
img_input_a.change(adujst_color_process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
img_input_b.change(adujst_color_process, inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
mirror_face_a.change(adujst_color_process, | |
inputs=[img_input_a, img_input_b, scale_factor, offset_x, offset_y, rotation_angle, | |
margin_left, margin_right, margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], | |
outputs=result_image) | |
saturation.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
temperature.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
contrast.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
brightness.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
saturation_b.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
temperature_b.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
contrast_b.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
brightness_b.release(adujst_color_process, | |
inputs=[img_input_a, img_input_b,scale_factor, offset_x, offset_y, rotation_angle, margin_left, margin_right, | |
margin_top, margin_bottom, mirror_face_a, saturation, temperature, contrast, brightness, saturation_b, temperature_b, contrast_b, brightness_b], outputs=result_image) | |
btn_send_to_tab1 = gr.Button("Send To Mask Maker(发送给蒙版制作页面)") | |
btn_send_to_tab1.click(update_image, inputs=result_image, outputs=img) | |
app.launch() |