使用dlib中的深度殘差網絡(ResNet)實現實時人臉識別

opencv中提供的基於haar特徵級聯進行人臉檢測的方法效果很是很差,本文使用dlib中提供的人臉檢測方法(使用HOG特徵或卷積神經網方法),並使用提供的深度殘差網絡(ResNet)實現實時人臉識別,不過本文的目的不是構建深度殘差網絡,而是利用已經訓練好的模型進行實時人臉識別,實時性要求一秒鐘達到10幀以上的速率,而且保證不錯的精度。opencv和dlib都是很是好用的計算機視覺庫,特別是dlib,前面文章提到了其內部封裝了一些比較新的深度學習方法,使用這些算法能夠實現不少應用,好比人臉檢測、車輛檢測、目標追蹤、語義分割等等。因爲這兩個庫相應的都包含了C++和Python的版本,而Python的配置和相對使用起來更加簡單,所以這篇文章主要經過Python來實現。html

先上測試的識別效果,第一張識別吳恩達和Bengio,後者我沒有打標籤因此識別的是「other」;另一張gif 是識別梁朝偉、劉德華和一個女主持的過程,本地庫中沒有存儲女主持的圖片。python

                                                     

             

 

由於博客園不方便上傳本地視頻,因此用的gif顯示效果圖,源視頻要比gif清楚,640X480像素大小,總的來講效果識別的效果還不錯。算法

1、準備json

(1)須要安裝opencv和dlib的Python庫,以前的一篇文章提到了怎樣安裝:http://www.cnblogs.com/supersayajin/p/8446685.html;若是你有GPU而且開啓了加速,那麼實現的人臉識別程序速度很是快,能夠知足實時性,以我運行的結果來看,檢測+識別640X480像素的視頻流一秒鐘大約十幾幀;若是你沒有GPU那麼速度就會很慢了,並且在檢測階段不能使用卷積神經網絡的方法了,不然檢測一幀數據可能須要幾秒甚至幾十秒:)安全

(2)須要一個和PC鏈接的攝像頭;在本文中使用的是串口的攝像頭,筆記本電腦集成的攝像頭就是串口的,opencv中提供了直接獲取串口攝像頭的接口,很是方便使用;若是是網口的攝像頭那麼就要看攝像頭提供方,好比大華、海康他們的攝像頭可能會提供官方的SDK,若是有接口那是最好;或者,若是攝像頭支持RTSP協議,opencv也能夠經過RTSP協議獲取攝像頭的數據;不然可能就要寫一套socket通訊來實現數據傳輸,這個不在本文範圍以內,默認使用的是串口的攝像頭。網絡

2、策略數據結構

人臉識別分爲人臉檢測和識別兩個階段,人臉檢測會找到人臉區域的矩形窗口,識別則經過ResNet返回人臉特徵向量,並進行匹配。app

(1)人臉檢測階段。人臉檢測算法須要用大小位置不一樣的窗口在圖像中進行滑動,而後判斷窗口中是否存在人臉。在深度學習以前的主流方法是特徵提取+集成學習分類器,好比之前火熱的haar特徵+adaboost級聯分類器,opencv中實現的人臉檢測方法就採用了這種,不過實驗結果來看,這種檢測方法效果很很差,常常誤檢測人臉,或者檢測不到真實的人臉;dlib中使用的是HOG(histogram of oriented gradient)+ 迴歸樹的方法,使用dlib訓練好的模型進行檢測效果要好不少。dlib也使用了卷積神經網絡來進行人臉檢測,效果好於HOG的集成學習方法,不過須要使用GPU加速,否則程序會卡爆了,一張圖片可能幾秒甚至幾十秒。socket

(2)識別階段。識別也就是咱們常說的「分類」,攝像頭採集到這我的臉時,讓機器判斷是張三仍是其餘人。分類分爲兩個部分:ide

  • 特徵向量抽取。本文用到的是dlib中已經訓練好的ResNet模型的接口,此接口會返回一個128維的人臉特徵向量。
  • 距離匹配。在獲取特徵向量以後可使用歐式距離和本地的人臉特徵向量進行匹配,使用最近鄰分類器返回樣本的標籤。

根據以上,識別的大體過程以下:

圖1 人臉識別分類過程

 

對於圖1中的獲取人臉特徵向量,其過程以下:

圖2 獲取人臉特徵向量過程

用簡單的話總結,整個過程分爲兩個階段,本地存儲已標記人臉數據;識別階段把從攝像頭讀取的人臉和本地進行匹配,獲得分類結果。

3、程序實現

(1)構建本地人臉特徵向量庫,而且打標籤。

首先加載須要的python庫:

import dlib
import numpy as np
import cv2
import os
import json

 

而後加載模型參數:

