Spaces:
Sleeping
Sleeping
Update detect.py
Browse files
detect.py
CHANGED
|
@@ -53,17 +53,17 @@ def _shape_probabilities_from_geometry(landmarks):
|
|
| 53 |
|
| 54 |
targets = {
|
| 55 |
"Round": {"lw": 1.05, "jaw": 0.88},
|
| 56 |
-
"Square": {"lw": 1.
|
| 57 |
-
"Oval": {"lw": 1.
|
| 58 |
-
"Oblong": {"lw": 1.
|
| 59 |
-
"Heart": {"lw": 1.
|
| 60 |
-
"Diamond": {"lw": 1.
|
| 61 |
}
|
| 62 |
|
| 63 |
scores = {}
|
| 64 |
for shape, target in targets.items():
|
| 65 |
-
lw_score = _gaussian_score(lw_ratio, target["lw"], 0.
|
| 66 |
-
jaw_score = _gaussian_score(jaw_ratio, target["jaw"], 0.
|
| 67 |
scores[shape] = lw_score * jaw_score
|
| 68 |
|
| 69 |
total = sum(scores.values())
|
|
@@ -73,6 +73,20 @@ def _shape_probabilities_from_geometry(landmarks):
|
|
| 73 |
probabilities = {k: round(v / total, 4) for k, v in scores.items()}
|
| 74 |
return dict(sorted(probabilities.items(), key=lambda item: item[1], reverse=True))
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
def detect_face_shape(image_path):
|
| 77 |
"""
|
| 78 |
Detects face shape using PIL and trained Mean Face model.
|
|
@@ -97,17 +111,24 @@ def detect_face_shape(image_path):
|
|
| 97 |
img = img.convert("RGB")
|
| 98 |
img = _crop_to_face(img)
|
| 99 |
|
|
|
|
| 100 |
if isinstance(image_path, str):
|
| 101 |
try:
|
| 102 |
landmarks = get_landmarks(image_path)
|
| 103 |
geom_probs = _shape_probabilities_from_geometry(landmarks)
|
| 104 |
-
if geom_probs:
|
| 105 |
-
return geom_probs
|
| 106 |
except Exception as e:
|
| 107 |
logger.info(f"Landmark-based detection failed, falling back: {e}")
|
| 108 |
|
| 109 |
# Classify directly (classifier handles resizing/grayscale)
|
| 110 |
shape_probs = classify_face_shape(img)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
# Get the top shape
|
| 113 |
best_shape = list(shape_probs.keys())[0]
|
|
|
|
| 53 |
|
| 54 |
targets = {
|
| 55 |
"Round": {"lw": 1.05, "jaw": 0.88},
|
| 56 |
+
"Square": {"lw": 1.12, "jaw": 0.95},
|
| 57 |
+
"Oval": {"lw": 1.25, "jaw": 0.86},
|
| 58 |
+
"Oblong": {"lw": 1.35, "jaw": 0.86},
|
| 59 |
+
"Heart": {"lw": 1.22, "jaw": 0.75},
|
| 60 |
+
"Diamond": {"lw": 1.20, "jaw": 0.70},
|
| 61 |
}
|
| 62 |
|
| 63 |
scores = {}
|
| 64 |
for shape, target in targets.items():
|
| 65 |
+
lw_score = _gaussian_score(lw_ratio, target["lw"], 0.1)
|
| 66 |
+
jaw_score = _gaussian_score(jaw_ratio, target["jaw"], 0.07)
|
| 67 |
scores[shape] = lw_score * jaw_score
|
| 68 |
|
| 69 |
total = sum(scores.values())
|
|
|
|
| 73 |
probabilities = {k: round(v / total, 4) for k, v in scores.items()}
|
| 74 |
return dict(sorted(probabilities.items(), key=lambda item: item[1], reverse=True))
|
| 75 |
|
| 76 |
+
|
| 77 |
+
def _blend_probabilities(primary, secondary, alpha=0.55):
|
| 78 |
+
labels = set(primary.keys()) | set(secondary.keys())
|
| 79 |
+
blended = {}
|
| 80 |
+
for label in labels:
|
| 81 |
+
blended[label] = alpha * primary.get(label, 0) + (1 - alpha) * secondary.get(label, 0)
|
| 82 |
+
total = sum(blended.values())
|
| 83 |
+
if total <= 0:
|
| 84 |
+
return primary
|
| 85 |
+
blended = {k: round(v / total, 4) for k, v in blended.items()}
|
| 86 |
+
return dict(sorted(blended.items(), key=lambda item: item[1], reverse=True))
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
| 90 |
def detect_face_shape(image_path):
|
| 91 |
"""
|
| 92 |
Detects face shape using PIL and trained Mean Face model.
|
|
|
|
| 111 |
img = img.convert("RGB")
|
| 112 |
img = _crop_to_face(img)
|
| 113 |
|
| 114 |
+
geom_probs = None
|
| 115 |
if isinstance(image_path, str):
|
| 116 |
try:
|
| 117 |
landmarks = get_landmarks(image_path)
|
| 118 |
geom_probs = _shape_probabilities_from_geometry(landmarks)
|
|
|
|
|
|
|
| 119 |
except Exception as e:
|
| 120 |
logger.info(f"Landmark-based detection failed, falling back: {e}")
|
| 121 |
|
| 122 |
# Classify directly (classifier handles resizing/grayscale)
|
| 123 |
shape_probs = classify_face_shape(img)
|
| 124 |
+
|
| 125 |
+
if geom_probs:
|
| 126 |
+
geom_values = list(geom_probs.values())
|
| 127 |
+
top = geom_values[0] if geom_values else 0
|
| 128 |
+
runner_up = geom_values[1] if len(geom_values) > 1 else 0
|
| 129 |
+
if top >= 0.45 and (top - runner_up) >= 0.08:
|
| 130 |
+
return geom_probs
|
| 131 |
+
return _blend_probabilities(geom_probs, shape_probs, alpha=0.55)
|
| 132 |
|
| 133 |
# Get the top shape
|
| 134 |
best_shape = list(shape_probs.keys())[0]
|