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
根據以上,識別的大體過程以下:
圖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,而且要求很大量的數據以及豐富的調參經驗,這些也都是深度學習中的點~
(完)