detector = dlib.cnn_face_detection_model_v1('mmod_human_face_detector.dat')
sp = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat')

 

上面代碼中的模型參數能夠到這裏下載:http://dlib.net/files/。detector是使用卷積神經網絡(CNN)進行人臉檢測的檢測算子,固然若是你使用CNN的話須要使用GPU加速,不然速度會超級慢。也可使用另外一種方法,即HOG特徵級聯分類的檢測方法,效果略差於CNN。變量sp,使用預測算子獲取獲得的人臉區域中的五官的幾何點區域,這裏加載的是68特徵點的landmark模型;而後facerec會獲得ResNet模型,He Kaiming(2009年和2015的CVPR best paper做者)提出的方法的一個實現,這裏訓練模型已經給出,所以不須要本身手動去訓練了。

最後,對某個目錄中的全部圖片進行處理,處理的方式是一張一張地讀取某個目錄中的圖片,每讀取一張就檢測人臉,若是存在人臉就使用ResNet的接口獲取人臉特性向量,保存到事先準備好的矩陣中,而且按照文件名存取標籤,完了以後把全部的人臉特徵向量和標籤都存到本地的文本文件中。注意這裏給圖片打標籤的方式,我把每張圖片命名爲標籤名+下劃線+序號+點號+後綴名的形式,標籤名是手動命名的標記名稱,序號用以區分同一類中的第幾張。如下是demo中存放的部分圖片:

 

也有不少其餘的方法打標籤,這裏很少舉例。

imagePath = 'LocalImage/'                                                                           #圖像的目錄
data = np.zeros((1,128))                                                                            #定義一個128維的空向量data
label = []                                                                                          #定義空的list存放人臉的標籤

for file in os.listdir(imagePath):                                                                  #開始一張一張索引目錄中的圖像
    if '.jpg' in file or '.png' in file:
        fileName = file
        labelName = file.split('_')[0]                                                              #獲取標籤名
        print('current image: ', file)
        print('current label: ', labelName)
        
        img = cv2.imread(imagePath + file)                                                          #使用opencv讀取圖像數據
        if img.shape[0]*img.shape[1] > 500000:                                                      #若是圖太大的話須要壓縮,這裏像素的閾值能夠本身設置
            img = cv2.resize(img, (0,0), fx=0.5, fy=0.5)
        dets = detector(img, 1)                                                                     #使用檢測算子檢測人臉,返回的是全部的檢測到的人臉區域
        for k, d in enumerate(dets):
            rec = dlib.rectangle(d.rect.left(),d.rect.top(),d.rect.right(),d.rect.bottom())
            shape = sp(img, rec)                                                                    #獲取landmark
            face_descriptor = facerec.compute_face_descriptor(img, shape)                           #使用resNet獲取128維的人臉特徵向量
            faceArray = np.array(face_descriptor).reshape((1, 128))                                 #轉換成numpy中的數據結構
            data = np.concatenate((data, faceArray))                                                #拼接到事先準備好的data當中去
            label.append(labelName)                                                                 #保存標籤
            cv2.rectangle(img, (rec.left(), rec.top()), (rec.right(), rec.bottom()), (0, 255, 0), 2)       #顯示人臉區域
        cv2.waitKey(2)
        cv2.imshow('image', img)

data = data[1:, :]                                                                                  #由於data的第一行是空的128維向量,因此實際存儲的時候從第二行開始
np.savetxt('faceData.txt', data, fmt='%f')                                                          #保存人臉特徵向量合成的矩陣到本地

labelFile=open('label.txt','w')                                      
json.dump(label, labelFile)                                                                         #使用json保存list到本地
labelFile.close()

cv2.destroyAllWindows()                                                                             #關閉全部的窗口

 

上面的代碼中,會索引imagePath這個存放圖像的目錄;而後定義一個128維的空向量data,在後續獲取每一張人臉特徵向量的時候能夠往這個向量後面追加,即data的每一行是一個樣本的特徵向量;而後定義一個list來存儲標籤。以後開始索引某個目錄下全部的圖片文件。注意我這裏用的是opencv的接口讀取圖像,也可使用其餘的圖像讀取接口,好比dlib自帶的或者PIL接口中的,均可以使用,不太重要的是接口必定要統一,由於每一個接口讀取圖片轉成矩陣的數值可能會有差別。而後使用前面定義的測算子開始檢測人臉,返回的是dlib中的一個數據結構,這個數據結構存儲了全部檢測到的人臉區域信息,對每一個檢測到的人臉區域獲取landmark,而且調用深度殘差模型的接口獲取128維的人臉特徵向量,以後咱們把這我的臉向量存儲到data中去,這裏使用numpy中提供的concatenate方法進行拼接,同時把標籤添加到label列表中去。最後,由於data事先定義的是一個128維的空向量,以後利用concatenate方法進行拼接獲得,咱們須要拋棄第一行;最後把獲得的人臉特徵和標籤存儲到本地文件。

