Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import logging | |
| from typing import Mapping | |
| from config import FACE_LABELS | |
| LOGGER = logging.getLogger(__name__) | |
| DEEPFACE_TO_3CLASS = { | |
| "happy": "positive", | |
| # In this demo, surprise is treated as positive activation by default. | |
| "surprise": "positive", | |
| "neutral": "neutral", | |
| "sad": "negative", | |
| "angry": "negative", | |
| "fear": "negative", | |
| "disgust": "negative", | |
| } | |
| def map_deepface_emotion_to_3class(raw_emotion: str) -> str: | |
| """Map a DeepFace emotion label to positive/neutral/negative.""" | |
| normalized = str(raw_emotion or "").strip().lower() | |
| mapped = DEEPFACE_TO_3CLASS.get(normalized) | |
| if mapped is None: | |
| LOGGER.warning("Unknown DeepFace emotion label %r; fallback to neutral.", raw_emotion) | |
| return "neutral" | |
| return mapped | |
| def convert_deepface_emotion_scores(emotion_scores: Mapping[str, float] | None) -> dict[str, float]: | |
| """Aggregate DeepFace emotion scores into normalized 3-class probabilities. | |
| DeepFace may return values as percentages or probabilities. Because the final | |
| output is normalized by total mass, both formats are handled consistently. | |
| """ | |
| if not emotion_scores: | |
| return {"positive": 0.0, "neutral": 1.0, "negative": 0.0} | |
| grouped = {label: 0.0 for label in FACE_LABELS} | |
| for raw_label, value in emotion_scores.items(): | |
| mapped_label = map_deepface_emotion_to_3class(raw_label) | |
| try: | |
| numeric_value = float(value) | |
| except (TypeError, ValueError): | |
| LOGGER.warning("Invalid emotion score for %r: %r", raw_label, value) | |
| continue | |
| if numeric_value > 0: | |
| grouped[mapped_label] += numeric_value | |
| total = sum(grouped.values()) | |
| if total <= 0: | |
| return {"positive": 0.0, "neutral": 1.0, "negative": 0.0} | |
| normalized = {label: grouped[label] / total for label in FACE_LABELS} | |
| return _round_and_balance(normalized, FACE_LABELS) | |
| def _round_and_balance(values: dict[str, float], labels: list[str]) -> dict[str, float]: | |
| rounded = {label: round(max(0.0, min(1.0, values.get(label, 0.0))), 4) for label in labels} | |
| diff = round(1.0 - sum(rounded.values()), 4) | |
| if abs(diff) > 0 and labels: | |
| max_label = max(labels, key=lambda label: rounded[label]) | |
| rounded[max_label] = round(max(0.0, min(1.0, rounded[max_label] + diff)), 4) | |
| return rounded | |