Spaces:
Configuration error
Configuration error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,187 +1,34 @@
|
|
| 1 |
import gradio as gr
|
|
|
|
| 2 |
import cv2
|
| 3 |
import numpy as np
|
| 4 |
-
import
|
| 5 |
-
from sklearn.
|
| 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 |
-
mask = np.zeros_like(image, dtype=np.uint8)
|
| 32 |
-
|
| 33 |
-
color_dict = {
|
| 34 |
-
"fair": (255, 182, 193),
|
| 35 |
-
"medium": (0, 191, 255),
|
| 36 |
-
"dark": (138, 43, 226)
|
| 37 |
-
}
|
| 38 |
-
color = color_dict.get(skin_tone, (255, 255, 255))
|
| 39 |
-
|
| 40 |
-
if face_shape == "oval":
|
| 41 |
-
center = (x + w // 2, y + h // 2)
|
| 42 |
-
axes = (w // 2, h // 2)
|
| 43 |
-
cv2.ellipse(mask, center, axes, 0, 0, 360, color, -1)
|
| 44 |
-
elif face_shape == "round":
|
| 45 |
-
radius = min(w, h) // 2
|
| 46 |
-
center = (x + w // 2, y + h // 2)
|
| 47 |
-
cv2.circle(mask, center, radius, color, -1)
|
| 48 |
-
elif face_shape == "square":
|
| 49 |
-
cv2.rectangle(mask, (x, y), (x + w, y + h), color, -1)
|
| 50 |
-
else:
|
| 51 |
-
cv2.rectangle(mask, (x, y), (x + w, y + h), color, -1)
|
| 52 |
-
|
| 53 |
-
# Get mask style
|
| 54 |
-
style = recommend_mask_style(face_shape, skin_tone)
|
| 55 |
-
|
| 56 |
-
# Blend mask overlay
|
| 57 |
-
alpha = 0.4
|
| 58 |
-
blended = cv2.addWeighted(mask, alpha, image, 1 - alpha, 0)
|
| 59 |
-
|
| 60 |
-
# Add style label
|
| 61 |
-
label_text = f"{face_shape}, {skin_tone}, {style}"
|
| 62 |
-
cv2.putText(blended, label_text, (x, y - 10),
|
| 63 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
|
| 64 |
-
|
| 65 |
-
return blended
|
| 66 |
-
|
| 67 |
-
# Initialize MediaPipe modules
|
| 68 |
-
mp_face_detection = mp.solutions.face_detection
|
| 69 |
-
mp_face_mesh = mp.solutions.face_mesh
|
| 70 |
-
|
| 71 |
-
face_detector = mp_face_detection.FaceDetection(model_selection=0, min_detection_confidence=0.6)
|
| 72 |
-
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True)
|
| 73 |
-
|
| 74 |
-
def detect_face_shape(landmarks, image_width, image_height):
|
| 75 |
-
# Extract specific landmarks
|
| 76 |
-
jaw_left = landmarks[234]
|
| 77 |
-
jaw_right = landmarks[454]
|
| 78 |
-
chin = landmarks[152]
|
| 79 |
-
forehead = landmarks[10]
|
| 80 |
-
|
| 81 |
-
x1 = int(jaw_left.x * image_width)
|
| 82 |
-
x2 = int(jaw_right.x * image_width)
|
| 83 |
-
y1 = int(chin.y * image_height)
|
| 84 |
-
y2 = int(forehead.y * image_height)
|
| 85 |
-
|
| 86 |
-
face_width = abs(x2 - x1)
|
| 87 |
-
face_height = abs(y1 - y2)
|
| 88 |
-
|
| 89 |
-
ratio = face_width / face_height if face_height != 0 else 0
|
| 90 |
-
|
| 91 |
-
if ratio > 1.05:
|
| 92 |
-
return "round"
|
| 93 |
-
elif 0.95 < ratio <= 1.05:
|
| 94 |
-
return "square"
|
| 95 |
-
else:
|
| 96 |
-
return "oval"
|
| 97 |
-
|
| 98 |
-
def detect_skin_tone(image, x, y, w, h):
|
| 99 |
-
roi = image[y:y+h, x:x+w]
|
| 100 |
-
roi_rgb = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB)
|
| 101 |
-
roi_flat = roi_rgb.reshape((-1, 3))
|
| 102 |
-
|
| 103 |
-
kmeans = KMeans(n_clusters=3, n_init=10)
|
| 104 |
-
kmeans.fit(roi_flat)
|
| 105 |
-
avg_color = kmeans.cluster_centers_[0]
|
| 106 |
-
brightness = np.mean(avg_color)
|
| 107 |
-
|
| 108 |
-
if brightness > 200:
|
| 109 |
-
return "fair"
|
| 110 |
-
elif brightness > 100:
|
| 111 |
-
return "medium"
|
| 112 |
-
else:
|
| 113 |
-
return "dark"
|
| 114 |
-
|
| 115 |
-
def overlay_mask(image, face_shape, skin_tone, x, y, w, h):
|
| 116 |
-
overlay = image.copy()
|
| 117 |
-
mask = np.zeros_like(image, dtype=np.uint8)
|
| 118 |
-
|
| 119 |
-
color_dict = {
|
| 120 |
-
"fair": (255, 182, 193), # light pink
|
| 121 |
-
"medium": (0, 191, 255), # deep sky blue
|
| 122 |
-
"dark": (138, 43, 226) # blue violet
|
| 123 |
-
}
|
| 124 |
-
color = color_dict.get(skin_tone, (255, 255, 255))
|
| 125 |
-
|
| 126 |
-
if face_shape == "oval":
|
| 127 |
-
center = (x + w // 2, y + h // 2)
|
| 128 |
-
axes = (w // 2, h // 2)
|
| 129 |
-
cv2.ellipse(mask, center, axes, 0, 0, 360, color, -1)
|
| 130 |
-
elif face_shape == "round":
|
| 131 |
-
radius = min(w, h) // 2
|
| 132 |
-
center = (x + w // 2, y + h // 2)
|
| 133 |
-
cv2.circle(mask, center, radius, color, -1)
|
| 134 |
-
elif face_shape == "square":
|
| 135 |
-
cv2.rectangle(mask, (x, y), (x + w, y + h), color, -1)
|
| 136 |
-
else:
|
| 137 |
-
cv2.rectangle(mask, (x, y), (x + w, y + h), color, -1)
|
| 138 |
-
|
| 139 |
-
alpha = 0.4
|
| 140 |
-
blended = cv2.addWeighted(mask, alpha, image, 1 - alpha, 0)
|
| 141 |
-
|
| 142 |
-
cv2.putText(blended, f"{face_shape}, {skin_tone}", (x, y - 10),
|
| 143 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
|
| 144 |
-
|
| 145 |
-
return blended
|
| 146 |
-
|
| 147 |
-
def process_image(image):
|
| 148 |
-
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
|
| 149 |
-
ih, iw, _ = image.shape
|
| 150 |
-
|
| 151 |
-
results = face_detector.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
| 152 |
-
if not results.detections:
|
| 153 |
-
return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
| 154 |
-
|
| 155 |
-
for detection in results.detections:
|
| 156 |
-
bboxC = detection.location_data.relative_bounding_box
|
| 157 |
-
x = int(bboxC.xmin * iw)
|
| 158 |
-
y = int(bboxC.ymin * ih)
|
| 159 |
-
w = int(bboxC.width * iw)
|
| 160 |
-
h = int(bboxC.height * ih)
|
| 161 |
-
x, y = max(x, 0), max(y, 0)
|
| 162 |
-
|
| 163 |
-
# Detect mesh
|
| 164 |
-
results_mesh = face_mesh.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
| 165 |
-
if results_mesh.multi_face_landmarks:
|
| 166 |
-
landmarks = results_mesh.multi_face_landmarks[0].landmark
|
| 167 |
-
face_shape = detect_face_shape(landmarks, iw, ih)
|
| 168 |
-
else:
|
| 169 |
-
face_shape = "oval"
|
| 170 |
-
|
| 171 |
-
skin_tone = detect_skin_tone(image, x, y, w, h)
|
| 172 |
-
image = overlay_mask(image, face_shape, skin_tone, x, y, w, h)
|
| 173 |
-
|
| 174 |
-
return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
| 175 |
-
|
| 176 |
-
# Gradio UI
|
| 177 |
-
demo = gr.Interface(
|
| 178 |
-
fn=process_image,
|
| 179 |
-
inputs=gr.Image(type="numpy", label="Upload or Snap Image"),
|
| 180 |
-
outputs=gr.Image(label="Face Shape + Skin Tone + Mask Overlay"),
|
| 181 |
-
live=True,
|
| 182 |
-
title="Face Shape & Skin Tone Analyzer",
|
| 183 |
-
description="This app detects face shape & skin tone and overlays a dynamic mask using OpenCV."
|
| 184 |
)
|
| 185 |
|
| 186 |
-
|
| 187 |
-
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import tensorflow as tf
|
| 3 |
import cv2
|
| 4 |
import numpy as np
|
| 5 |
+
import pandas as pd
|
| 6 |
+
from sklearn.preprocessing import LabelEncoder
|
| 7 |
+
|
| 8 |
+
# Load trained model
|
| 9 |
+
model = tf.keras.models.load_model("cnn_mask_model.h5")
|
| 10 |
+
|
| 11 |
+
# Load dataset & encode mask types
|
| 12 |
+
df = pd.read_excel("mask_dataset.xlsx")
|
| 13 |
+
label_encoder = LabelEncoder()
|
| 14 |
+
df["mask_type"] = label_encoder.fit_transform(df["mask_type"])
|
| 15 |
+
|
| 16 |
+
# Define function for prediction
|
| 17 |
+
def predict_mask(image):
|
| 18 |
+
img_resized = cv2.resize(image, (128, 128)).reshape(1, 128, 128, 3) / 255.0
|
| 19 |
+
mask_pred = model.predict(img_resized).argmax()
|
| 20 |
+
|
| 21 |
+
mask_name = label_encoder.inverse_transform([mask_pred])[0] # Convert label back
|
| 22 |
+
|
| 23 |
+
return f"Recommended Mask: {mask_name}"
|
| 24 |
+
|
| 25 |
+
# Gradio interface for Hugging Face Spaces
|
| 26 |
+
iface = gr.Interface(
|
| 27 |
+
fn=predict_mask,
|
| 28 |
+
inputs=gr.Image(type="numpy", label="Upload Your Face Image"),
|
| 29 |
+
outputs="text",
|
| 30 |
+
title="🎭 Party Mask Recommendation App",
|
| 31 |
+
description="Upload a face image and get the best party mask recommendation!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
)
|
| 33 |
|
| 34 |
+
iface.launch()
|
|
|