Spaces:
Sleeping
Sleeping
File size: 6,205 Bytes
68e4b96 |
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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
import numpy as np
import cv2
from PIL import Image
def detect_features(image):
"""
Detect facial and body features in the input image.
Args:
image (numpy.ndarray): Input image in numpy array format
Returns:
dict: Dictionary containing detected features and their coordinates
"""
# Convert to uint8 if the image is float
if image.dtype == np.float32 or image.dtype == np.float64:
image_uint8 = (image * 255).astype(np.uint8)
else:
image_uint8 = image
# Initialize feature dictionary
features = {
"Eyes": [],
"Nose": [],
"Lips": [],
"Face": [],
"Hair": [],
"Body": []
}
# Load pre-trained face detector
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
# Convert to grayscale for detection
gray = cv2.cvtColor(image_uint8, cv2.COLOR_RGB2GRAY)
# Detect faces
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
# Add face to features
features["Face"].append((x, y, w, h))
# Define regions of interest for other facial features
face_roi = gray[y:y+h, x:x+w]
# Detect eyes
eyes = eye_cascade.detectMultiScale(face_roi)
for (ex, ey, ew, eh) in eyes:
features["Eyes"].append((x+ex, y+ey, ew, eh))
# Approximate nose position (center of face)
nose_w = w // 4
nose_h = h // 4
nose_x = x + w//2 - nose_w//2
nose_y = y + h//2 - nose_h//2
features["Nose"].append((nose_x, nose_y, nose_w, nose_h))
# Approximate lips position (lower third of face)
lips_w = w // 2
lips_h = h // 6
lips_x = x + w//2 - lips_w//2
lips_y = y + 2*h//3
features["Lips"].append((lips_x, lips_y, lips_w, lips_h))
# Approximate hair region (top of face and above)
hair_w = w
hair_h = h // 2
hair_x = x
hair_y = max(0, y - hair_h // 2)
features["Hair"].append((hair_x, hair_y, hair_w, hair_h))
# If no faces detected, use whole image as body
if len(faces) == 0:
h, w = image.shape[:2]
features["Body"].append((0, 0, w, h))
else:
# Approximate body region (below face)
for (x, y, w, h) in faces:
body_w = w * 2
body_h = h * 3
body_x = max(0, x - w//2)
body_y = y + h
body_w = min(body_w, image.shape[1] - body_x)
body_h = min(body_h, image.shape[0] - body_y)
features["Body"].append((body_x, body_y, body_w, body_h))
return features
def create_mask(image, feature_type, features):
"""
Create a binary mask for the selected feature type.
Args:
image (numpy.ndarray): Input image
feature_type (str): Type of feature to mask
features (dict): Dictionary of detected features
Returns:
numpy.ndarray: Binary mask highlighting the selected feature
"""
# Create empty mask
mask = np.zeros(image.shape[:2], dtype=np.float32)
# Map feature_type to the corresponding key in features dictionary
if feature_type == "Face Shape":
feature_key = "Face"
elif feature_type in features:
feature_key = feature_type
else:
# Default to Face if feature type not found
feature_key = "Face"
# Draw filled rectangles for the selected feature
for (x, y, w, h) in features[feature_key]:
# Create a filled rectangle
cv2.rectangle(mask, (x, y), (x+w, y+h), 1.0, -1)
# Apply Gaussian blur to soften the mask edges
mask = cv2.GaussianBlur(mask, (21, 21), 0)
# Normalize mask to range [0, 1]
if mask.max() > 0:
mask = mask / mask.max()
return mask
def refine_mask_with_segmentation(image, mask, feature_type):
"""
Refine the initial mask using image segmentation for more precise feature isolation.
Args:
image (numpy.ndarray): Input image
mask (numpy.ndarray): Initial mask
feature_type (str): Type of feature to mask
Returns:
numpy.ndarray: Refined binary mask
"""
# Convert to uint8 if the image is float
if image.dtype == np.float32 or image.dtype == np.float64:
image_uint8 = (image * 255).astype(np.uint8)
else:
image_uint8 = image
# Create a masked region to focus segmentation
masked_region = image_uint8.copy()
for c in range(3):
masked_region[:, :, c] = masked_region[:, :, c] * mask
# Apply GrabCut algorithm for better segmentation
# Create initial mask for GrabCut
grabcut_mask = np.zeros(image.shape[:2], dtype=np.uint8)
# Areas with high mask values (>0.5) are definitely foreground
grabcut_mask[mask > 0.5] = cv2.GC_PR_FGD
# Areas with some mask values (>0.1) are probably foreground
grabcut_mask[(mask > 0.1) & (mask <= 0.5)] = cv2.GC_PR_FGD
# Rest is probably background
grabcut_mask[mask <= 0.1] = cv2.GC_PR_BGD
# Create temporary arrays for GrabCut
bgd_model = np.zeros((1, 65), np.float64)
fgd_model = np.zeros((1, 65), np.float64)
# Apply GrabCut
try:
cv2.grabCut(
image_uint8,
grabcut_mask,
None,
bgd_model,
fgd_model,
5,
cv2.GC_INIT_WITH_MASK
)
except:
# If GrabCut fails, return the original mask
return mask
# Create refined mask
refined_mask = np.zeros_like(mask)
refined_mask[grabcut_mask == cv2.GC_FGD] = 1.0
refined_mask[grabcut_mask == cv2.GC_PR_FGD] = 0.8
# Apply Gaussian blur to soften the mask edges
refined_mask = cv2.GaussianBlur(refined_mask, (15, 15), 0)
# Normalize mask to range [0, 1]
if refined_mask.max() > 0:
refined_mask = refined_mask / refined_mask.max()
return refined_mask
|