這裏使用的是CNN進行人臉檢測,若是你沒有GPU,或者你有GPU但沒有進行GPU的配置,那麼速度巨慢,此時你可使用傳統的HOG特徵+級聯分類的方法,不過效果沒有CNN的好。這時代碼的第6行中模型須要替換成:

detector = dlib.get_frontal_face_detector()

 

其他的基本保持不變。 

以上的代碼能夠直接運行,運行以後會檢測全部的圖像,相似於:

而且存取獲得本地的人臉特徵向量庫和標籤:

 

(2)實時讀取攝像頭進行人臉識別

在(1)中咱們已經獲得了本地的打過標籤的人臉特徵向量,這一部分是實現讀取攝像頭實時識別。首先加載須要的python庫:

import dlib
import numpy as np
import cv2
import json

 

而後加載神經網絡模型:

detector = dlib.cnn_face_detection_model_v1('mmod_human_face_detector.dat')
sp = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat')
threshold = 0.54

其中threshold是人臉識別的閾值,當測試圖片和本地圖片歐式距離最近的值大於這個值的時候,咱們認爲不屬於本都圖片的任何一個類別。而後定義最近鄰分類器:

def findNearestClassForImage(face_descriptor, faceLabel):
    temp =  face_descriptor - data
    e = np.linalg.norm(temp,axis=1,keepdims=True)
    min_distance = e.min() 
    print('distance: ', min_distance)
    if min_distance > threshold:
        return 'other'
    index = np.argmin(e)
    return faceLabel[index]

 

 當距離值大於threshold的時候咱們返回標籤「other」,不然返回本地的標籤,你能夠根據實際狀況來設置這個閾值。題外話,安全閾值很依賴於具體的場合,好比安檢、銀行裏進行人臉驗證、iPhone解鎖,這些對安全要求很高的場合須要比較小的threshold來保證安全,在嫌犯追蹤的時候,須要比較大的threshold以保證由嫌疑的人不會漏過。

而後是讀取圖像進行識別的函數:

def recognition(img):
    dets = detector(img, 1)
    for k, d in enumerate(dets):
        
        print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format(
            k, d.rect.left(), d.rect.top(), d.rect.right(), d.rect.bottom()))
        rec = dlib.rectangle(d.rect.left(),d.rect.top(),d.rect.right(),d.rect.bottom())
        print(rec.left(),rec.top(),rec.right(),rec.bottom())
        shape = sp(img, rec)
        face_descriptor = facerec.compute_face_descriptor(img, shape)        
        
        class_pre = findNearestClassForImage(face_descriptor, label)
        print(class_pre)
        cv2.rectangle(img, (rec.left(), rec.top()+10), (rec.right(), rec.bottom()), (0, 255, 0), 2)
        cv2.putText(img, class_pre , (rec.left(),rec.top()), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0), 2, cv2.LINE_AA)

    cv2.imshow('image', img)

 

 最後是實時讀取攝像頭圖像,而且進行識別的過程:

labelFile=open('label.txt','r')
label = json.load(labelFile)                                                   #載入本地人臉庫的標籤
labelFile.close()
    
data = np.loadtxt('faceData.txt',dtype=float)                                  #載入本地人臉特徵向量

cap = cv2.VideoCapture(0)
fps = 10
size = (640,480)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
videoWriter = cv2.VideoWriter('video.MP4', fourcc, fps, size)

while(1):
    ret, frame = cap.read()
    #frame = cv2.resize(frame, (0,0), fx=0.5, fy=0.5)
    recognition(frame)
    videoWriter.write(frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
    
cap.release()
videoWriter.release()
cv2.destroyAllWindows()

 在上面的代碼中爲了展現檢測的效果,我用opencv的接口把圖像保存到了視頻當中。識別效果截圖:

 

4、總結

利用已有的計算機視覺庫能夠實現不少好玩和有用的應用,本文只是粗略地展現了一個進行實時人臉識別的demo,還有不少能夠改善的點來提升精度和效率,好比人臉受角度、表情影響很大,或者須要處理速度要求更高的場景;同時圖像類別規模很大的狀況下如何保證效果,如何優化這些都是難點。另外dlib中的提供的這些模型都是已經訓練好的,咱們能夠到官方demo下載,demo給出了在一些benchmark中的效果,也能夠本身訓練獲得這些模型,固然前提是你須要有GPU,而且要求很大量的數據以及豐富的調參經驗,這些也都是深度學習中的點~

(完)

相關文章
相關標籤/搜索