根据人脸预测年龄性别和情绪 (python + keras)(三)

人脸面部情绪识别 (一)

人脸面部情绪识别(二)

人脸面部情绪识别 age&gender(三)

根据人脸预测年龄性别和情绪代码实现 (c++ + caffe)(四)

* 背景 *

1、 目前人脸识别已经广泛运用,即使在视频流里面也能流畅识别出来,无论是对安防还是其他体验类产品都有很大的影响。研究完人脸识别后,对于年龄的预测,性别的判断以及根据面部动作识别表情也开始实现,以后可能还会学习颜值预测和是否带眼睛戴帽子什么的。面部表情识别技术主要的应用领域包括人机交互、智能控制、安全、医疗、通信等领域。颜值预测可以运用于未来的虚拟化妆,客户可以看见化妆后的自己,满意后再实际化妆出来的效果最能让客户开心。

实现

  • 在哪里实现?

    第一,在视频流里实时识别,人脸识别的人脸对齐过程实现,人脸检测完之后将检测结果传参给预测模型。

    第二、直接给图片先检测再预测

  • 代码实现
    demo.py

import os
import cv2
import time
import numpy as np
import argparse
import dlib
from contextlib import contextmanager
from wide_resnet import WideResNet
from keras.utils.data_utils import get_file
from keras.models import model_from_json

pretrained_model = "https://github.com/yu4u/age-gender-estimation/releases/download/v0.5/weights.18-4.06.hdf5"
modhash = '89f56a39a78454e96379348bddd78c0d'

emotion_labels = ['angry', 'fear', 'happy', 'sad', 'surprise', 'neutral']

# load json and create model arch 
json_file = open('model.json','r')
loaded_model_json = json_file.read()
json_file.close()
#将json重构为model结构
model = model_from_json(loaded_model_json)

# load weights into new model
model.load_weights('model.h5')

def predict_emotion(face_image_gray): # a single cropped face
    resized_img = cv2.resize(face_image_gray, (48,48), interpolation = cv2.INTER_AREA)

    image = resized_img.reshape(1, 1, 48, 48)
    im = cv2.resize(resized_img,(90,100))
    cv2.imwrite('face.bmp', im)
    list_of_list = model.predict(image, batch_size=1, verbose=1)
    angry, fear, happy, sad, surprise, neutral = [prob for lst in list_of_list for prob in lst]
    return [angry, fear, happy, sad, surprise, neutral]


def get_args():
    parser = argparse.ArgumentParser(description="This script detects faces from web cam input, "
                                                 "and estimates age and gender for the detected faces.",
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    #改成自己的地址                                 
    parser.add_argument("--weight_file", type=str, default="./pretrained_models/weights.18-4.06.hdf5",
                        help="path to weight file (e.g. weights.18-4.06.hdf5)")
    parser.add_argument("--depth", type=int, default=16,
                        help="depth of network")
    parser.add_argument("--width", type=int, default=8,
                        help="width of network")
    args = parser.parse_args()
    return args


def draw_label(image, point, label, font=cv2.FONT_HERSHEY_SIMPLEX,
               font_scale=1, thickness=2):
    size = cv2.getTextSize(label, font, font_scale, thickness)[0]
    x, y = point
    cv2.rectangle(image, (x, y - size[1]), (x + size[0], y), (255, 0, 0), cv2.FILLED)
    cv2.putText(image, label, point, font, font_scale, (255, 255, 255), thickness)


@contextmanager
def video_capture(*args, **kwargs):
    cap = cv2.VideoCapture(*args, **kwargs)
    try:
        yield cap
    finally:
        cap.release()


def yield_images():
    # capture video
    with video_capture(0) as cap:
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

        while True:
            # get video frame
            ret, img = cap.read()

            if not ret:
                raise RuntimeError("Failed to capture image")

            yield img


def main():
    biaoqing = ""
    args = get_args()
    depth = args.depth
    k = args.width
    weight_file = args.weight_file
    print(weight_file)
    #第一次运行时会自动从给的网址下载weights.18-4.06.hdf5模型(190M左右)
    if not weight_file:
        weight_file = get_file("weights.18-4.06.hdf5", pretrained_model, cache_subdir="pretrained_models",
                               file_hash=modhash, cache_dir=os.path.dirname(os.path.abspath(__file__)))

    # for face detection
    detector = dlib.get_frontal_face_detector()

    # load model and weights
    img_size = 64
    model = WideResNet(img_size, depth=depth, k=k)()
    model.load_weights(weight_file)


    for img in yield_images():
        #img = cv2.imread("1.jpg")
        input_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img_h, img_w, _ = np.shape(input_img)
        #print("h w ",img_h,img_w)

        emotions = []
        # Draw a rectangle around the faces


        # detect faces using dlib detector
        detected = detector(img_gray, 0)
        faces = np.empty((len(detected), img_size, img_size, 3))
        #print("dector",detected)

        if len(detected) > 0:
            for i, d in enumerate(detected):
                #print("i,d =",i,d)
                x1, y1, x2, y2, w, h = d.left(), d.top(), d.right() + 1, d.bottom() + 1, d.width(), d.height()
                #print("w h =",w,h)
                xw1 = max(int(x1 - 0.4 * w), 0)
                yw1 = max(int(y1 - 0.4 * h), 0)
                xw2 = min(int(x2 + 0.4 * w), img_w - 1)
                yw2 = min(int(y2 + 0.4 * h), img_h - 1)
                cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0), 2)
                #cv2.rectangle(img, (xw1, yw1), (xw2, yw2), (255, 0, 0), 2)
                faces[i, :, :, :] = cv2.resize(img[yw1:yw2 + 1, xw1:xw2 + 1, :], (img_size, img_size))
                #print("faces  ",faces)
                face_image_gray = img_gray[y1:y1 + y2, x1:x1 + x2]
                angry, fear, happy, sad, surprise, neutral = predict_emotion(face_image_gray)
                emotions = [angry, fear, happy, sad, surprise, neutral]
                m = emotions.index(max(emotions))

                for index, val in enumerate(emotion_labels):
                    if (m == index):
                        biaoqing = val

            # predict ages and genders of the detected faces
            results = model.predict(faces)
            predicted_genders = results[0]
            ages = np.arange(0, 101).reshape(101, 1)
            predicted_ages = results[1].dot(ages).flatten()

            # draw results
            for i, d in enumerate(detected):
                #print("表情",biaoqing)
                label = "{}, {},{}".format(int(predicted_ages[i]),
                                        "F" if predicted_genders[i][0] > 0.5 else "M" ,biaoqing)
                draw_label(img, (d.left(), d.top()), label)

        cv2.imshow("result", img)
        #等待3ms
        key = cv2.waitKey(3)

        if key == 27:
            break


