face.py 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. # Copyright 2025 Yakhyokhuja Valikhujaev
  2. # Author: Yakhyokhuja Valikhujaev
  3. # GitHub: https://github.com/yakhyo
  4. from dataclasses import asdict, dataclass
  5. from typing import Optional
  6. import numpy as np
  7. from uniface.face_utils import compute_similarity
  8. __all__ = ['Face']
  9. @dataclass
  10. class Face:
  11. """Detected face with analysis results."""
  12. bbox: np.ndarray
  13. confidence: float
  14. landmarks: np.ndarray
  15. embedding: Optional[np.ndarray] = None
  16. age: Optional[int] = None
  17. gender_id: Optional[int] = None # 0: Female, 1: Male
  18. def compute_similarity(self, other: 'Face') -> float:
  19. """Compute cosine similarity with another face."""
  20. if self.embedding is None or other.embedding is None:
  21. raise ValueError('Both faces must have embeddings for similarity computation')
  22. return float(compute_similarity(self.embedding, other.embedding))
  23. def to_dict(self) -> dict:
  24. """Convert to dictionary."""
  25. return asdict(self)
  26. @property
  27. def gender(self) -> str:
  28. """Get gender as a string label (Female or Male)."""
  29. if self.gender_id is None:
  30. return None
  31. return 'Female' if self.gender_id == 0 else 'Male'
  32. def __repr__(self) -> str:
  33. parts = [f'Face(confidence={self.confidence:.3f}']
  34. if self.age is not None:
  35. parts.append(f'age={self.age}')
  36. if self.gender_id is not None:
  37. parts.append(f'gender={self.gender}')
  38. if self.embedding is not None:
  39. parts.append(f'embedding_dim={self.embedding.shape[0]}')
  40. return ', '.join(parts) + ')'