analyzer.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. # Copyright 2025 Yakhyokhuja Valikhujaev
  2. # Author: Yakhyokhuja Valikhujaev
  3. # GitHub: https://github.com/yakhyo
  4. from typing import List, Optional
  5. import numpy as np
  6. from uniface.attribute.age_gender import AgeGender
  7. from uniface.detection.base import BaseDetector
  8. from uniface.face import Face
  9. from uniface.log import Logger
  10. from uniface.recognition.base import BaseRecognizer
  11. __all__ = ['FaceAnalyzer']
  12. class FaceAnalyzer:
  13. """Unified face analyzer combining detection, recognition, and attributes."""
  14. def __init__(
  15. self,
  16. detector: BaseDetector,
  17. recognizer: Optional[BaseRecognizer] = None,
  18. age_gender: Optional[AgeGender] = None,
  19. ) -> None:
  20. self.detector = detector
  21. self.recognizer = recognizer
  22. self.age_gender = age_gender
  23. Logger.info(f'Initialized FaceAnalyzer with detector={detector.__class__.__name__}')
  24. if recognizer:
  25. Logger.info(f' - Recognition enabled: {recognizer.__class__.__name__}')
  26. if age_gender:
  27. Logger.info(f' - Age/Gender enabled: {age_gender.__class__.__name__}')
  28. def analyze(self, image: np.ndarray) -> List[Face]:
  29. """Analyze faces in an image."""
  30. detections = self.detector.detect(image)
  31. Logger.debug(f'Detected {len(detections)} face(s)')
  32. faces = []
  33. for idx, detection in enumerate(detections):
  34. bbox = detection['bbox']
  35. confidence = detection['confidence']
  36. landmarks = detection['landmarks']
  37. embedding = None
  38. if self.recognizer is not None:
  39. try:
  40. embedding = self.recognizer.get_normalized_embedding(image, landmarks)
  41. Logger.debug(f' Face {idx + 1}: Extracted embedding with shape {embedding.shape}')
  42. except Exception as e:
  43. Logger.warning(f' Face {idx + 1}: Failed to extract embedding: {e}')
  44. age, gender_id = None, None
  45. if self.age_gender is not None:
  46. try:
  47. gender_id, age = self.age_gender.predict(image, bbox)
  48. gender_str = 'Female' if gender_id == 0 else 'Male'
  49. Logger.debug(f' Face {idx + 1}: Age={age}, Gender={gender_str}')
  50. except Exception as e:
  51. Logger.warning(f' Face {idx + 1}: Failed to predict age/gender: {e}')
  52. face = Face(
  53. bbox=bbox,
  54. confidence=confidence,
  55. landmarks=landmarks,
  56. embedding=embedding,
  57. age=age,
  58. gender_id=gender_id,
  59. )
  60. faces.append(face)
  61. Logger.info(f'Analysis complete: {len(faces)} face(s) processed')
  62. return faces
  63. def __repr__(self) -> str:
  64. parts = [f'FaceAnalyzer(detector={self.detector.__class__.__name__}']
  65. if self.recognizer:
  66. parts.append(f'recognizer={self.recognizer.__class__.__name__}')
  67. if self.age_gender:
  68. parts.append(f'age_gender={self.age_gender.__class__.__name__}')
  69. return ', '.join(parts) + ')'