Spaces:
Running
Running
import cv2 | |
import numpy as np | |
def _read_png_rgba(path): | |
png = cv2.imread(path, cv2.IMREAD_UNCHANGED) | |
if png is None or png.shape[2] != 4: | |
raise ValueError("Hairstyle PNG must be RGBA with transparency.") | |
return png | |
def auto_align(png_rgba, mask, landmarks=None): | |
mh, mw = mask.shape[:2] | |
ys, xs = np.where(mask > 0) | |
if len(xs) == 0 or len(ys) == 0: | |
return cv2.resize(png_rgba, (mw, mh)) | |
x0, x1 = xs.min(), xs.max() | |
y0, y1 = ys.min(), ys.max() | |
tw, th = int((x1 - x0) * 1.1), int((y1 - y0) * 0.7) | |
tw = max(1, min(tw, mw)) | |
th = max(1, min(th, mh)) | |
aligned = cv2.resize(png_rgba, (tw, th)) | |
canvas = np.zeros((mh, mw, 4), dtype=np.uint8) | |
y = max(0, y0 - int(0.25 * th)) | |
x = max(0, x0 - int(0.05 * tw)) | |
y2, x2 = min(mh, y + th), min(mw, x + tw) | |
canvas[y:y2, x:x2] = aligned[:(y2 - y), :(x2 - x)] | |
return canvas | |
def _alpha_blend(base_bgr, overlay_rgba): | |
bgr = base_bgr.copy() | |
alpha = overlay_rgba[:, :, 3:4] / 255.0 | |
rgb = overlay_rgba[:, :, :3] | |
return (alpha * rgb + (1 - alpha) * bgr).astype(np.uint8) | |
def apply_hairstyle(img_bgr, style_path, mask, landmarks=None): | |
png = _read_png_rgba(style_path) | |
aligned = auto_align(png, mask, landmarks) | |
return _alpha_blend(img_bgr, aligned) | |