ghep_image / overlay.py
VanNguyen1214's picture
Update overlay.py
390e44b verified
import numpy as np
from PIL import Image
import mediapipe as mp
from baldhead import inference # cạo tóc background
from segmentation import extract_hair
# MediaPipe Face Detection
mp_fd = mp.solutions.face_detection.FaceDetection(model_selection=1,
min_detection_confidence=0.5)
def get_face_bbox(img: Image.Image) -> tuple[int,int,int,int] | None:
arr = np.array(img.convert("RGB"))
res = mp_fd.process(arr)
if not res.detections:
return None
d = res.detections[0].location_data.relative_bounding_box
h, w = arr.shape[:2]
x1 = int(d.xmin * w)
y1 = int(d.ymin * h)
x2 = x1 + int(d.width * w)
y2 = y1 + int(d.height * h)
return x1, y1, x2, y2
def compute_scale(w_bg, h_bg, w_src, h_src) -> float:
return ((w_bg / w_src) + (h_bg / h_src)) / 2
def compute_offset(bbox_bg, bbox_src, scale) -> tuple[int,int]:
x1, y1, x2, y2 = bbox_bg
bg_cx = x1 + (x2 - x1)//2
bg_cy = y1 + (y2 - y1)//2
sx1, sy1, sx2, sy2 = bbox_src
src_cx = int((sx1 + (sx2 - sx1)//2) * scale)
src_cy = int((sy1 + (sy2 - sy1)//2) * scale)
return bg_cx - src_cx, bg_cy - src_cy
def paste_with_alpha(bg: np.ndarray, src: np.ndarray, offset: tuple[int,int]) -> Image.Image:
res = bg.copy()
x, y = offset
h, w = src.shape[:2]
x1, y1 = max(x,0), max(y,0)
x2 = min(x+w, bg.shape[1])
y2 = min(y+h, bg.shape[0])
if x1>=x2 or y1>=y2:
return Image.fromarray(res)
cs = src[y1-y:y2-y, x1-x:x2-x]
cd = res[y1:y2, x1:x2]
mask = cs[...,3] > 0
if cd.shape[2] == 3:
cd[mask] = cs[mask][...,:3]
else:
cd[mask] = cs[mask]
res[y1:y2, x1:x2] = cd
return Image.fromarray(res)
def overlay_source(background: Image.Image, source: Image.Image):
# 1) detect bboxes
bbox_bg = get_face_bbox(background)
bbox_src = get_face_bbox(source)
if bbox_bg is None:
return None, "❌ No face in background."
if bbox_src is None:
return None, "❌ No face in source."
# 2) compute scale & resize source
w_bg, h_bg = bbox_bg[2]-bbox_bg[0], bbox_bg[3]-bbox_bg[1]
w_src, h_src = bbox_src[2]-bbox_src[0], bbox_src[3]-bbox_src[1]
scale = compute_scale(w_bg, h_bg, w_src, h_src)
src_scaled = source.resize(
(int(source.width*scale), int(source.height*scale)),
Image.Resampling.LANCZOS
)
# 3) compute offset
offset = compute_offset(bbox_bg, bbox_src, scale)
# 4) baldhead background
bg_bald = inference(background)
# 5) extract hair-only from source
hair_only = extract_hair(src_scaled)
# 6) paste onto bald background
result = paste_with_alpha(
np.array(bg_bald.convert("RGBA")),
np.array(hair_only),
offset
)
return result