summaryrefslogtreecommitdiff
path: root/megapixels/app/processors/face_age_gender.py
blob: 95efa8fcbbb701f9b60c15e212745df7e3679bef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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