Spaces:
Sleeping
Sleeping
Update services/vision.py
Browse files- services/vision.py +53 -46
services/vision.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
| 1 |
import cv2
|
| 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
|
| 10 |
-
|
| 11 |
"""
|
| 12 |
report = []
|
| 13 |
pipeline_status = {"detected": 0, "identified": 0}
|
|
@@ -19,58 +20,64 @@ def process_frame_synchronous(frame):
|
|
| 19 |
for face_obj in raw_faces:
|
| 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 |
|
| 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 |
-
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
|
|
|
|
| 1 |
import cv2
|
| 2 |
import os
|
| 3 |
import time
|
| 4 |
+
import base64
|
| 5 |
from deepface import DeepFace
|
| 6 |
from core.config import DETECTOR, MODELS, THRESHOLDS, MIN_FACE_AREA, REQUIRED_AVERAGE_CONFIDENCE
|
| 7 |
|
| 8 |
def process_frame_synchronous(frame):
|
| 9 |
"""
|
| 10 |
+
The Visual-Hard Pipeline:
|
| 11 |
+
Identifies faces and returns base64 crops for debugging.
|
| 12 |
"""
|
| 13 |
report = []
|
| 14 |
pipeline_status = {"detected": 0, "identified": 0}
|
|
|
|
| 20 |
for face_obj in raw_faces:
|
| 21 |
region = face_obj.get('facial_area', {})
|
| 22 |
w, h = region.get('w', 0), region.get('h', 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
x, y = region.get('x', 0), region.get('y', 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
+
# Prepare face data structure
|
| 26 |
+
face_info = {
|
| 27 |
+
"status": "unknown",
|
| 28 |
+
"name": "Unknown",
|
| 29 |
+
"score": 0,
|
| 30 |
+
"box": {"x": x, "y": y, "w": w, "h": h},
|
| 31 |
+
"crop_b64": ""
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
# Generate Debug Crop
|
| 35 |
+
if w > 0 and h > 0 and x >= 0 and y >= 0:
|
| 36 |
+
cropped_face = frame[y:y+h, x:x+w]
|
| 37 |
+
if cropped_face.size != 0:
|
| 38 |
+
# Encode crop to base64 so user can see it in browser
|
| 39 |
+
_, buffer = cv2.imencode('.jpg', cropped_face)
|
| 40 |
+
face_info["crop_b64"] = "data:image/jpeg;base64," + base64.b64encode(buffer).decode('utf-8')
|
| 41 |
|
| 42 |
+
# Quality Gate
|
| 43 |
+
if (w * h) >= MIN_FACE_AREA:
|
| 44 |
+
# Recognition Logic
|
| 45 |
+
predictions = {}
|
| 46 |
+
confidences = {}
|
| 47 |
+
for model in MODELS:
|
| 48 |
+
results = DeepFace.find(img_path=cropped_face, db_path="faces_db", model_name=model,
|
| 49 |
+
enforce_detection=False, detector_backend="skip",
|
| 50 |
+
distance_metric="cosine", silent=True)
|
| 51 |
+
|
| 52 |
+
if len(results) > 0 and not results[0].empty:
|
| 53 |
+
df = results[0].sort_values(by="distance")
|
| 54 |
+
best_match = df.iloc[0]
|
| 55 |
+
conf = round((1.0 - best_match["distance"]) * 100, 1)
|
| 56 |
+
predictions[model] = os.path.basename(best_match['identity']).split('.')[0]
|
| 57 |
+
confidences[model] = conf
|
| 58 |
+
else:
|
| 59 |
+
predictions[model] = "Unknown"
|
| 60 |
+
confidences[model] = 0.0
|
| 61 |
|
| 62 |
+
all_names = [n for n in predictions.values() if n != "Unknown"]
|
| 63 |
+
unique_names = set(all_names)
|
| 64 |
+
avg_conf = sum(confidences.values()) / len(MODELS)
|
| 65 |
+
|
| 66 |
+
if len(unique_names) == 1 and avg_conf >= REQUIRED_AVERAGE_CONFIDENCE:
|
| 67 |
+
face_info["status"] = "match"
|
| 68 |
+
face_info["name"] = all_names[0]
|
| 69 |
+
face_info["score"] = avg_conf
|
| 70 |
+
pipeline_status["identified"] += 1
|
| 71 |
+
else:
|
| 72 |
+
face_info["status"] = "fail"
|
| 73 |
+
if unique_names: face_info["name"] = list(unique_names)[0]
|
| 74 |
+
face_info["score"] = avg_conf
|
| 75 |
|
| 76 |
+
report.append(face_info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
return report, pipeline_status
|
| 79 |
|
| 80 |
+
except ValueError: return [], pipeline_status
|
|
|
|
| 81 |
except Exception as e:
|
| 82 |
print(f"🔴 Critical Pipeline Error: {e}")
|
| 83 |
return [], pipeline_status
|