if __name__ == '__main__':
    main()

wide_resnet.py

# This code is imported from the following project: https://github.com/asmith26/wide_resnets_keras

import logging
import sys
import numpy as np
from keras.models import Model
from keras.layers import Input, Activation, add, Dense, Flatten, Dropout
from keras.layers.convolutional import Conv2D, AveragePooling2D
from keras.layers.normalization import BatchNormalization
from keras.regularizers import l2
from keras import backend as K

sys.setrecursionlimit(2 ** 20)
np.random.seed(2 ** 10)


class WideResNet:
    def __init__(self, image_size, depth=16, k=8):
        self._depth = depth
        self._k = k
        self._dropout_probability = 0
        self._weight_decay = 0.0005
        self._use_bias = False
        self._weight_init = "he_normal"

        if K.image_dim_ordering() == "th":
            logging.debug("image_dim_ordering = 'th'")
            self._channel_axis = 1
            self._input_shape = (3, image_size, image_size)
        else:
            logging.debug("image_dim_ordering = 'tf'")
            self._channel_axis = -1
            self._input_shape = (image_size, image_size, 3)

    # Wide residual network http://arxiv.org/abs/1605.07146
    def _wide_basic(self, n_input_plane, n_output_plane, stride):
        def f(net):
            # format of conv_params:
            #               [ [kernel_size=("kernel width", "kernel height"),
            #               strides="(stride_vertical,stride_horizontal)",
            #               padding="same" or "valid"] ]
            # B(3,3): orignal <<basic>> block
            conv_params = [[3, 3, stride, "same"],
                           [3, 3, (1, 1), "same"]]

            n_bottleneck_plane = n_output_plane

            # Residual block
            for i, v in enumerate(conv_params):
                if i == 0:
                    if n_input_plane != n_output_plane:
                        net = BatchNormalization(axis=self._channel_axis)(net)
                        net = Activation("relu")(net)
                        convs = net
                    else:
                        convs = BatchNormalization(axis=self._channel_axis)(net)
                        convs = Activation("relu")(convs)

                    convs = Conv2D(n_bottleneck_plane, kernel_size=(v[0], v[1]),
                                          strides=v[2],
                                          padding=v[3],
                                          kernel_initializer=self._weight_init,
                                          kernel_regularizer=l2(self._weight_decay),
                                          use_bias=self._use_bias)(convs)
                else:
                    convs = BatchNormalization(axis=self._channel_axis)(convs)
                    convs = Activation("relu")(convs)
                    if self._dropout_probability > 0:
                        convs = Dropout(self._dropout_probability)(convs)
                    convs = Conv2D(n_bottleneck_plane, kernel_size=(v[0], v[1]),
                                          strides=v[2],
                                          padding=v[3],
                                          kernel_initializer=self._weight_init,
                                          kernel_regularizer=l2(self._weight_decay),
                                          use_bias=self._use_bias)(convs)

            # Shortcut Connection: identity function or 1x1 convolutional
            #  (depends on difference between input & output shape - this
            #   corresponds to whether we are using the first block in each
            #   group; see _layer() ).
            if n_input_plane != n_output_plane:
                shortcut = Conv2D(n_output_plane, kernel_size=(1, 1),
                                         strides=stride,
                                         padding="same",
                                         kernel_initializer=self._weight_init,
                                         kernel_regularizer=l2(self._weight_decay),
                                         use_bias=self._use_bias)(net)
            else:
                shortcut = net

            return add([convs, shortcut])

        return f


    # "Stacking Residual Units on the same stage"
    def _layer(self, block, n_input_plane, n_output_plane, count, stride):
        def f(net):
            net = block(n_input_plane, n_output_plane, stride)(net)
            for i in range(2, int(count + 1)):
                net = block(n_output_plane, n_output_plane, stride=(1, 1))(net)
            return net

        return f

#    def create_model(self):
    def __call__(self):
        logging.debug("Creating model...")

        assert ((self._depth - 4) % 6 == 0)
        n = (self._depth - 4) / 6

        inputs = Input(shape=self._input_shape)

        n_stages = [16, 16 * self._k, 32 * self._k, 64 * self._k]

        conv1 = Conv2D(filters=n_stages[0], kernel_size=(3, 3),
                              strides=(1, 1),
                              padding="same",
                              kernel_initializer=self._weight_init,
                              kernel_regularizer=l2(self._weight_decay),
                              use_bias=self._use_bias)(inputs)  # "One conv at the beginning (spatial size: 32x32)"

        # Add wide residual blocks
        block_fn = self._wide_basic
        conv2 = self._layer(block_fn, n_input_plane=n_stages[0], n_output_plane=n_stages[1], count=n, stride=(1, 1))(conv1)
        conv3 = self._layer(block_fn, n_input_plane=n_stages[1], n_output_plane=n_stages[2], count=n, stride=(2, 2))(conv2)
        conv4 = self._layer(block_fn, n_input_plane=n_stages[2], n_output_plane=n_stages[3], count=n, stride=(2, 2))(conv3)
        batch_norm = BatchNormalization(axis=self._channel_axis)(conv4)
        relu = Activation("relu")(batch_norm)

        # Classifier block
        pool = AveragePooling2D(pool_size=(8, 8), strides=(1, 1), padding="same")(relu)
        flatten = Flatten()(pool)
        predictions_g = Dense(units=2, kernel_initializer=self._weight_init, use_bias=self._use_bias,
                              kernel_regularizer=l2(self._weight_decay), activation="softmax",
                              name="pred_gender")(flatten)
        predictions_a = Dense(units=101, kernel_initializer=self._weight_init, use_bias=self._use_bias,
                              kernel_regularizer=l2(self._weight_decay), activation="softmax",
                              name="pred_age")(flatten)
        model = Model(inputs=inputs, outputs=[predictions_g, predictions_a])

        return model


def main():
    model = WideResNet(64)()
    model.summary()


if __name__ == '__main__':
    main()

准备工作

 环境:python3  TensorFlow-gpu  numpy  keras  dlib
 模型:model.h5(表情预测模型)  model.json(表情预测模型的json类型)  weights.18-4.06.hdf5(性别年龄预测模型)
[模型下载](https://download.csdn.net/download/hpymiss/10490349)

运行

python demo.py

效果

这里写图片描述

处理一帧一秒以内,视频流里能流畅运行
不足之处:模型的精度还不够,需要进行微调,如何改进还待研究

硬件

  • GPU:
    name: GeForce GTX 960M major: 5 minor: 0 memoryClockRate(GHz): 1.176
    pciBusID: 0000:02:00.0
    totalMemory: 4.00GiB freeMemory: 3.34GiB
  • 处理器 (i7)

学习参考
keras官方文档
参考代码以及model.h5下载
年龄性别预测
彻底理解Python中的yield
Keras 实现的性别年龄检测 (已并入颜值服务)
keras系列︱人脸表情分类与识别:opencv人脸检测+Keras情绪分类(四)

  • 11
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
Python Keras是一个优秀的深度学习库,可以帮助我们进行图像识别任务。要使用Python Keras进行性别识别,我们可以采取以下步骤: 1. 数据集准备:首先,我们需要准备一个标记了男性和女性人脸图像的数据集。该数据集应该包括一系列预处理过的面部图像,以及相应的性别标签。 2. 数据预处理:接下来,我们需要对数据进行预处理,以便于神经网络模型的训练。这包括将图像转换为标准的尺寸,并对图像进行归一化处理,以便于提高训练效果。 3. 构建模型:使用Keras库,我们可以构建一个深度学习模型。模型可以包含多个卷积层、池化层和全连接层,以便于从图像中提取特征,并进行性别分类。 4. 模型训练:将准备好的数据集输入到模型中,通过迭代的方式训练模型。在训练过程中,我们可以调整超参数、监控损失函数并评估模型的性能。 5. 测试和预测:当模型训练完毕后,可以使用训练好的模型进行预测。输入一个未知的人脸图像,模型将输出一个概率值,表示该人脸属于男性或女性的概率。 6. 模型优化:根据预测结果,我们可以进一步优化模型,以提高性别识别的准确率。可以尝试调整网络结构、增加训练数据量、应用数据增强等方法。 综上所述,通过Python Keras可以使用深度学习技术来实现人脸性别识别。该方法可以通过构建神经网络模型并进行训练和优化来实现准确的性别分类。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值