Spaces:
Sleeping
Sleeping
Update services/vision.py
Browse files- services/vision.py +27 -33
services/vision.py
CHANGED
|
@@ -2,21 +2,17 @@ import cv2
|
|
| 2 |
import os
|
| 3 |
import time
|
| 4 |
from deepface import DeepFace
|
| 5 |
-
from core.config import DETECTOR, MODELS, THRESHOLDS, MIN_FACE_AREA
|
| 6 |
|
| 7 |
def process_frame_synchronous(frame):
|
| 8 |
"""
|
| 9 |
-
The
|
| 10 |
-
|
| 11 |
-
2. Filters by quality.
|
| 12 |
-
3. Identifies every face with a confidence score.
|
| 13 |
-
4. Returns a complete report BEFORE the next frame is allowed.
|
| 14 |
"""
|
| 15 |
report = []
|
| 16 |
pipeline_status = {"detected": 0, "identified": 0}
|
| 17 |
|
| 18 |
try:
|
| 19 |
-
# Phase 1: Harvester (Detection)
|
| 20 |
raw_faces = DeepFace.extract_faces(img_path=frame, detector_backend=DETECTOR, enforce_detection=True, align=True)
|
| 21 |
pipeline_status["detected"] = len(raw_faces)
|
| 22 |
|
|
@@ -24,17 +20,13 @@ def process_frame_synchronous(frame):
|
|
| 24 |
region = face_obj.get('facial_area', {})
|
| 25 |
w, h = region.get('w', 0), region.get('h', 0)
|
| 26 |
|
| 27 |
-
# Quality Gate
|
| 28 |
if (w * h) < MIN_FACE_AREA:
|
| 29 |
continue
|
| 30 |
|
| 31 |
x, y = region.get('x', 0), region.get('y', 0)
|
| 32 |
cropped_face = frame[y:y+h, x:x+w]
|
| 33 |
-
|
| 34 |
-
if cropped_face.size == 0:
|
| 35 |
-
continue
|
| 36 |
|
| 37 |
-
# Phase 2: Interrogator (Consensus Engine)
|
| 38 |
predictions = {}
|
| 39 |
confidences = {}
|
| 40 |
|
|
@@ -48,35 +40,37 @@ def process_frame_synchronous(frame):
|
|
| 48 |
best_match = df.iloc[0]
|
| 49 |
dist = best_match["distance"]
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
confidences[model] = conf
|
| 58 |
-
else:
|
| 59 |
-
predictions[model] = None
|
| 60 |
else:
|
| 61 |
-
predictions[model] =
|
|
|
|
| 62 |
|
| 63 |
-
#
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
| 71 |
pipeline_status["identified"] += 1
|
| 72 |
else:
|
| 73 |
-
#
|
| 74 |
-
|
|
|
|
|
|
|
| 75 |
|
| 76 |
return report, pipeline_status
|
| 77 |
|
| 78 |
except ValueError:
|
| 79 |
-
return [], pipeline_status
|
| 80 |
except Exception as e:
|
| 81 |
print(f"🔴 Critical Pipeline Error: {e}")
|
| 82 |
return [], pipeline_status
|
|
|
|
| 2 |
import os
|
| 3 |
import time
|
| 4 |
from deepface import DeepFace
|
| 5 |
+
from core.config import DETECTOR, MODELS, THRESHOLDS, MIN_FACE_AREA, REQUIRED_AVERAGE_CONFIDENCE
|
| 6 |
|
| 7 |
def process_frame_synchronous(frame):
|
| 8 |
"""
|
| 9 |
+
The Smart-Hard Pipeline:
|
| 10 |
+
Calculates weighted average across models and returns failure details.
|
|
|
|
|
|
|
|
|
|
| 11 |
"""
|
| 12 |
report = []
|
| 13 |
pipeline_status = {"detected": 0, "identified": 0}
|
| 14 |
|
| 15 |
try:
|
|
|
|
| 16 |
raw_faces = DeepFace.extract_faces(img_path=frame, detector_backend=DETECTOR, enforce_detection=True, align=True)
|
| 17 |
pipeline_status["detected"] = len(raw_faces)
|
| 18 |
|
|
|
|
| 20 |
region = face_obj.get('facial_area', {})
|
| 21 |
w, h = region.get('w', 0), region.get('h', 0)
|
| 22 |
|
|
|
|
| 23 |
if (w * h) < MIN_FACE_AREA:
|
| 24 |
continue
|
| 25 |
|
| 26 |
x, y = region.get('x', 0), region.get('y', 0)
|
| 27 |
cropped_face = frame[y:y+h, x:x+w]
|
| 28 |
+
if cropped_face.size == 0: continue
|
|
|
|
|
|
|
| 29 |
|
|
|
|
| 30 |
predictions = {}
|
| 31 |
confidences = {}
|
| 32 |
|
|
|
|
| 40 |
best_match = df.iloc[0]
|
| 41 |
dist = best_match["distance"]
|
| 42 |
|
| 43 |
+
# Convert distance to confidence (0.0 distance = 100%)
|
| 44 |
+
conf = round((1.0 - dist) * 100, 1)
|
| 45 |
+
name = os.path.basename(best_match['identity']).split('.')[0]
|
| 46 |
+
|
| 47 |
+
predictions[model] = name
|
| 48 |
+
confidences[model] = conf
|
|
|
|
|
|
|
|
|
|
| 49 |
else:
|
| 50 |
+
predictions[model] = "Unknown"
|
| 51 |
+
confidences[model] = 0.0
|
| 52 |
|
| 53 |
+
# --- SMART CONSENSUS LOGIC ---
|
| 54 |
+
# 1. Check if they agree on the NAME
|
| 55 |
+
all_names = [n for n in predictions.values() if n != "Unknown"]
|
| 56 |
+
unique_names = set(all_names)
|
| 57 |
+
|
| 58 |
+
avg_conf = sum(confidences.values()) / len(MODELS)
|
| 59 |
+
|
| 60 |
+
if len(unique_names) == 1 and avg_conf >= REQUIRED_AVERAGE_CONFIDENCE:
|
| 61 |
+
confirmed_name = all_names[0]
|
| 62 |
+
report.append({"status": "match", "name": confirmed_name, "score": avg_conf})
|
| 63 |
pipeline_status["identified"] += 1
|
| 64 |
else:
|
| 65 |
+
# Failure Report: Tell the user WHO we thought it was and WHAT the score was
|
| 66 |
+
best_guess = "Unknown"
|
| 67 |
+
if unique_names: best_guess = list(unique_names)[0]
|
| 68 |
+
report.append({"status": "fail", "name": best_guess, "score": avg_conf})
|
| 69 |
|
| 70 |
return report, pipeline_status
|
| 71 |
|
| 72 |
except ValueError:
|
| 73 |
+
return [], pipeline_status
|
| 74 |
except Exception as e:
|
| 75 |
print(f"🔴 Critical Pipeline Error: {e}")
|
| 76 |
return [], pipeline_status
|