Source code for lunavl.sdk.estimators.face_estimators.basic_attributes

"""Module contains a basic attributes estimator.

See `basic attributes`_.
"""
from enum import Enum
from typing import Union

from FaceEngine import IAttributeEstimatorPtr, AttributeRequest, AttributeResult  # pylint: disable=E0611,E0401
from FaceEngine import EthnicityEstimation, Ethnicity as CoreEthnicity  # pylint: disable=E0611,E0401
from lunavl.sdk.errors.errors import LunaVLError
from lunavl.sdk.errors.exceptions import CoreExceptionWarp, LunaSDKException

from lunavl.sdk.estimators.base_estimation import BaseEstimator, BaseEstimation
from lunavl.sdk.estimators.face_estimators.warper import Warp, WarpedImage


[docs]class Ethnicity(Enum): """ Enum for ethnicities. """ #: african american AfricanAmerican = 1 #: asian Asian = 2 #: indian Indian = 3 #: caucasian Caucasian = 4 @staticmethod def fromCoreEmotion(coreEthnicity: CoreEthnicity) -> 'Ethnicity': """ Get enum element by core ethnicity. Args: coreEthnicity: core ethnicity Returns: corresponding ethnicity """ return getattr(Ethnicity, coreEthnicity.name) def __str__(self): """ Convert enum element to string. Returns: snake case ethnicity """ if self in (Ethnicity.Asian, Ethnicity.Indian, Ethnicity.Caucasian): # pylint: disable=E1101 return self.name.lower() return "african_american"
[docs]class Ethnicities(BaseEstimation): """ Class for ethnicities estimation. Estimation properties: - asian - indian - caucasian - africanAmerican - predominateEmotion """ # pylint: disable=W0235 def __init__(self, coreEstimation: EthnicityEstimation): """ Init. Args: coreEstimation: core ethnicities estimation """ super().__init__(coreEstimation) @property def asian(self) -> float: """ Get asian ethnicity value. Returns: value in range [0, 1] """ return self._coreEstimation.asian @property def indian(self): """ Get indian ethnicity value. Returns: value in range [0, 1] """ return self._coreEstimation.indian @property def caucasian(self): """ Get caucasian ethnicity value. Returns: value in range [0, 1] """ return self._coreEstimation.caucasian @property def africanAmerican(self): """ Get african american ethnicity value. Returns: value in range [0, 1] """ return self._coreEstimation.africanAmerican
[docs] def asDict(self) -> dict: """ Convert to dict. Returns: dict in platform format """ return { "predominant_ethnicity": str(self.predominateEmotion), "estimations": { "asian": self.asian, "indian": self.indian, "caucasian": self.caucasian, "african_american": self.africanAmerican } }
@property def predominateEmotion(self) -> Ethnicity: """ Get predominate ethnicity (ethnicity with max score value). Returns: ethnicity with max score value """ return Ethnicity.fromCoreEmotion(self._coreEstimation.getPredominantEthnicity())
[docs]class BasicAttributes(BaseEstimation): """ Class for basic attribute estimation Attributes: age (Optional[float]): age, number in range [0, 100] gender (Optional[float]): gender, number in range [0, 1] ethnicity (Optional[Ethnicities]): ethnicity """ __slots__ = ("ethnicity", 'age', 'gender') # pylint: disable=W0235 def __init__(self, coreEstimation: AttributeResult): """ Init. Args: coreEstimation: core ethnicity estimation """ super().__init__(coreEstimation) if not coreEstimation.ethnicity_opt.isValid(): self.ethnicity = None else: self.ethnicity = Ethnicities(coreEstimation.ethnicity_opt.value()) if not coreEstimation.ethnicity_opt.isValid(): self.age = None else: self.age = coreEstimation.age_opt.value() if not coreEstimation.gender_opt.isValid(): self.gender = None else: self.gender = coreEstimation.gender_opt.value()
[docs] def asDict(self) -> dict: """ Convert to dict. Returns: dict with keys "ethnicity", "gender", "age" """ res = {} if self.ethnicity is not None: res["ethnicities"] = self.ethnicity.asDict() else: res["ethnicities"] = None if self.age is not None: res["age"] = round(self.age) else: res["age"] = None if self.gender is not None: res["gender"] = round(self.gender) else: res["gender"] = None return res
[docs]class BasicAttributesEstimator(BaseEstimator): """ Basic attributes estimator. """ # pylint: disable=W0235 def __init__(self, coreEstimator: IAttributeEstimatorPtr): """ Init. Args: coreEstimator: core estimator """ super().__init__(coreEstimator) # pylint: disable=W0221
[docs] @CoreExceptionWarp(LunaVLError.EstimationBasicAttributeError) def estimate(self, warp: Union[Warp, WarpedImage], estimateAge: bool, estimateGender: bool, estimateEthnicity: bool) -> BasicAttributes: """ Estimate ethnicity. Args: warp: warped image estimateAge: estimate age or not estimateGender: estimate gender or not estimateEthnicity: estimate ethnicity or not Returns: estimated ethnicity Raises: LunaSDKException: if estimation failed """ dtAttributes = 0 if estimateAge: dtAttributes |= AttributeRequest.estimateAge if estimateGender: dtAttributes |= AttributeRequest.estimateGender if estimateEthnicity: dtAttributes |= AttributeRequest.estimateEthnicity error, baseAttributes = self._coreEstimator.estimate(warp.warpedImage.coreImage, AttributeRequest(dtAttributes)) if error.isError: raise LunaSDKException(LunaVLError.fromSDKError(error)) return BasicAttributes(baseAttributes)