人臉表情識別系統介紹——上篇(python實現,含UI界面及完整代碼)


功能演示動圖

摘要:這篇博文介紹基於深度卷積神經網絡實現的人臉表情識別系統,系統程序由Keras, OpenCv, PyQt5的庫實現,訓練測試集采用fer2013表情庫。如圖系統可通過攝像頭獲取實時畫面並識別其中的人臉表情,也可以通過讀取圖片識別,本文提供完整的程序文件並詳細介紹其實現過程。博文要點如下:

點擊跳轉至文末博文涉及的全部文件下載頁

下載鏈接:博主在面包多網站上的完整資源下載頁

人臉表情識別介紹與演示視頻鏈接https://www.bilibili.com/video/BV18C4y1H7mH/


1. 前言

    在這個人工智能成為超級大熱門的時代,人臉表情識別已成為其中的一項研究熱點,而卷積神經網絡、深度信念網絡和多層感知器等相關算法在人臉面部表情識別領域的運用最為廣泛。面部的表情中包含了太多的信息,輕微的表情變化都會反映出人心理的變化,可想而知如果機器能敏銳地識別人臉中表達的情感該是多么令人興奮的事。

    當前深度學習發展迅速,關於表情識別IEEE上面有許多質量很高的文章,里面介紹的是利用深度神經網絡實現的面部表情識別,可以學習和參考。於是自己動手做了這個項目,這里特此將前期工作作個總結,希望能給類似工作的朋友帶來一點幫助。這里使用的是已有的模型——如今CNN的主流框架之mini_XCEPTION,該模型性能也已是不錯的了,關於更高性能模型以后更新,后面也會分享給大家,敬請關注。

功能演示動圖

2. 表情識別數據集

    目前,現有的公開的人臉表情數據集比較少,並且數量級比較小。比較有名的廣泛用於人臉表情識別系統的數據集Extended Cohn-Kanada (CK+)是由P.Lucy收集的。CK+數據集包含123 個對象的327 個被標記的表情圖片序列,共分為正常、生氣、蔑視、厭惡、恐懼、開心和傷心七種表情。對於每一個圖片序列,只有最后一幀被提供了表情標簽,所以共有327 個圖像被標記。為了增加數據,我們把每個視頻序列的最后三幀圖像作為訓練樣本。這樣CK+數據總共被標記的有981 張圖片。這個數據庫是人臉表情識別中比較流行的一個數據庫,很多文章都會用到這個數據做測試,可通過下面的鏈接下載。
