import cv2 import numpy as np import csv def normalize8(I): mn = I.min() mx = I.max() mx -= mn I = ((I - mn) / mx) * 255 return I.astype(np.uint8) def overlay_transparent(background, overlay, x, y): background_width = background.shape[1] background_height = background.shape[0] if x >= background_width or y >= background_height: return background h, w = overlay.shape[0], overlay.shape[1] if x + w > background_width: w = background_width - x overlay = overlay[:, :w] if y + h > background_height: h = background_height - y overlay = overlay[:h] if overlay.shape[2] < 4: overlay = np.concatenate( [ overlay, np.ones((overlay.shape[0], overlay.shape[1], 1), dtype=overlay.dtype) * 255, ], axis=2, ) overlay_image = overlay[..., :3] mask = overlay[..., 3:] / 255.0 background[y : y + h, x : x + w] = (1.0 - mask) * background[ y : y + h, x : x + w ] + mask * overlay_image return background def get_mask_points(mask_name): if mask_name == "Front Man Mask": mask_path = "./assets/front_man_mask.png" csv_path = "./assets/front_man_mask.csv" elif mask_name == "Guards Mask": mask_path = "./assets/guards_mask.png" csv_path = "./assets/guards_mask.csv" elif mask_name == "Red Mask": mask_path = "./assets/redmask.png" csv_path = "./assets/red_mask.csv" elif mask_name == "Blue Mask": mask_path = "./assets/bluemask.png" csv_path = "./assets/blue_mask.csv" else: raise ValueError(f"❌ Unknown mask name: {mask_name}") mask_img = cv2.imread(mask_path, cv2.IMREAD_UNCHANGED) if mask_img is None: raise FileNotFoundError(f"❌ Could not load mask image: {mask_path}") mask_img = mask_img.astype(np.float32) / 255.0 mask_points = {} with open(csv_path) as csv_file: csv_reader = csv.reader(csv_file, delimiter=",") for i, row in enumerate(csv_reader): mask_points[int(row[0])] = [float(row[1]), float(row[2])] return mask_points, mask_img def mask_overlay(image, faces, mask_up, mask_down, mask_name): mask_points, mask_img = get_mask_points(mask_name) mirror_point = { 234: 1, 93: 2, 132: 3, 58: 4, 172: 5, 136: 6, 150: 7, 149: 8, 176: 9, 148: 10, 152: 11, 377: 12, 400: 13, 378: 14, 379: 15, 365: 16, 397: 17, 288: 18, 361: 19, 323: 20, 454: 21, 356: 22, 389: 23, 251: 24, 284: 25, 332: 26, 297: 27, 338: 28, 10: 29, 109: 30, 67: 31, 103: 32, 54: 33, 21: 34, 162: 35, 127: 36, } mask_points = mask_points src_pts = [] for i in sorted(mask_points.keys()): try: src_pts.append(np.array(mask_points[i])) except ValueError: continue src_pts = np.array(src_pts, dtype="float32") extend_y = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ] minimize_y = [22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36] face_points = {} for i in faces[0]: for j in mirror_point.keys(): if i[0] == j: if mirror_point[i[0]] in minimize_y: face_points[mirror_point[j]] = [ float(i[1]), float(i[2] - int(mask_up)), ] else: if mirror_point[i[0]] in extend_y: face_points[mirror_point[j]] = [ float(i[1]), float(i[2] + int(mask_down)), ] else: face_points[mirror_point[j]] = [float(i[1]), float(i[2])] dst_pts = [] for i in sorted(face_points.keys()): try: dst_pts.append(np.array(face_points[i])) except ValueError: continue dst_pts = np.array(dst_pts, dtype="float32") M, _ = cv2.findHomography(src_pts, dst_pts) transformed_mask = cv2.warpPerspective( mask_img, M, (image.shape[1], image.shape[0]), None, cv2.INTER_LINEAR, cv2.BORDER_CONSTANT, borderValue=[0, 0, 0, 0], ) png_image = normalize8(transformed_mask) new_image = overlay_transparent(image, png_image, 0, 0) # cv2.imwrite("output.png", new_image) return new_image