matrice-analytics 0.1.53__py3-none-any.whl → 0.1.55__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of matrice-analytics might be problematic. Click here for more details.
- matrice_analytics/post_processing/face_reg/embedding_manager.py +113 -24
- matrice_analytics/post_processing/face_reg/face_recognition.py +11 -15
- {matrice_analytics-0.1.53.dist-info → matrice_analytics-0.1.55.dist-info}/METADATA +1 -1
- {matrice_analytics-0.1.53.dist-info → matrice_analytics-0.1.55.dist-info}/RECORD +7 -7
- {matrice_analytics-0.1.53.dist-info → matrice_analytics-0.1.55.dist-info}/WHEEL +0 -0
- {matrice_analytics-0.1.53.dist-info → matrice_analytics-0.1.55.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_analytics-0.1.53.dist-info → matrice_analytics-0.1.55.dist-info}/top_level.txt +0 -0
|
@@ -181,33 +181,82 @@ class EmbeddingManager:
|
|
|
181
181
|
self.logger.info("Loading staff embeddings from API...")
|
|
182
182
|
response = await self.face_client.get_all_staff_embeddings()
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
# Robust response handling: accept dict with data or raw list
|
|
185
|
+
embeddings_data: List[Dict[str, Any]] = []
|
|
186
|
+
if isinstance(response, dict):
|
|
187
|
+
# Typical: { success: True, data: [...] }
|
|
188
|
+
if response.get("success", False) and isinstance(response.get("data"), list):
|
|
189
|
+
embeddings_data = response.get("data", [])
|
|
190
|
+
# Alternate: { data: [...] } without success flag
|
|
191
|
+
elif isinstance(response.get("data"), list):
|
|
192
|
+
embeddings_data = response.get("data", [])
|
|
193
|
+
# Fallback keys sometimes used
|
|
194
|
+
elif isinstance(response.get("items"), list):
|
|
195
|
+
embeddings_data = response.get("items", [])
|
|
196
|
+
else:
|
|
197
|
+
self.logger.error(f"Unexpected embeddings response shape (dict): keys={list(response.keys())}")
|
|
198
|
+
return False
|
|
199
|
+
elif isinstance(response, list):
|
|
200
|
+
# Some deployments return raw list directly
|
|
201
|
+
embeddings_data = response
|
|
202
|
+
else:
|
|
203
|
+
self.logger.error(f"Unexpected embeddings response type: {type(response)}")
|
|
186
204
|
return False
|
|
187
|
-
|
|
188
|
-
# The API response has the format: {"success": True, "data": [embedding_items]}
|
|
189
|
-
# Each item has the structure shown in the sample response
|
|
190
|
-
embeddings_data = response.get("data", [])
|
|
191
205
|
|
|
192
206
|
self.staff_embeddings = []
|
|
193
207
|
embeddings_list = []
|
|
194
|
-
|
|
208
|
+
expected_dim: Optional[int] = None
|
|
209
|
+
dims_observed: List[int] = []
|
|
210
|
+
mismatch_examples: List[Tuple[str, int]] = [] # (staffId, dim)
|
|
211
|
+
|
|
195
212
|
for item in embeddings_data:
|
|
196
|
-
#
|
|
197
|
-
|
|
198
|
-
|
|
213
|
+
# Skip inactive if provided
|
|
214
|
+
if isinstance(item, dict) and item.get("isActive") is False:
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
raw_emb = []
|
|
218
|
+
try:
|
|
219
|
+
raw_emb = item.get("embedding", []) if isinstance(item, dict) else []
|
|
220
|
+
except Exception:
|
|
221
|
+
raw_emb = []
|
|
222
|
+
# Record observed dimension for debugging
|
|
223
|
+
try:
|
|
224
|
+
dims_observed.append(len(raw_emb) if isinstance(raw_emb, list) else 0)
|
|
225
|
+
except Exception:
|
|
226
|
+
dims_observed.append(0)
|
|
227
|
+
|
|
228
|
+
# Validate and coerce embedding list
|
|
229
|
+
if not isinstance(raw_emb, list) or len(raw_emb) == 0:
|
|
230
|
+
continue
|
|
231
|
+
try:
|
|
232
|
+
# Ensure numeric float32 list
|
|
233
|
+
clean_emb = [float(v) for v in raw_emb]
|
|
234
|
+
except Exception:
|
|
235
|
+
continue
|
|
236
|
+
|
|
237
|
+
# Dimension consistency
|
|
238
|
+
if expected_dim is None:
|
|
239
|
+
expected_dim = len(clean_emb)
|
|
240
|
+
if len(clean_emb) != expected_dim:
|
|
241
|
+
# Collect a few examples to aid debugging
|
|
242
|
+
try:
|
|
243
|
+
mismatch_examples.append((str(item.get("staffId", "")), len(clean_emb)))
|
|
244
|
+
except Exception:
|
|
245
|
+
mismatch_examples.append(("", len(clean_emb)))
|
|
246
|
+
self.logger.warning(f"Skipping embedding with mismatched dimension: got {len(clean_emb)} expected {expected_dim}")
|
|
247
|
+
continue
|
|
248
|
+
|
|
199
249
|
staff_embedding = StaffEmbedding(
|
|
200
|
-
embedding_id=item.get("embeddingId", ""),
|
|
201
|
-
staff_id=item.get("staffId", ""),
|
|
202
|
-
embedding=
|
|
203
|
-
employee_id=str(item.get("employeeId", "")),
|
|
204
|
-
staff_details=item.get("staffDetails", {}),
|
|
205
|
-
is_active=item.get("isActive", True)
|
|
250
|
+
embedding_id=(item.get("embeddingId", "") if isinstance(item, dict) else ""),
|
|
251
|
+
staff_id=(item.get("staffId", "") if isinstance(item, dict) else ""),
|
|
252
|
+
embedding=clean_emb,
|
|
253
|
+
employee_id=str(item.get("employeeId", "")) if isinstance(item, dict) else "",
|
|
254
|
+
staff_details=(item.get("staffDetails", {}) if isinstance(item, dict) else {}),
|
|
255
|
+
is_active=(item.get("isActive", True) if isinstance(item, dict) else True)
|
|
206
256
|
)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
embeddings_list.append(staff_embedding.embedding)
|
|
257
|
+
|
|
258
|
+
self.staff_embeddings.append(staff_embedding)
|
|
259
|
+
embeddings_list.append(clean_emb)
|
|
211
260
|
|
|
212
261
|
# Create numpy matrix for fast similarity computation (thread-safe)
|
|
213
262
|
with self._embeddings_lock:
|
|
@@ -220,12 +269,27 @@ class EmbeddingManager:
|
|
|
220
269
|
|
|
221
270
|
self.embedding_metadata = self.staff_embeddings.copy()
|
|
222
271
|
self.staff_embeddings_last_update = time.time()
|
|
223
|
-
self.logger.info(f"Successfully loaded and cached {len(self.staff_embeddings)} staff embeddings")
|
|
224
|
-
|
|
272
|
+
self.logger.info(f"Successfully loaded and cached {len(self.staff_embeddings)} staff embeddings (dim={self.embeddings_matrix.shape[1]})")
|
|
273
|
+
try:
|
|
274
|
+
# Quick sanity metrics
|
|
275
|
+
row0_sum = float(np.sum(self.embeddings_matrix[0])) if self.embeddings_matrix.shape[0] > 0 else 0.0
|
|
276
|
+
self.logger.debug(f"Embeddings matrix shape: {self.embeddings_matrix.shape}, dtype={self.embeddings_matrix.dtype}, row0_sum={row0_sum:.4f}")
|
|
277
|
+
except Exception:
|
|
278
|
+
pass
|
|
225
279
|
return True
|
|
226
280
|
else:
|
|
227
|
-
|
|
228
|
-
|
|
281
|
+
# Build diagnostics and raise to stop pipeline early with actionable info
|
|
282
|
+
dims_summary: Dict[int, int] = {}
|
|
283
|
+
for d in dims_observed:
|
|
284
|
+
dims_summary[d] = dims_summary.get(d, 0) + 1
|
|
285
|
+
self.logger.error(
|
|
286
|
+
"No valid staff embeddings loaded. Observed dimension distribution: "
|
|
287
|
+
f"{dims_summary}. Expected_dim={expected_dim}. Mismatch examples (staffId, dim): "
|
|
288
|
+
f"{mismatch_examples[:5]}"
|
|
289
|
+
)
|
|
290
|
+
raise RuntimeError(
|
|
291
|
+
f"Failed to load staff embeddings due to dimension mismatch. Observed dims: {dims_summary}"
|
|
292
|
+
)
|
|
229
293
|
|
|
230
294
|
except Exception as e:
|
|
231
295
|
self.logger.error(f"Error loading staff embeddings: {e}", exc_info=True)
|
|
@@ -278,6 +342,10 @@ class EmbeddingManager:
|
|
|
278
342
|
|
|
279
343
|
try:
|
|
280
344
|
query_array = np.array(query_embedding, dtype=np.float32).reshape(1, -1)
|
|
345
|
+
# Dimension check
|
|
346
|
+
if embeddings_matrix.shape[1] != query_array.shape[1]:
|
|
347
|
+
self.logger.warning(f"Query embedding dim mismatch: query={query_array.shape[1]} staff={embeddings_matrix.shape[1]}")
|
|
348
|
+
return None
|
|
281
349
|
|
|
282
350
|
# Normalize query embedding
|
|
283
351
|
query_norm = np.linalg.norm(query_array)
|
|
@@ -302,6 +370,27 @@ class EmbeddingManager:
|
|
|
302
370
|
except Exception as e:
|
|
303
371
|
self.logger.error(f"Error in local similarity search: {e}", exc_info=True)
|
|
304
372
|
return None
|
|
373
|
+
|
|
374
|
+
def get_best_similarity(self, query_embedding: List[float]) -> float:
|
|
375
|
+
"""Return the best cosine similarity for debugging/observability (no threshold gating)."""
|
|
376
|
+
with self._embeddings_lock:
|
|
377
|
+
if self.embeddings_matrix is None or len(self.embedding_metadata) == 0:
|
|
378
|
+
return 0.0
|
|
379
|
+
embeddings_matrix = self.embeddings_matrix.copy() if self.embeddings_matrix is not None else None
|
|
380
|
+
if embeddings_matrix is None:
|
|
381
|
+
return 0.0
|
|
382
|
+
try:
|
|
383
|
+
query_array = np.array(query_embedding, dtype=np.float32).reshape(1, -1)
|
|
384
|
+
if embeddings_matrix.shape[1] != query_array.shape[1]:
|
|
385
|
+
return 0.0
|
|
386
|
+
qn = np.linalg.norm(query_array)
|
|
387
|
+
if qn == 0:
|
|
388
|
+
return 0.0
|
|
389
|
+
query_array = query_array / qn
|
|
390
|
+
similarities = np.dot(embeddings_matrix, query_array.T).flatten()
|
|
391
|
+
return float(np.max(similarities)) if similarities.size > 0 else 0.0
|
|
392
|
+
except Exception:
|
|
393
|
+
return 0.0
|
|
305
394
|
|
|
306
395
|
def extract_embedding_from_detection(self, detection: Dict) -> Tuple[Dict, Optional[List[float]]]:
|
|
307
396
|
"""Extract and validate embedding from detection."""
|
|
@@ -178,10 +178,16 @@ class TemporalIdentityManager:
|
|
|
178
178
|
"known"
|
|
179
179
|
)
|
|
180
180
|
else:
|
|
181
|
-
# No local match found
|
|
182
|
-
|
|
181
|
+
# No local match found; log best similarity for observability
|
|
182
|
+
best_sim = 0.0
|
|
183
|
+
try:
|
|
184
|
+
best_sim = float(self.embedding_manager.get_best_similarity(emb))
|
|
185
|
+
except Exception:
|
|
186
|
+
pass
|
|
187
|
+
self.logger.debug(f"No local match found - best_similarity={best_sim:.3f}, threshold={self.threshold:.3f}")
|
|
183
188
|
print("------------------FACE RECOG TEMPORAL IDENTITY MANAGER UPDATE - COMPUTE BEST IDENTITY (LOCAL - NO MATCH)----------------------------")
|
|
184
189
|
print("LATENCY:",(time.time() - st10)*1000,"| Throughput fps:",(1.0 / (time.time() - st10)) if (time.time() - st10) > 0 else None)
|
|
190
|
+
print(f"BEST_SIM={best_sim:.3f} THRESH={self.threshold:.3f}")
|
|
185
191
|
print("------------------FACE RECOG TEMPORAL IDENTITY MANAGER UPDATE - COMPUTE BEST IDENTITY (LOCAL - NO MATCH)----------------------------")
|
|
186
192
|
|
|
187
193
|
return None, "Unknown", 0.0, None, {}, "unknown"
|
|
@@ -987,20 +993,10 @@ class FaceRecognitionEmbeddingUseCase(BaseProcessor):
|
|
|
987
993
|
) -> List[Dict]:
|
|
988
994
|
"""Process face recognition for each detection with embeddings"""
|
|
989
995
|
|
|
990
|
-
#
|
|
991
|
-
st0=time.time()
|
|
996
|
+
# Face client is initialized in initialize(); if absent, this indicates a prior init failure
|
|
992
997
|
if not self.face_client:
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
except Exception as e:
|
|
996
|
-
self.logger.warning(
|
|
997
|
-
f"Could not initialize face recognition client: {e}"
|
|
998
|
-
)
|
|
999
|
-
# No client available, return empty list (no results)
|
|
1000
|
-
return []
|
|
1001
|
-
print("------------------FACE RECOG CLIENT INIT----------------------------")
|
|
1002
|
-
print("LATENCY:",(time.time() - st0)*1000,"| Throughput fps:",(1.0 / (time.time() - st0)) if (time.time() - st0) > 0 else None)
|
|
1003
|
-
print("------------------FACE RECOG CLIENT INIT----------------------------")
|
|
998
|
+
self.logger.error("Face client not initialized; initialize() must succeed before processing")
|
|
999
|
+
return []
|
|
1004
1000
|
|
|
1005
1001
|
# Initialize unknown faces storage if not exists
|
|
1006
1002
|
if not hasattr(self, "unknown_faces_storage"):
|
|
@@ -28,8 +28,8 @@ matrice_analytics/post_processing/core/config.py,sha256=uyxWndO-DE9PeGD_h5K3TeB0
|
|
|
28
28
|
matrice_analytics/post_processing/core/config_utils.py,sha256=QuAS-_JKSoNOtfUWgr7Alf_wsqODzN2rHlQu-cHRK0s,34311
|
|
29
29
|
matrice_analytics/post_processing/face_reg/__init__.py,sha256=yntaiGlW9vdjBpPZQXNuovALihJPzRlFyUE88l3MhBA,1364
|
|
30
30
|
matrice_analytics/post_processing/face_reg/compare_similarity.py,sha256=NlFc8b2a74k0PqSFAbuM_fUbA1BT3pr3VUgvSqRpJzQ,23396
|
|
31
|
-
matrice_analytics/post_processing/face_reg/embedding_manager.py,sha256=
|
|
32
|
-
matrice_analytics/post_processing/face_reg/face_recognition.py,sha256=
|
|
31
|
+
matrice_analytics/post_processing/face_reg/embedding_manager.py,sha256=KhSV-JXykqvhRC1iwvbAyJZrFl3Ntv1eiLE5semKOHE,40533
|
|
32
|
+
matrice_analytics/post_processing/face_reg/face_recognition.py,sha256=ILkQumMY2ij_QrhJxzLifOv_q5rXh7N4mW94hr6EcgQ,103585
|
|
33
33
|
matrice_analytics/post_processing/face_reg/face_recognition_client.py,sha256=eF2NYju1uWKXhILndI1rh4_VhWrKSGidui2jjbPQXgM,27596
|
|
34
34
|
matrice_analytics/post_processing/face_reg/people_activity_logging.py,sha256=vZbIvkK1h3h58ROeF0_ygF3lqr19O2h5222bN8XyIis,13675
|
|
35
35
|
matrice_analytics/post_processing/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -189,8 +189,8 @@ matrice_analytics/post_processing/utils/format_utils.py,sha256=UTF7A5h9j0_S12xH9
|
|
|
189
189
|
matrice_analytics/post_processing/utils/geometry_utils.py,sha256=BWfdM6RsdJTTLR1GqkWfdwpjMEjTCJyuBxA4zVGKdfk,9623
|
|
190
190
|
matrice_analytics/post_processing/utils/smoothing_utils.py,sha256=78U-yucAcjUiZ0NIAc9NOUSIT0PWP1cqyIPA_Fdrjp0,14699
|
|
191
191
|
matrice_analytics/post_processing/utils/tracking_utils.py,sha256=rWxuotnJ3VLMHIBOud2KLcu4yZfDp7hVPWUtNAq_2xw,8288
|
|
192
|
-
matrice_analytics-0.1.
|
|
193
|
-
matrice_analytics-0.1.
|
|
194
|
-
matrice_analytics-0.1.
|
|
195
|
-
matrice_analytics-0.1.
|
|
196
|
-
matrice_analytics-0.1.
|
|
192
|
+
matrice_analytics-0.1.55.dist-info/licenses/LICENSE.txt,sha256=_uQUZpgO0mRYL5-fPoEvLSbNnLPv6OmbeEDCHXhK6Qc,1066
|
|
193
|
+
matrice_analytics-0.1.55.dist-info/METADATA,sha256=XlOk1g_bwwjiRWoXw5loC_kJd9weHzxZC58JgkhOzhA,14378
|
|
194
|
+
matrice_analytics-0.1.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
195
|
+
matrice_analytics-0.1.55.dist-info/top_level.txt,sha256=STAPEU-e-rWTerXaspdi76T_eVRSrEfFpURSP7_Dt8E,18
|
|
196
|
+
matrice_analytics-0.1.55.dist-info/RECORD,,
|
|
File without changes
|
{matrice_analytics-0.1.53.dist-info → matrice_analytics-0.1.55.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
|
File without changes
|