官網鏈接:The Extended Cohn-Kanade Dataset(CK+)
網盤鏈接:百度網盤下載(提取碼:8r15

功能演示動圖

    KaggleKaggle人臉表情分析比賽提供的一個數據集。該數據集含28709 張訓練樣本,3859 張驗證數據集和3859 張測試樣本,共35887 張包含生氣、厭惡、恐懼、高興、悲傷、驚訝和正常七種類別的圖像,圖像分辨率為48×48。該數據集中的圖像大都在平面和非平面上有旋轉,並且很多圖像都有手、頭發和圍巾等的遮擋物的遮擋。該數據庫是2013年Kaggle比賽的數據,由於這個數據庫大多是從網絡爬蟲下載的,存在一定的誤差性。這個數據庫的人為准確率是65%±5%
官網鏈接:FER2013
網盤鏈接:百度網盤下載(提取碼:t7xj

功能演示動圖

    由於FER2013數據集數據更加齊全,同時更加符合實際生活的場景,所以這里主要選取FER2013訓練和測試模型。為了防止網絡過快地過擬合,可以人為的做一些圖像變換,例如翻轉,旋轉,切割等。上述操作稱為數據增強。數據操作還有另一大好處是擴大數據庫的數據量,使得訓練的網絡魯棒性更強。下載數據集保存在fer2013的文件夾下,為了對數據集進行處理,采用如下代碼載入和進行圖片預處理:

import pandas as pd
import cv2
import numpy as np

dataset_path = 'fer2013/fer2013/fer2013.csv' # 文件保存位置
image_size=(48,48) # 圖片大小

# 載入數據
def load_fer2013():
        data = pd.read_csv(dataset_path)
        pixels = data['pixels'].tolist()
        width, height = 48, 48
        faces = []
        for pixel_sequence in pixels:
            face = [int(pixel) for pixel in pixel_sequence.split(' ')]
            face = np.asarray(face).reshape(width, height)
            face = cv2.resize(face.astype('uint8'),image_size)
            faces.append(face.astype('float32'))
        faces = np.asarray(faces)
        faces = np.expand_dims(faces, -1)
        emotions = pd.get_dummies(data['emotion']).as_matrix()
        return faces, emotions

# 將數據歸一化
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

    載入數據后將數據集划分為訓練集和測試集,在程序中調用上面的函數代碼如下:

from load_and_process import load_fer2013
from load_and_process import preprocess_input
from sklearn.model_selection import train_test_split

# 載入數據集
faces, emotions = load_fer2013()
faces = preprocess_input(faces)
num_samples, num_classes = emotions.shape

# 划分訓練、測試集
xtrain, xtest,ytrain,ytest = train_test_split(faces, emotions,test_size=0.2,shuffle=True)

3. 搭建表情識別的模型

    接下來就是搭建表情識別的模型了,這里用到的是CNN的主流框架之mini_XCEPTIONXCEPTIONGoogleInception后提出的對Inception v3的另一種改進,主要是采用深度可分離的卷積(depthwise separable convolution)來替換原來Inception v3中的卷積操作。XCEPTION的網絡結構在ImageNet數據集(Inception v3的設計解決目標)上略優於Inception v3,並且在包含3.5億個圖像甚至更大的圖像分類數據集上明顯優於Inception v3,而兩個結構保持了相同數目的參數,性能增益來自於更加有效地使用模型參數,詳細可參考論文:Xception: Deep Learning with Depthwise Separable Convolutions,論文Real-time Convolutional Neural Networks for Emotion and Gender Classification等。

功能演示動圖

    既然這樣的網絡能獲得更好結果又是主流,那當然有必要作為對比算法實現以下了,這里博主模型這部分的代碼引用了GitHub:https://github.com/oarriaga/face_classification中的模型(其他地方也能找到這個模型的類似代碼),模型框圖如上圖所示,其代碼如下:

def mini_XCEPTION(input_shape, num_classes, l2_regularization=0.01):
    regularization = l2(l2_regularization)

    # base
    img_input = Input(input_shape)
    x = Conv2D(8, (3, 3), strides=(1, 1), kernel_regularizer=regularization,
                                            use_bias=False)(img_input)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Conv2D(8, (3, 3), strides=(1, 1), kernel_regularizer=regularization,
                                            use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)

    # module 1
    residual = Conv2D(16, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(16, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(16, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    # module 2
    residual = Conv2D(32, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(32, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(32, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    # module 3
    residual = Conv2D(64, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(64, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(64, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    # module 4
    residual = Conv2D(128, (1, 1), strides=(2, 2),
                      padding='same', use_bias=False)(x)
    residual = BatchNormalization()(residual)

    x = SeparableConv2D(128, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = SeparableConv2D(128, (3, 3), padding='same',
                        kernel_regularizer=regularization,
                        use_bias=False)(x)
    x = BatchNormalization()(x)

    x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
    x = layers.add([x, residual])

    x = Conv2D(num_classes, (3, 3),
            #kernel_regularizer=regularization,
            padding='same')(x)
    x = GlobalAveragePooling2D()(x)
    output = Activation('softmax',name='predictions')(x)

    model = Model(img_input, output)
    return model

4. 數據增強的批量訓練

    神經網絡的訓練需要大量的數據,數據的量決定了網絡模型可以達到的高度,網絡模型盡量地逼近這個高度。然而對於人臉表情的數據來說,都只存在少量的數據Extended Cohn-Kanada (CK+)的數據量是遠遠不夠的,並且CK+多是比較誇張的數據。Kaggle Fer2013數據集也不過只有3萬多數據量,而且有很多遮擋、角度等外界影響因素。既然收集數據要花費很大的人力物力,那么我們就用技術解決這個問題,為避免重復開發首先還是看看有沒有寫好的庫。博主又通讀了遍Keras官方文檔,其中ImageDataGenerator的圖片生成器就可完成這一目標。

為了盡量利用我們有限的訓練數據,我們將通過一系列隨機變換堆數據進行提升,這樣我們的模型將看不到任何兩張完全相同的圖片,這有利於我們抑制過擬合,使得模型的泛化能力更好。在Keras中,這個步驟可以通過keras.preprocessing.image.ImageGenerator來實現,這個類使你可以:在訓練過程中,設置要施行的隨機變換通過.flow或.flow_from_directory(directory)方法實例化一個針對圖像batch的生成器,這些生成器可以被用作keras模型相關方法的輸入,如fit_generator,evaluate_generatorpredict_generator。——Keras官方文檔

    ImageDataGenerator()是一個圖片生成器,同時也可以在batch中對數據進行增強,擴充數據集大小(比如進行旋轉,變形,歸一化等),增強模型的泛化能力。結合前面的模型和數據訓練部分的代碼如下:

"""
Description: 訓練人臉表情識別程序
"""

from keras.callbacks import CSVLogger, ModelCheckpoint, EarlyStopping
from keras.callbacks import ReduceLROnPlateau
from keras.preprocessing.image import ImageDataGenerator
from load_and_process import load_fer2013
from load_and_process import preprocess_input
from models.cnn import mini_XCEPTION
from sklearn.model_selection import train_test_split

# 參數
batch_size = 32
num_epochs = 10000
input_shape = (48, 48, 1)
validation_split = .2
verbose = 1
num_classes = 7
patience = 50
base_path = 'models/'

# 構建模型
model = mini_XCEPTION(input_shape, num_classes)
model.compile(optimizer='adam', # 優化器采用adam
              loss='categorical_crossentropy', # 多分類的對數損失函數
              metrics=['accuracy'])
model.summary()

# 定義回調函數 Callbacks 用於訓練過程
log_file_path = base_path + '_emotion_training.log'
csv_logger = CSVLogger(log_file_path, append=False)
early_stop = EarlyStopping('val_loss', patience=patience)
reduce_lr = ReduceLROnPlateau('val_loss', factor=0.1,
                              patience=int(patience/4),
                              verbose=1)
# 模型位置及命名
trained_models_path = base_path + '_mini_XCEPTION'
model_names = trained_models_path + '.{epoch:02d}-{val_acc:.2f}.hdf5'

# 定義模型權重位置、命名等
model_checkpoint = ModelCheckpoint(model_names,
                                   'val_loss', verbose=1,
                                    save_best_only=True)
callbacks = [model_checkpoint, csv_logger, early_stop, reduce_lr]

# 載入數據集
faces, emotions = load_fer2013()
faces = preprocess_input(faces)
num_samples, num_classes = emotions.shape

# 划分訓練、測試集
xtrain, xtest,ytrain,ytest = train_test_split(faces, emotions,test_size=0.2,shuffle=True)

# 圖片產生器,在批量中對數據進行增強,擴充數據集大小
data_generator = ImageDataGenerator(
                        featurewise_center=False,
                        featurewise_std_normalization=False,
                        rotation_range=10,
                        width_shift_range=0.1,
                        height_shift_range=0.1,
                        zoom_range=.1,
                        horizontal_flip=True)

# 利用數據增強進行訓練
model.fit_generator(data_generator.flow(xtrain, ytrain, batch_size),
                        steps_per_epoch=len(xtrain) / batch_size,
                        epochs=num_epochs,
                        verbose=1, callbacks=callbacks,
                        validation_data=(xtest,ytest))

    以上代碼中設置了訓練時的結果輸出,在訓練結束后會將訓練的模型保存為hdf5文件到自己指定的文件夾下,由於數據量大模型的訓練時間會比較長,建議使用GPU加速。訓練結束后測試得到混淆矩陣如下:

功能演示動圖

    訓練的模型綜合在FER2013數據集上的分類准確率為71%,算是中等偏上水平,其實並非模型不好而是在數據預處理、超參數的選取上有很大的可提升空間,當然也可使用其他的模型,譬如可參考論文:Extended deep neural network for facial emotion recognition,大家可自行研究,這里就不多介紹了。


5. 系統UI界面的實現

    上面的模型訓練好了,但對於我們來說它的作用就只是知道了其准確率還行,其實深度學習的目的最重要還是應用,是時候用上面的模型做點酷酷的東西了。可不可以用上面的模型識別下自己表達的情緒呢?不如做個系統調取攝像頭對實時畫面中的表情進行識別並顯示識別結果,既能可視化的檢測模型的實用性能,同時使得整個項目生動有趣激發自己的創造性,當你向別人介紹你的項目時也顯得高大上。這里采用PyQt5進行設計,首先看一下最后的效果圖,運行后的界面如下:

功能演示動圖

    設計功能:

    (1)可選擇模型文件后基於該模型進行識別;
    (2)打開攝像頭識別實時畫面中的人臉表情;
    (3)選擇一張人臉圖片,對其中的表情進行識別;
    (4)選擇要識別的視頻文件,識別畫面中的表情;

    我們可以選擇一張圖片測試識別效果,如下圖所示:

功能演示動圖

    博主對UI界面的要求是可以簡單但顏值必須高,必須高,實用簡約高顏值是我奉行的標准,以上的界面幾經修改才有了上面的效果。當然博主的目的並不單純的想秀,而是借此做一個測試模型的系統,可以選擇模型、訓練測試集等以便界面化地對后面的模型進行各種測試評估,生成特定結果數據圖或表格等,這個測試系統后面有機會分享給大家。

    系統UI界面的實現這部分又設計PyQt5的許多內容,在這一篇博文中介紹恐怕尾大不掉,效果也不好,所以更多的細節內容將在后面的博文中介紹,敬請期待!有需要的朋友可通過下面的鏈接下載這部分的文件。


下載鏈接
    若您想獲得博文中涉及的實現完整全部程序文件(包括數據集,py, UI文件等,如下圖),這里已打包上傳至博主的面包多下載資源中。文件下載鏈接如下:

功能演示動圖

數據鏈接:訓練用到的數據集(提取碼:t7xj

    本資源已上傳至面包多網站,可以點擊以下鏈接獲取,已將數據集同時打包到里面,點擊即可運行,完整文件下載鏈接如下:

完整資源下載鏈接博主在面包多網站上的完整資源下載頁

人臉表情識別介紹與演示視頻鏈接https://www.bilibili.com/video/BV18C4y1H7mH/

【運行程序須知】

    請配置Python3.7,要安裝的庫如下(以下是博主安裝的版本),如您想直接運行界面程序,只需在下載鏈接中的文件和按照人臉表情識別系統介紹——離線環境配置篇的安裝步驟配置環境后,運行runMain.py程序。

    keras==2.2.4
    PyQt5==5.11.3
    pandas==0.24.2
    scikit-learn==0.21.2
    tensorflow==1.13.1
    imutils==0.5.2
    opencv-python==4.10.25
    matplotlib==3.2.1  # 注意:此依賴包為第二版新增,請注意安裝

    如您想重新訓練模型,運行train_emotion_classifier.py程序即可重新訓練。

詳細安裝教程人臉表情識別系統介紹——離線環境配置篇

公眾號獲取
    本人微信公眾號已創建,掃描以下二維碼並關注公眾號“AI技術研究與分享”,后台回復“ER20190609”獲取。

功能演示動圖

5. 結束語

    由於博主能力有限,博文中提及的方法與代碼即使經過測試,也難免會有疏漏之處。希望您能熱心指出其中的錯誤,以便下次修改時能以一個更完美更嚴謹的樣子,呈現在大家面前。同時如果有更好的實現方法也請您不吝賜教。

    大家的點贊和關注是博主最大的動力,博主所有博文中的代碼文件都可分享給您,如果您想要獲取博文中的完整代碼文件,可通過C幣或積分下載,沒有C幣或積分的朋友可在關注、點贊博文后提供郵箱,我會在第一時間發送給您。博主后面會有更多的分享,敬請關注哦!

參考文獻:
[1] Chollet F. Xception: Deep learning with depthwise separable convolutions[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2017: 1251-1258.
[2] Arriaga O, Valdenegro-Toro M, Plöger P. Real-time convolutional neural networks for emotion and gender classification[J]. arXiv preprint arXiv:1710.07557, 2017.
[3] Jain D K, Shamsolmoali P, Sehdev P. Extended deep neural network for facial emotion recognition[J]. Pattern Recognition Letters, 2019, 120: 69-74.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM