deepface icon indicating copy to clipboard operation
deepface copied to clipboard

Feat/batch predict age and gender

Open NatLee opened this issue 1 year ago • 3 comments

Tickets

https://github.com/serengil/deepface/issues/441 https://github.com/serengil/deepface/issues/678 https://github.com/serengil/deepface/issues/1069 https://github.com/serengil/deepface/issues/1101

What has been done

With this PR, new predicts function to support batch predictions.

How to test

Use this class, user can load model and predict in batch.

class AgeGenderModel():
    """
    Age and gender model
    """
    def __init__(self):
        self.target_size = (224, 224)
        self.age_model, self.gender_model = self.load()

    def load(self) -> Tuple:
        age_model = modeling.build_model(task="facial_attribute", model_name="Age")
        gender_model = modeling.build_model(task="facial_attribute", model_name="Gender")
        return age_model, gender_model

    def process_data(self, data: np.ndarray) -> np.ndarray:
        """
        Process input image data
        """
        img_content = data[:, :, ::-1] # rgb to bgr
        img_content = preprocessing.resize_image(
            img=img_content,
            target_size=self.target_size
        )
        return img_content

    def predict(self, data: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """
        Predict age and gender for one input image
        """
        img_content = self.process_data(data)
        age = self.age_model.predict(img_content)
        gender = self.gender_model.predict(img_content)
        return gender, age

    def predicts(self, data: List[np.ndarray]) -> Tuple[List, List]:
        """
        Predict age and gender for batch input images
        """
        img_contents = [self.process_data(img_content) for img_content in data]
        ages = self.age_model.predicts(img_contents)
        genders = self.gender_model.predicts(img_contents)
        return genders, ages 

Time Costs for 10 Images:

Original for-loop prediction: 1.91108 seconds

Loading split Age and Gender models: 0.00002 seconds
Batch prediction: 0.58243 seconds
Single prediction: 0.03749 seconds

Here's my test script:

from deepface import DeepFace
import cv2
import time
# Load one face
img = cv2.imread("./test.png")

# Make it as a list
imgs = [img] * 10

# For-loop predict
start = time.time()
print("=====For-loop Predict=====")
for img in imgs:
    objs = DeepFace.analyze(
    img_path=img,
    actions=['age', 'gender'],
    )
print(f"Time: {time.time() - start:.5f}s")

# Load models
print("=====Load Split Models=====")
start = time.time()
model = AgeGenderModel()
print(f"Time: {time.time() - start:.5f}s")

# Batch Predict
start = time.time()
genders, ages = model.predicts(imgs)
print("=====Batch Predict=====")
print(genders, ages)
print(f"Time: {time.time() - start:.5f}s")

# Single Predict
start = time.time()
gender, age = model.predict(img)
print("=====Single Predict=====")
print(gender, age)
print(f"Time: {time.time() - start:.5f}s")

predicts is placed in age and gender clients to keep the DeepFace.analyze function logic.

Thank you for taking time to go through this feedback. :)

NatLee avatar Dec 06 '24 06:12 NatLee

Bump Really needs this feature.

h-alice avatar Dec 06 '24 06:12 h-alice

I don't support having another predicts function. Instead, you can add that logic under predict.

1- predict accepts both single image and list of images as

img: Union[np.ndarray, List[np.ndarray]]

2- in predict function, you can check the type of img, and redirect it to your logic if it is list as

if isinstance(img, np.ndarray):
   # put old predict logic here
elif isinstance(img, np.ndarray):
   # put your batch processing logic here

3- this new logic is worth to have its own unit tests. possibly, you can add some unit tests here.

4- return type of predict should be Union[np.float64, np.ndarray]

5- You should also update the interface in DeepFace.py

serengil avatar Dec 06 '24 10:12 serengil

Actions failed because of linting - link

************* Module deepface.models.demography.Age
pylint: Command line or configuration file:1: UserWarning: 'Exception' is not a proper value for the 'overgeneral-exceptions' option. Use fully qualified name (maybe 'builtins.Exception' ?) instead. This will cease to be checked at runtime in 3.1.0.
deepface/models/demography/Age.py:70:0: C0303: Trailing whitespace (trailing-whitespace)
************* Module deepface.models.demography.Emotion
deepface/models/demography/Emotion.py:88:0: C0303: Trailing whitespace (trailing-whitespace)

serengil avatar Dec 31 '24 09:12 serengil

I am not available to review it until early Feb.

serengil avatar Jan 22 '25 10:01 serengil

Tests failed

serengil avatar Jan 23 '25 12:01 serengil

The test seems passed. If you're okay, it's ready to merge.

NatLee avatar Feb 05 '25 20:02 NatLee

LGTM

thank you for your contribution

serengil avatar Feb 16 '25 19:02 serengil

Thank you so much @serengil !

h-alice avatar Feb 17 '25 02:02 h-alice

@serengil Many thanks for your reviews!

NatLee avatar Feb 17 '25 02:02 NatLee