深度学习网络模型部署——简单的表情识别demo演示

实现从项目调研、数据收集、数据预处理、深度卷积神经网络训练再到服务器部署的人脸表情识别小项目

主要分为两个部分展开:图片检测视频检测
检测部分分为两个方面:人脸检测表情识别

先呈上一张效果图(果然,包包越贵越自信hhh):

效果图

一、数据预处理

在数据预处理上主要有:

图像预处理一般有数字化,归一化,几何变化,平滑,复原和增强等步骤。

在检测边缘前,通常会进行灰度化处理,噪声处理,二值化,开闭运算等。

二、图片检测

首先,运行所需要的库:

import sys
import numpy as np
import dlib
import cv2
import os

以及所需要的检测68个关键点参数模型:shape_predictor_68_face_landmarks.dat
链接:https://pan.baidu.com/s/1Xkh17Wdmn9dfx33yA8ghRw
提取码:rxwh
人物表情检测参数模型(这个是我自己训练的,准确率有待参考~):my_merge_Xception_0807.61-0.669.hdf5
链接:https://pan.baidu.com/s/1zviLruPE3wG4BFQhNSJsyA
提取码:rg3d

当然你也可以运用自己训练好的模型参数。

2.1、检测人脸区域,并标上68个点,按区域连线

导入已经训练好的模型,以及相关变量,人脸表情方面检测主要有7种基本表情:"angry", "disgust", "scared", "happy", "sad", "surprised", "neutral";

image_file = "image/5.jpg"
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("face_landmarks/shape_predictor_68_face_landmarks.dat")
emotion_model_path = 'models/my_merge_Xception_0807.61-0.669.hdf5'
emotion_classifier = load_model(emotion_model_path, compile=False)
EMOTIONS = ["angry", "disgust", "scared", "happy", "sad", "surprised", "neutral"]

读取图片并显示:

    image = cv2.imread(image_file)
    cv2.imshow("origin", image)

处理部分,将图片进行resize处理:

#这个函数里的image就是我们要检测的图片。
#在人脸检测程序的最后,我们会显示检测的结果图片来验证,这里做resize是为了避免图片过大,超出屏幕范围。
def resize(image, width=1200):
    r = width * 1.0 / image.shape[1]
    dim = (width, int(image.shape[0] * r))
    resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
    return resized

调用人脸检测模型,在检测之前,作了一个图片灰度处理;

灰度处理原因:
1、最直白的原因是,彩色图像单个像素是(R, G, B),转换成灰度图就是(L),简化矩阵, 提高运算速度。

2、首先,梯度信息对于识别物体来说很重要。所以我们可以把灰度图像看作图像的强度(Intensity),来求一些梯度特征。比较常用的有 HOG,LBP,SIFT等等。如下图为行人检测中的hog模型。通过hog来检测部件,最后找到图像中的行人。(Reference: rbgirshick/voc-dpm)注:可视化为rgb图像,但输入是灰度图像,不要被骗了。

现行很多算法是需要将彩色图像灰度化再进行处理的。其实你要想明白,灰度化之后失去了什么,又得到了什么。。

灰度化之后颜色信息丢失,很多color-based算法就不可能这么做,但是很多简单的识别算法对于颜色的依赖性不强,hand-craft特征更多关注边缘梯度信息。工程中很多应用加上color信息之后鲁棒性会下降。灰度化之后矩阵维数下降,运算速度大幅度提高,并且梯度信息仍然保留。就是在performance和efficiency之间做一个权衡罢了。

链接:https://www.zhihu.com/question/24453478
来源:知乎

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    rects = detector(gray, 1)

接着检测问题说,当我检测出一张图片上所有人脸的相关信息(都存储在rects);这时我们可以根据关键点的得到相关人脸区域图片,便于后面表情识别。

首先,先来看看,如何将每张人脸的关键点标出来(本次是将68个点按区域连接起来的),在检测到的人脸区域中,进一步检测器官(眼睛、鼻子、嘴巴、下巴、眉毛)。

将相关区域进行划分;

face_outline = []
        face_eye_left = []
        face_eye_right = []
        face_eyebrow_left = []
        face_eyebrow_right = []
        face_nosepiece = []
        face_nose = []
        face_mouth = []
        face_lip = []
        i = 0
        for (x, y) in shape:
            i = i + 1
            # print(i, x, y)
            if i >= 1 and i <= 17:
                # print("face_outline")
                face_outline.append((x, y))
            if i >= 18 and i <= 22:
                face_eyebrow_left.append((x, y))
            if i >= 23 and i <= 27:
                face_eyebrow_right.append((x, y))
            if i >= 37 and i <= 42:
                face_eye_left.append((x, y))
            if i >= 43 and i <= 48:
                face_eye_right.append((x, y))
            if i >= 28 and i <= 31:
                face_nosepiece.append((x, y))
            if i >= 32 and i <= 36:
                face_nose.append((x, y))
            if i >= 49 and i <= 60:
                face_mouth.append((x, y))
            if i >= 61 and i <= 68:
                face_lip.append((x, y))

