SynthAIzer commited on
Commit
a84ad2a
·
verified ·
1 Parent(s): b21d91d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -141
app.py CHANGED
@@ -1,141 +1,193 @@
1
- import os
2
- import time
3
- import cv2
4
- import numpy as np
5
- import torch
6
- from ultralytics import YOLO
7
- from PIL import Image as PILImage
8
- from keras_facenet import FaceNet
9
- from transformers import pipeline
10
- import gradio as gr
11
- from datetime import datetime, timedelta
12
- import gc
13
-
14
- # -----------------------------
15
- # Device Setup
16
- # -----------------------------
17
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
18
- print(f"Using device: {DEVICE}")
19
-
20
- # -----------------------------
21
- # Load YOLOv8 Face Model
22
- # -----------------------------
23
- MODEL_PATH = "yolov8n-face.pt" # put this file in your Space repository
24
- face_model = YOLO(MODEL_PATH).to(DEVICE)
25
-
26
- # -----------------------------
27
- # Load FaceNet Embedder
28
- # -----------------------------
29
- embedder = FaceNet() # CPU mode
30
-
31
- # -----------------------------
32
- # Load HuggingFace Age & Gender Models
33
- # -----------------------------
34
- age_model = pipeline(
35
- "image-classification",
36
- model="prithivMLmods/Age-Classification-SigLIP2",
37
- device=-1
38
- )
39
- gender_model = pipeline(
40
- "image-classification",
41
- model="dima806/fairface_gender_image_detection",
42
- device=-1
43
- )
44
-
45
- # -----------------------------
46
- # Face DB
47
- # -----------------------------
48
- FACE_DB = []
49
- NEXT_ID = 1
50
-
51
- def clean_gpu():
52
- if torch.cuda.is_available():
53
- torch.cuda.empty_cache()
54
- gc.collect()
55
-
56
- def cosine_similarity(a, b):
57
- return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
58
-
59
- # -----------------------------
60
- # Main Inference Function
61
- # -----------------------------
62
- def process_image(image):
63
- global NEXT_ID, FACE_DB
64
- start_time = time.time()
65
-
66
- rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
67
-
68
- # Detect faces
69
- results = face_model(rgb_img, verbose=False)
70
- boxes = results[0].boxes.xyxy.cpu().numpy().astype(int)
71
-
72
- now = datetime.now()
73
- FACE_DB = [f for f in FACE_DB if now - f["time"] <= timedelta(hours=1)]
74
-
75
- faces = []
76
- for (x1, y1, x2, y2) in boxes:
77
- face_crop = rgb_img[y1:y2, x1:x2]
78
- if face_crop.size == 0:
79
- continue
80
-
81
- face_embedding = embedder.embeddings([face_crop])[0]
82
-
83
- assigned_id = None
84
- age_pred, gender_pred = "unknown", "unknown"
85
-
86
- # Compare with DB
87
- if FACE_DB:
88
- similarities = [cosine_similarity(face_embedding, entry["embedding"]) for entry in FACE_DB]
89
- best_match_index = int(np.argmax(similarities))
90
- best_score = similarities[best_match_index]
91
-
92
- if best_score > 0.6:
93
- assigned_id = FACE_DB[best_match_index]["id"]
94
- FACE_DB[best_match_index]["time"] = now
95
- FACE_DB[best_match_index]["seen_count"] += 1
96
- age_pred = FACE_DB[best_match_index]["age"]
97
- gender_pred = FACE_DB[best_match_index]["gender"]
98
-
99
- if assigned_id is None:
100
- assigned_id = NEXT_ID
101
- face_pil = PILImage.fromarray(face_crop)
102
- try:
103
- age_pred = age_model(face_pil)[0]["label"]
104
- gender_pred = gender_model(face_pil)[0]["label"]
105
- except Exception:
106
- age_pred, gender_pred = "unknown", "unknown"
107
-
108
- FACE_DB.append({
109
- "id": assigned_id,
110
- "embedding": face_embedding,
111
- "time": now,
112
- "seen_count": 1,
113
- "age": age_pred,
114
- "gender": gender_pred
115
- })
116
- NEXT_ID += 1
117
-
118
- faces.append({
119
- "id": assigned_id,
120
- "age": age_pred,
121
- "gender": gender_pred,
122
- "box": [int(x1), int(y1), int(x2), int(y2)]
123
- })
124
-
125
- total_time = round(time.time() - start_time, 3)
126
- clean_gpu()
127
-
128
- return {"faces": faces, "processing_time_sec": total_time}
129
-
130
- # -----------------------------
131
- # Gradio UI
132
- # -----------------------------
133
- iface = gr.Interface(
134
- fn=process_image,
135
- inputs=gr.Image(type="numpy"),
136
- outputs="json",
137
- title="Face Detection + Age/Gender"
138
- )
139
-
140
- if __name__ == "__main__":
141
- iface.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ # Disable GPU for TensorFlow / Keras BEFORE importing anything
3
+ os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
4
+
5
+ import time
6
+ import cv2
7
+ import numpy as np
8
+ import torch
9
+ from flask import Flask, request, jsonify
10
+ from ultralytics import YOLO
11
+ from PIL import Image as PILImage
12
+ from datetime import datetime, timedelta
13
+ import gc
14
+
15
+ from keras_facenet import FaceNet
16
+ from transformers import pipeline
17
+
18
+ # -----------------------------
19
+ # Flask Setup
20
+ # -----------------------------
21
+ app = Flask(__name__)
22
+
23
+ # -----------------------------
24
+ # Device Setup
25
+ # -----------------------------
26
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
27
+ print(f"Using device: {DEVICE}")
28
+
29
+ # -----------------------------
30
+ # Load YOLOv8 Face Model
31
+ # -----------------------------
32
+ MODEL_PATH = "yolov8n-face.pt" # put this file in your repo root
33
+ if not os.path.exists(MODEL_PATH):
34
+ raise FileNotFoundError(f"Model file not found at {MODEL_PATH}")
35
+
36
+ print("Loading YOLOv8 face detector...")
37
+ face_model = YOLO(MODEL_PATH).to(DEVICE)
38
+ print("YOLOv8 loaded")
39
+
40
+ # -----------------------------
41
+ # Load FaceNet Embedder
42
+ # -----------------------------
43
+ print("Loading FaceNet (Google) model...")
44
+ embedder = FaceNet() # CPU mode
45
+ print("FaceNet loaded")
46
+
47
+ # -----------------------------
48
+ # Load HuggingFace Age & Gender Models
49
+ # -----------------------------
50
+ print("Loading HuggingFace models...")
51
+ age_model = pipeline(
52
+ "image-classification",
53
+ model="prithivMLmods/Age-Classification-SigLIP2",
54
+ device=-1 # CPU mode
55
+ )
56
+ gender_model = pipeline(
57
+ "image-classification",
58
+ model="dima806/fairface_gender_image_detection",
59
+ device=-1
60
+ )
61
+ print("Age & Gender models loaded")
62
+
63
+ # -----------------------------
64
+ # Face DB
65
+ # -----------------------------
66
+ FACE_DB = []
67
+ NEXT_ID = 1
68
+
69
+ # -----------------------------
70
+ # GPU Cleaner
71
+ # -----------------------------
72
+ def clean_gpu():
73
+ if torch.cuda.is_available():
74
+ torch.cuda.empty_cache()
75
+ gc.collect()
76
+
77
+ # -----------------------------
78
+ # Cosine Similarity
79
+ # -----------------------------
80
+ def cosine_similarity(a, b):
81
+ return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
82
+
83
+ # -----------------------------
84
+ # Upload API Endpoint
85
+ # -----------------------------
86
+ @app.route("/upload", methods=["POST"])
87
+ def upload():
88
+ global NEXT_ID, FACE_DB
89
+ start_time = time.time()
90
+
91
+ try:
92
+ # Decode image
93
+ jpg_bytes = request.data
94
+ np_arr = np.frombuffer(jpg_bytes, np.uint8)
95
+ img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
96
+ if img is None:
97
+ return jsonify({"status": "error", "message": "Invalid image"}), 400
98
+
99
+ rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
100
+
101
+ # Detect faces using YOLOv8
102
+ results = face_model(rgb_img, verbose=False)
103
+ boxes = results[0].boxes.xyxy.cpu().numpy().astype(int)
104
+
105
+ now = datetime.now()
106
+ # Remove old entries (> 1 hour)
107
+ FACE_DB = [f for f in FACE_DB if now - f["time"] <= timedelta(hours=1)]
108
+
109
+ faces = []
110
+ for (x1, y1, x2, y2) in boxes:
111
+ face_crop = rgb_img[y1:y2, x1:x2]
112
+ if face_crop.size == 0:
113
+ continue
114
+
115
+ # Get embedding from FaceNet
116
+ face_embedding = embedder.embeddings([face_crop])[0]
117
+
118
+ assigned_id = None
119
+ age_pred, gender_pred = "unknown", "unknown"
120
+
121
+ # Compare with known embeddings
122
+ if FACE_DB:
123
+ similarities = [cosine_similarity(face_embedding, entry["embedding"]) for entry in FACE_DB]
124
+ best_match_index = int(np.argmax(similarities))
125
+ best_score = similarities[best_match_index]
126
+
127
+ if best_score > 0.6: # same person threshold
128
+ assigned_id = FACE_DB[best_match_index]["id"]
129
+ FACE_DB[best_match_index]["time"] = now
130
+ FACE_DB[best_match_index]["seen_count"] += 1
131
+ age_pred = FACE_DB[best_match_index]["age"]
132
+ gender_pred = FACE_DB[best_match_index]["gender"]
133
+
134
+ # If new person
135
+ if assigned_id is None:
136
+ assigned_id = NEXT_ID
137
+ face_pil = PILImage.fromarray(face_crop)
138
+
139
+ try:
140
+ age_pred = age_model(face_pil)[0]["label"]
141
+ gender_pred = gender_model(face_pil)[0]["label"]
142
+ except Exception:
143
+ age_pred, gender_pred = "unknown", "unknown"
144
+
145
+ FACE_DB.append({
146
+ "id": assigned_id,
147
+ "embedding": face_embedding,
148
+ "time": now,
149
+ "seen_count": 1,
150
+ "age": age_pred,
151
+ "gender": gender_pred
152
+ })
153
+ NEXT_ID += 1
154
+
155
+ faces.append({
156
+ "id": assigned_id,
157
+ "age": age_pred,
158
+ "gender": gender_pred,
159
+ "box": [int(x1), int(y1), int(x2), int(y2)]
160
+ })
161
+
162
+ total_time = round(time.time() - start_time, 3)
163
+ clean_gpu()
164
+
165
+ summary = [
166
+ {
167
+ "id": entry["id"],
168
+ "seen_count": entry["seen_count"],
169
+ "age": entry["age"],
170
+ "gender": entry["gender"]
171
+ }
172
+ for entry in FACE_DB
173
+ ]
174
+
175
+ return jsonify({
176
+ "status": "ok",
177
+ "faces": faces,
178
+ "face_count": len(faces),
179
+ "processing_time_sec": total_time,
180
+ "active_faces_last_hour": len(FACE_DB),
181
+ "seen_summary_last_hour": summary
182
+ })
183
+
184
+ except Exception as e:
185
+ return jsonify({"status": "error", "message": str(e)}), 500
186
+
187
+
188
+ # -----------------------------
189
+ # Run Server
190
+ # -----------------------------
191
+ if __name__ == "__main__":
192
+ # Hugging Face Spaces expects host 0.0.0.0 and port 7860
193
+ app.run(host="0.0.0.0", port=7860, debug=False, use_reloader=False)