Spaces:
Running
Running
File size: 4,442 Bytes
170e54a 34e6e02 170e54a 34e6e02 170e54a 34e6e02 170e54a 34e6e02 170e54a 34e6e02 170e54a 34e6e02 ce4dbf8 34e6e02 170e54a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
import gradio as gr
from transformers import SegformerImageProcessor, AutoModelForSemanticSegmentation
from PIL import Image
import torch
import torch.nn.functional as F
import numpy as np
import mediapipe as mp
import cv2
processor = SegformerImageProcessor.from_pretrained("VanNguyen1214/get_face_and_hair")
model = AutoModelForSemanticSegmentation.from_pretrained("VanNguyen1214/get_face_and_hair")
# Hàm lấy mặt (bao mặt và trán) mà không bao gồm tóc
def get_facemesh_mask(image):
image_np = np.array(image)
height, width, _ = image_np.shape
face_mask = np.zeros((height, width), dtype=np.uint8)
mp_face_mesh = mp.solutions.face_mesh
with mp_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(image_np)
if results.multi_face_landmarks:
for face_landmarks in results.multi_face_landmarks:
points = []
for lm in face_landmarks.landmark:
x, y = int(lm.x * width), int(lm.y * height)
points.append([x, y])
points = np.array(points, np.int32)
if len(points) > 0:
hull = cv2.convexHull(points)
cv2.fillConvexPoly(face_mask, hull, 1)
return face_mask
# Hàm mở rộng vùng trán từ mask mặt đầu vào
def expand_forehead_mask(face_mask, expand_percent=0.2):
ys, xs = np.where(face_mask > 0)
if len(ys) == 0:
return face_mask # không tìm thấy mặt
min_y, max_y = ys.min(), ys.max()
height = max_y - min_y
expand = int(height * expand_percent)
expanded_min_y = max(min_y - expand, 0)
expanded_mask = np.zeros_like(face_mask)
src_start = min_y
src_end = max_y
dst_start = expanded_min_y
dst_end = expanded_min_y + (src_end - src_start)
if dst_end > face_mask.shape[0]:
overlap = dst_end - face_mask.shape[0]
dst_end = face_mask.shape[0]
src_end -= overlap
expanded_mask[dst_start:dst_end, :] = face_mask[src_start:src_end, :]
return expanded_mask
# Hàm chính: kết hợp mặt và trán mở rộng, không bao gồm tóc, lưu mask vào biến face_forehead_mask
face_forehead_mask = None
def extract_face_and_forehead_no_hair(image):
image = image.convert("RGB")
# SegFormer hair mask
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits.cpu()
upsampled_logits = F.interpolate(
logits,
size=image.size[::-1],
mode="bilinear",
align_corners=False,
)
pred_seg = upsampled_logits.argmax(dim=1)[0].numpy()
hair_mask = (pred_seg == 2).astype(np.uint8) # tóc
# Face mesh mask (bao trọn mặt, trán, không cổ)
face_mesh_mask = get_facemesh_mask(image)
# Expand lên trên 20% chiều cao mặt (ăn gian trán)
expanded_face_mask = expand_forehead_mask(face_mesh_mask, expand_percent=0.2)
# Vùng trán mở rộng chỉ lấy phần không trùng vùng mặt gốc và không trùng tóc
expanded_only_forehead = cv2.bitwise_and(expanded_face_mask, 1 - face_mesh_mask)
expanded_only_forehead = cv2.bitwise_and(expanded_only_forehead, 1 - hair_mask)
# Kết hợp: tóc + mặt mediapipe (gốc) + vùng trán mở rộng (phía trên mặt gốc, không trùng tóc, không trùng mặt gốc)
combined_mask = ((face_mesh_mask + expanded_only_forehead) > 0).astype(np.uint8)
# Làm mượt mask
combined_mask = cv2.GaussianBlur(combined_mask.astype(np.float32), (3, 3), 0)
combined_mask = (combined_mask > 0.5).astype(np.uint8)
np_image = np.array(image)
alpha = (combined_mask * 255).astype(np.uint8)
rgba_image = np.dstack([np_image, alpha])
return Image.fromarray(rgba_image)
iface = gr.Interface(
fn=extract_face_and_forehead_no_hair,
inputs=gr.Image(type="pil"),
outputs=gr.Image(type="numpy", label="Hair, Face & Full Forehead PNG"),
live=False,
title="Tách tóc, khuôn mặt và toàn bộ trán (ăn gian, không lấy cổ, không chồng lên tóc)",
description="Upload ảnh chân dung để nhận file PNG gồm tóc, mặt và trán mở rộng (không lấy cổ, không bị thiếu trán, không lấy vùng tóc vào vùng trán)."
)
iface.launch() |