趙澤軍 蘭雁
【摘 要】近幾年深度學習大火,而人臉識別又是深度學習的典型代表。TensorFlow是深度學習的一個平臺,主要通過搭建卷積神經網絡系統來對圖片樣本進行特征識別、分類訓練,進而達到人臉的識別。本系統調用了OpenCV計算機視覺庫中的功能函數,通過USB攝像頭實時截取人臉圖像,然后進行Haar特征識別以及Adaboost分類器的分類訓練,最后打開攝像頭,將捕捉到的人臉與訓練好的圖片進行對比,從而達到人臉識別的效果。
【關鍵詞】深度學習;人臉識別;卷積神經網絡;OpenCV;TensorFlow
人臉識別本質是用一個幾何特征矢量來表示人臉,并根據模式識別中層次聚類的思想設計分類器,達到識別目的。調用OpenCV庫中的功能函數來截取視頻中的圖像幀數據,并進行一系列預處理,完成圖片的灰度化以及統一圖片的大小和格式,然后再將這些圖片輸入到搭建好的卷積神經網絡中進行訓練,訓練完成后將生成一個訓練模型,此模型便是人臉識別時的評估標準。
一、模塊功能及結構劃分
系統共分為四個部分,分別為:實時人臉截取模塊、圖片預處理模塊、CNN搭建和訓練模塊以及人臉檢測模塊。
實時人臉截取模塊主要負責采集人臉圖片。管理員輸入要采集的人員姓名,后臺生成以該姓名為名稱的文件夾,然后使用USB攝像頭獲取視頻數據,利用OpenCV的功能函數定位人臉部分,截取一幀的圖像數據保存到該文件夾下,當采集數量達到設定的數量后,攝像頭關閉。
圖片預處理模塊主要負責圖片灰度化、像素歸一化、圖片大小規格化以及為每一類圖片夾指定一個唯一的標簽,便于后面的訓練。
CNN搭建和訓練模塊主要完成搭建神經網絡、劃分訓練集和驗證集、深度學習訓練以及訓練模型的保存,有了訓練模型后就能進行人臉識別了。
人臉檢測模塊主要調用攝像頭來獲取人臉圖片,讀入之前訓練成熟的網路模型來進行人臉識別。
二、項目原理
(一)人臉識別分類器
1.Haar特征
Haar特征分為三類:邊緣特征、線性特征、中心特征和對角線特征,組合成特征模板。特征模板內有白色和黑色兩種矩形,并定義該模板的特征值為白色矩形像素和減去黑色矩形像素和(在opencv實現中為黑色-白色)。Haar特征值反映了圖像的灰度變化情況。例如:臉部的一些特征能由矩形特征簡單的描述,如:眼睛要比臉頰顏色要深,鼻梁兩側比鼻梁顏色要深,嘴巴比周圍顏色要深等。但矩形特征只對一些簡單的圖形結構,如邊緣、線段較敏感,所以只能描述特定走向(水平、垂直、對角)的結構。
2.矩形特征個數的計算
在實際中,Haar特征可以在檢測窗口中由放大+平移產生一系列子特征,但是白:黑區域面積比始終保持不變。那么這些通過放大+平移的獲得的子特征到底總共有多少個?Rainer Lienhart在他的論文中給出了完美的解釋:假設檢測窗口大小為W*H,矩形特征大小為w*h,X和Y為表示矩形特征在水平和垂直方向的能放大的最大比例系數:。在檢測窗口Window中,一般矩形特征(upright rectangle)的數量為:
3.積分圖
當有了大量的矩形特征用于訓練和檢測時,接下來的問題是如何計算Haar特征值。Viola等人提出了利用積分圖求特征值的方法。一個區域的像素值,可以利用該區域的端點的積分圖來計算。在積分圖中,ii(1)表示區域A的像素值,ii(2)表示區域A+B的像素值,ii(3)表示區域A+C的像素值,ii(4) 表示區域A+B+C+D的像素值。而:
區域D的像素值=區域A+B+C+D的像素值+區域A的像素值-區域A+B的像素值-區域A+C的像素值.
即:
區域D的像素值=ii(4) + ii(1) - ii(2) - ii(3)
由此,可用積分圖快速得到一個區域的像素值。
4.Adaboost分類器
在確定了訓練子窗口中的矩形特征數量和特征值后,需要對每一個特征,訓練一個弱分類器。Adaboost算法基本原理就是將多個弱分類器(弱分類器一般選用單層決策樹)進行合理的結合,使其成為一個強分類器。具體說來,整個Adaboost迭代算法分為3步:
(1)初始化訓練數據的權值分布。如果有N個樣本,則每一個訓練樣本最開始時都被賦予相同的權重:1/N。
(2)訓練弱分類器。具體訓練過程中,如果某個樣本點已經被準確地分類,那么在構造下一個訓練集中,它的權重就被降低;相反,如果某個樣本點沒有被準確地分類,那么它的權重就得到提高。然后,權重更新過的樣本集被用于訓練下一個分類器,整個訓練過程如此迭代地進行下去。
(3)將各個訓練得到的弱分類器組合成強分類器。各個弱分類器的訓練過程結束后,加大分類誤差率小的弱分類器的權重,使其在最終的分類函數中起著較大的決定作用,而降低分類誤差率大的弱分類器的權重,使其在最終的分類函數中起著較小的決定作用。換言之,誤差率低的弱分類器在最終分類器中占的權重較大,否則較小。
(二)卷積神經網絡
1.輸入層
在處理圖像的卷積神經網絡中,它一般代表了一張圖片的像素矩陣。通常為(length * width *channel)。 三維矩陣的深度代表了圖像的彩色通道(channel)。比如黑白圖片的深度為1,而在RGB色彩模式下,圖像的深度為3。從輸入層開始,卷積神經網絡通過不同的神經網絡結構將上一層的三維矩陣轉化為下一層的三維矩陣,直到最后的全連接層。
2.卷積層
卷積層主要進行的操作是對圖片進行特征提取,隨著卷積層的深入它提取到的特征就越高級。對于一張輸入圖片,將其轉化為矩陣,矩陣的元素為對應的像素值,對于一張大小為5×5的圖像,使用3×3的感受野進行移動長度為1的非零填充卷積,可以得到大小為3×3的特征平面,此后卷積核會先向右移動,再向下移動,直到最后和每一個值完成了運算。每當圖像進行了一次卷積之后,它的輸出都會發生對應的改變。若輸入為5x5,在尺寸為3x3的卷積核和步長為1的作用下,最后得到的輸出圖像特征的尺寸不再是5,而是5-3+1=3。所以經過了卷積層之后,輸出尺寸會發生相應的變化。
3.池化層
池化層的作用是把卷積層得到的特征平面的數據量降低。池化之后的圖像大小雖然發生了變化,但是圖像的大致輪廓并沒有丟失,而且生成的平面作為下次卷積的輸入,在卷積核不變的情況下相當于增加了卷積核對于圖片信息的提取能力。池化有平均值池化和最大值池化,最大池化層(max pooling)使用最大值操作,是被使用得最多的池化層結構。
三、實現過程
(一)gain_face.py(截取人臉圖像)
該腳本主要運用OpenCV來獲取人臉圖像,圖片來源可以是一段保存好的視頻,也可以通過USB攝像頭實時捕捉。若通過攝像頭捕捉人臉,則需預先設定圖片捕捉數量,詢問用戶姓名。cv2.CascadeClassifier("E:\haarcascade_frontalface_alt2.xml")告訴OpenCV使用人臉識別分類器(給定haarcascade_frontalface_alt2.xml的路徑),然后循環從視頻里讀取一幀數據,調用cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)將當前幀轉換為灰度圖像,圖像灰度化后可以降低計算量,便于后面計算特征,然后調用classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=2, minSize=(32, 32))進行人臉檢測,其中scaleFactor=1.2是圖像縮放比例, minNeighbors=2為兩個檢測有效點,這樣OpenCV截取的人臉灰度圖將會保存到以各姓名命名的文件夾下,當達到我們設定的圖片數量后,程序會退出循環并銷毀攝像頭。
(二)load_dataset.py(圖像數據預處理)
上一步我們得到了不同的人的人臉灰度圖,這一步就是來處理這些灰度圖像。過程中調用了cv2.resize(constant_image,(height,width))來統一圖片大小,將所有圖片的規格統一成了64x64。考慮兩個數組,一個是數據集,另一個是標注集,數據集存放處理過后的圖像,標記集存放圖像路徑,最后為每一類圖像(每一個人)指定一個唯一的標簽,便于后面訓練。
(三)face_train.py(模型訓練)
1.準備訓練數據
首先導入訓練需要的庫TensorFlow和Keras,TensorFlow的版本我們選擇1.13.1,然后定義一個數據加載類,用于準備訓練數據,在這個類里我們設置了訓練集、驗證集和測試集, 然后導入load_dataset.py的方法,將上一步處理過的圖片加載到這三個集中。這一步的代碼為train_images, valid_images, train_labels,valid_labels=train_test_split(images,labels,test_size=0.3,random_state=random.randint(0, 100)),然后根據keras庫要求的維度順序重組訓練數據集,為減少計算量,我們將像素進行歸一化處理,代碼為:train_images /= 255
valid_images /= 255
test_images /= 255
2.搭建CNN網絡模型
到現在為止,訓練數據就準備完成了。接下來開始搭建CNN網絡模型,我們用model = Sequential()構建一個空的網絡模型,它是一個線性堆疊模型,各神經網絡層會被順序添加,然后順序地添加CNN網絡需要的各層,部分代碼為:
model.add(Convolution2D(32, 3, 3, border_mode='same',
input_shape=dataset.input_shape))? # 1 2維卷積層: 32個過濾器,每個像素是3x3
model.add(Activation('relu'))? # 2 激活函數層
model.add(MaxPooling2D(pool_size=(2, 2)))? # 3 池化層
model.add(Dropout(0.25))? # 4 Dropout層
model.add(Convolution2D(64, 3, 3, border_mode='same'))? # 5 2維卷積層
model.add(Activation('relu'))? # 6 激活函數層
model.add(Flatten())? # 7 Flatten層
model.add(Dense(512))? # 8 全連接層
model.add(Activation('relu'))? # 9 激活函數層
model.add(Dropout(0.5))? # 10 Dropout層
model.add(Dense(nb_classes))? # 11 全連接層
model.add(Activation('softmax'))? # 12 分類層 輸出最終結果
3.開始訓練
模型搭建后,接下來就開始訓練了。定義一個訓練函數def train(),采用SGD+momentum的優化器進行訓練,生成一個優化器對象,這里代碼為sgd = SGD(lr=0.01,decay=1e-6,momentum=0.9,nesterov=True),用model.compile(loss='categorical_crossentropy',optimizer=sgd,metrics=['accuracy'])完成實際的模型配置工作,然后用model.fit_generator()構建生成器訓練模型,訓練完成后將會生成一個aggregate.face.model.h5文件,該文件就是后面人臉識別時的判決模型。
(四)face_recongnition.py(識別人臉)
現在我們已經有了訓練成熟的模型,在這個腳本里,首先導入aggregate.face.model.h5模型,然后利用OpenCV打開攝像頭捕捉人臉,在一系列灰度處理、像素處理后,將獲得的實時人臉圖片與導入的模型進行對比,最后給出文字提示這是誰。部分代碼為:
image = frame[y: y + h, x: x + w]
faceID = model.face_predict(image) # 截取臉部圖像提交給模型
cv2.putText() # 給出文字提示
四、結語
本系統設計源于生活,有很好的實用性。項目的重點有二,一是圖像數據的處理,二是CNN模型的訓練。在數據處理中,必須將人臉圖像灰度化,否則計算強度很大,效率將大幅度降低;圖片的規格要統一,像素要歸一化處理,這樣才能最大化計算效率,訓練精度也會提高。模型訓練是此項目的重中之重,由于項目初衷在于實現任意數量人臉的識別,所以這里設定了多層卷積層和池化層,目的是提高訓練精度。訓練過程中,訓練數量是一個值得考慮的因素,太少則達不到訓練效果,太多則會出現訓練過擬合現象,但只要經過多次測試,找到合適的訓練數量,就能達到一個較為理想的訓練效果。
【參考文獻】
[1]許佳勝,李欣,孫宏凱. 人臉識別技術概述[J].科技風.2020(04)
[2]黨永成. 人臉識別技術綜述及分析[J].電子技術與軟件工程.2018(03)
[3]景晨凱,宋濤,莊雷. 基于深度卷積神經網絡的人臉識別技術綜述[J].計算機應用與軟件.2018(01)
[4]張延安,王宏玉,徐方. 基于深度卷積神經網絡與中心損失的人臉識別[J].科學技術與工程.2017(35)
[5]陳華官. 基于端到端深度卷積神經網絡的人臉識別算法[D].浙江大學.2017
[6]黃發揚. 淺談基于神經網絡深度學習算法的人臉識別技術[J].智能建 筑.2018(10)
[7]張廣才,何繼榮,高文朋. 基于深度學習的人臉識別研究[J].無線互聯科技.2019(19)
[8]徐政超. 基于深度學習的人臉識別技術研究[J].信息系統工程.2019(10).