进行关键点之间连线;

color = (225, 225, 225)
thickness = 1
def draw_line(image, pixls):
    i = 0
    # print(pixls)
    for x, y in pixls:
        if i < len(pixls)-1:
            next_x, next_y = pixls[i+1]
            cv2.line(image, (x, y), (next_x, next_y), color, thickness)
        # else:
        #     next_x, next_y = pixls[0]
        #     cv2.line(image, (x, y), (next_x, next_y), (225, 225, 225), 2)
        i = i + 1
    draw_line(image, face_outline)
    draw_line(image, face_eyebrow_left)
    draw_line(image, face_eyebrow_right)
    draw_line(image, face_eye_left)
    cv2.line(image, face_eye_left[0], face_eye_left[-1], color, thickness)
    draw_line(image, face_eye_right)
    cv2.line(image, face_eye_right[0], face_eye_right[-1], color, thickness)
    draw_line(image, face_nose)
    draw_line(image, face_nosepiece)
    cv2.line(image, face_nose[0], face_nosepiece[-1], color, thickness)
    cv2.line(image, face_nose[-1], face_nosepiece[-1], color, thickness)
    draw_line(image, face_mouth)
    cv2.line(image, face_mouth[0], face_mouth[-1], color, thickness)
    draw_line(image, face_lip)
    cv2.line(image, face_lip[0], face_lip[-1], color, thickness)

示意图:
原图
效果图

2.2、表情识别

导入资源包:

from tensorflow.keras.models import load_model
from keras.preprocessing.image import img_to_array

dlib脸部特征检测的输出,一个shape里包含了前面说到的脸部特征的68个点

     shape = predictor(gray, rect)
     shape = shape_to_np(shape)
# 这个函数将shape转换成Numpy array,为方便后续处理。
def shape_to_np(shape, dtype="int"):
    coords = np.zeros((68, 2), dtype=dtype)
    for i in range(0, 68):
            coords[i] = (shape.part(i).x, shape.part(i).y)
    return coords

取出68个关键点中max_left,max_right, max_top, max_bottom,进行裁剪。

     left, right, top, bottom = cope(shape)
     cropped = gray[top:bottom, left:right]  # 裁剪坐标为[y0:y1, x0:x1]
def cope(shape):
    x, y = shape[0]
    min_left = x
    max_right = x
    min_top = y
    max_bottom = y
    for (x, y) in shape:
        if min_left > x:
            min_left = x
        if max_right < x:
            max_right = x
        if min_top > y:
            min_top = y
        if max_bottom < y:
            max_bottom = y

    print(min_left,max_right,min_top,max_bottom)
    return min_left, max_right, min_top, max_bottom

将裁剪得到的单通道图像进行预处理,resize,Normalization等。

def preprocess_input(x, v2=True):
    # 归一化
    x = x.astype('float32')
    x = x / 255.0
    if v2:
        x = x - 0.5
        x = x * 2.0
    return x
        roi = cv2.resize(cropped, emotion_classifier.input_shape[1:3])
        roi = preprocess_input(roi)
        roi = img_to_array(roi)
        roi = np.expand_dims(roi, axis=0)

各个表情分类及表情概率。

        # 用模型预测各分类的概率
        preds = emotion_classifier.predict(roi)[0]
        emotion_probability = np.max(preds)  # 最大的概率
        label = EMOTIONS[preds.argmax()]  # 选取最大概率的表情类

圈出人脸区域并显示识别结果。

 cv2.putText(image,
                    "Face{}".format(i + 1) + ", " + label + ": " + str(round(emotion_probability * 100, 2)) + "%",
                    (left - 10, top - 10),
                    cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 225, 225), 1)  # putText各参数依次是:图片,添加的
效果图

另外,如果是多个人脸检测,只需要在外层加一个循环就行啦。
多人效果图

三、视频检测

视频检测其实就是将视频里面每一帧图片进行检测,其中人脸检测与表情识别都与图片检测方法一样(可以参考上面图片检测)。

import numpy as np
import dlib
import cv2

from tensorflow.keras.models import load_model
from keras.preprocessing.image import img_to_array

cv2.VideoCapture调用笔记本内置摄像头,所以参数为0,如果有其他的摄像头可以调整参数为1,2,也可以为视频路径;

cap=cv2.VideoCapture("image/7.mp4")
fps = cap.get(cv2.CAP_PROP_FPS) # 调用cv2方法获取cap的视频帧(帧:每秒多少张图片)
# 获取cap视频流的每帧大小
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
        int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 定义编码格式mpge-4
# 一种视频格式,参数搭配固定,不同的编码格式对应不同的参数
fourcc = cv2.VideoWriter_fourcc(*"XVID")
# 定义视频文件输入对象
outVideo = cv2.VideoWriter("output.avi", fourcc, fps, size) #第一个参数是保存视频文件的绝对路径

读取每一帧图片:

sucess, image = cap.read()

效果演示:
片段来自于电影《闻香识女人》

未完待续......

你可能感兴趣的:(深度学习网络模型部署——简单的表情识别demo演示)