diff options
Diffstat (limited to 'megapixels/app/processors/face_age_gender.py')
| -rw-r--r-- | megapixels/app/processors/face_age_gender.py | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/megapixels/app/processors/face_age_gender.py b/megapixels/app/processors/face_age_gender.py new file mode 100644 index 00000000..95efa8fc --- /dev/null +++ b/megapixels/app/processors/face_age_gender.py @@ -0,0 +1,116 @@ +''' +Uses models from: https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/ + +@article{Rothe-IJCV-2016, + author = {Rasmus Rothe and Radu Timofte and Luc Van Gool}, + title = {Deep expectation of real and apparent age from a single image without facial landmarks}, + journal = {International Journal of Computer Vision (IJCV)}, + year = {2016}, + month = {July}, +} +''' + +import os +from os.path import join +from pathlib import Path +import math + +import cv2 as cv +import numpy as np +import imutils + +from app.utils import im_utils, logger_utils +from app.models.bbox import BBox +from app.settings import app_cfg as cfg +from app.settings import types + + + +class _FaceAgeGender: + '''Predicts age from face crop + https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/ + ''' + + dnn_size = (224,224) + dnn_mean = (104.0, 177.0, 123.0) + ages = np.arange(0, 101).reshape(101, 1) + + def __init__(self, fp_prototxt, fp_model): + self.log = logger_utils.Logger.getLogger() + self.net = cv.dnn.readNetFromCaffe(fp_prototxt, fp_model) + + def _preprocess(self, im, bbox_dim): + # isolate face ROI, expand bbox by 40% according to authors + # https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/ + dim = im.shape[:2][::-1] + bbox_dim_exp = bbox_dim.expand_dim( int(0.4*bbox_dim.width), dim) + roi = bbox_dim_exp.to_xyxy() + im_face_crop = im[roi[1]:roi[3], roi[0]:roi[2]] # isolate face roi + + # resize for blob + im_resized = cv.resize(im_face_crop, self.dnn_size) + blob = cv.dnn.blobFromImage(im_resized, 1.0, self.dnn_size, self.dnn_mean) + return blob + +class FaceGender(_FaceAgeGender): + + # use "apparent" age models + fp_model = join(cfg.DIR_MODELS_CAFFE, 'gender', 'gender.caffemodel') + fp_prototxt = join(cfg.DIR_MODELS_CAFFE, 'gender', 'gender.prototxt') + + def __init__(self): + super().__init__(self.fp_prototxt, self.fp_model) + + def predict(self, im, bbox_dim): + '''Predicts gender from face crop + :param im: (numpy.ndarray) BGR image + :param bbox_dim: (BBox) dimensioned + :returns (dict) with scores for male and female + ''' + im_blob = self._preprocess(im, bbox_dim) + self.net.setInput(im_blob) + preds = self.net.forward()[0] + return {'f': preds[0], 'm': preds[1]} + +class FaceAgeApparent(_FaceAgeGender): + + # use "apparent" age models + fp_model = join(cfg.DIR_MODELS_CAFFE, 'age_apparent', 'dex_chalearn_iccv2015.caffemodel') + fp_prototxt = join(cfg.DIR_MODELS_CAFFE, 'age_apparent', 'age.prototxt') + + def __init__(self): + super().__init__(self.fp_prototxt, self.fp_model) + + def predict(self, im, bbox_dim): + '''Predicts apparent age from face crop + :param im: (numpy.ndarray) BGR image + :param bbox_dim: (BBox) dimensioned + :returns (float) predicted age + ''' + im_blob = self._preprocess(im, bbox_dim) + self.net.setInput(im_blob) + preds = self.net.forward()[0] + age = preds.dot(self.ages).flatten()[0] + return age + + +class FaceAgeReal(_FaceAgeGender): + + # use "real" age models + fp_model = join(cfg.DIR_MODELS_CAFFE, 'age_real', 'dex_imdb_wiki.caffemodel') + fp_prototxt = join(cfg.DIR_MODELS_CAFFE, 'age_real', 'age.prototxt') + + def __init__(self): + super().__init__(self.fp_prototxt, self.fp_model) + + def predict(self, im, bbox_dim): + '''Predicts apparent age from face crop + :param im: (numpy.ndarray) BGR image + :param bbox_dim: (BBox) dimensioned + :returns (float) predicted age + ''' + im_blob = self._preprocess(im, bbox_dim) + self.net.setInput(im_blob) + preds = self.net.forward()[0] + age = preds.dot(self.ages).flatten()[0] + return age
\ No newline at end of file |
