git地址:github.com/chenlinzhon…
本文主要介紹了系統涉及的人臉檢測與識別的詳細方法,該系統基於python2.7.10/opencv2/tensorflow1.7.0環境,實現了從攝像頭讀取視訊,檢測人臉,識別人臉的功能 由於模型檔案過大,git無法上傳,整個專案放在百度雲盤,地址:pan.baidu.com/s/1TaalpwQw…
人臉識別是計算機視覺研究領域的一個熱點。目前,在實驗室環境下,許多人臉識別已經趕上(超過)人工識別精度(準確率:0.9427~0.9920),比如face++,DeepID3,FaceNet等(詳情可以參考:基於深度學習的人臉識別技術綜述)。但是,由於光線,角度,表情,年齡等多種因素,導致人臉識別技術無法在現實生活中廣泛應用。本文基於python/opencv/tensorflow環境,採用FaceNet(LFW:0.9963 )為基礎來構建實時人臉檢測與識別系統,探索人臉識別系統在現實應用中的難點。下文主要內容如下 :
- 利用htm5 video標籤開啟攝像頭採集頭像並使用jquery.faceDeaction元件來粗略檢測人臉
- 將人臉影象上傳到伺服器,採用mtcnn檢測人臉
- 利用opencv的仿射變換對人臉進行對齊,儲存對齊後的人臉
- 採用預訓練的facenet對檢測的人臉進行embedding,embedding成512維度的特徵;
- 對人臉embedding特徵建立高效的annoy索引進行人臉檢測
人臉採集
採用html5 video標籤可以很方便的實現從攝像頭讀取視訊幀,下文程式碼實現了從攝像頭讀取視訊幀,faceDection識別人臉後擷取影象上傳到伺服器功能 在html檔案中新增video,canvas標籤
<div class="booth">
<video id="video" width="400" height="300" muted class="abs" ></video>
<canvas id="canvas" width="400" height="300"></canvas>
</div>
複製程式碼
開啟網路攝像頭
var video = document.getElementById('video'),
var vendorUrl = window.URL || window.webkitURL;
//媒體物件
navigator.getMedia = navigator.getUserMedia || navagator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
navigator.getMedia({video: true, //使用攝像頭物件audio: false //不適用音訊}, function(strem){
video.src = vendorUrl.createObjectURL(strem);
video.play();
});
複製程式碼
利用jquery的facetDection元件檢測人臉
$('#canvas').faceDetection()
檢測出人連臉的話截圖,並把圖片轉換為base64的格式,方便上傳
context.drawImage(video, 0, 0, video.width, video.height);
var base64 = canvas.toDataURL('images/png');
複製程式碼
將base64格式的圖片上傳到伺服器
//上傳人臉圖片
function upload(base64) {
$.ajax({
"type":"POST",
"url":"/upload.php",
"data":{'img':base64},
'dataType':'json',
beforeSend:function(){},
success:function(result){
console.log(result)
img_path = result.data.file_path
}
});
}
複製程式碼
圖片伺服器接受程式碼,php語言實現
function base64_image_content($base64_image_content,$path){
//匹配出圖片的格式
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)){
$type = $result[2];
$new_file = $path."/";
if(!file_exists($new_file)){
//檢查是否有該資料夾,如果沒有就建立,並給予最高許可權
mkdir($new_file, 0700,true);
}
$new_file = $new_file.time().".{$type}";
if (file_put_contents($new_file, base64_decode(str_replace($result[1], '', $base64_image_content)))){
return $new_file;
}else{
return false;
}
}else{
return false;
}
}
複製程式碼
人臉檢測
人臉檢測方法有許多,比如opencv自帶的人臉Haar特徵分類器和dlib人臉檢測方法等。 對於opencv的人臉檢測方法,有點是簡單,快速;存在的問題是人臉檢測效果不好。正面/垂直/光線較好的人臉,該方法可以檢測出來,而側面/歪斜/光線不好的人臉,無法檢測。因此,該方法不適合現場應用。對於dlib人臉檢測方法 ,效果好於opencv的方法,但是檢測力度也難以達到現場應用標準。 本文中,我們採用了基於深度學習方法的mtcnn人臉檢測系統(mtcnn:Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Neural Networks)。mtcnn人臉檢測方法對自然環境中光線,角度和人臉表情變化更具有魯棒性,人臉檢測效果更好;同時,記憶體消耗不大,可以實現實時人臉檢測。本文中採用mtcnn是基於python和tensorflow的實現(程式碼來自於davidsandberg,caffe實現程式碼參見:kpzhang93)
model= os.path.abspath(face_comm.get_conf('mtcnn','model'))
class Detect:
def __init__(self):
self.detector = MtcnnDetector(model_folder=model, ctx=mx.cpu(0), num_worker=4, accurate_landmark=False)
def detect_face(self,image):
img = cv2.imread(image)
results =self.detector.detect_face(img)
boxes=[]
key_points = []
if results is not None:
#box框
boxes=results[0]
#人臉5個關鍵點
points = results[1]
for i in results[0]:
faceKeyPoint = []
for p in points:
for i in range(5):
faceKeyPoint.append([p[i], p[i + 5]])
key_points.append(faceKeyPoint)
return {"boxes":boxes,"face_key_point":key_points}
複製程式碼
具體程式碼參考fcce_detect.py
人臉對齊
有時候我們擷取的人臉了頭像可能是歪的,為了提升檢測的質量,需要把人臉校正到同一個標準位置,這個位置是我們定義的,假設我們設定的標準檢測頭像是這樣的
假設眼睛,鼻子三個點的座標分別是a(10,30) b(20,30) c(15,45),具體設定可參看config.ini檔案alignment塊配置項
採用opencv仿射變換進行對齊,獲取仿射變換矩陣
dst_point=【a,b,c】
tranform = cv2.getAffineTransform(source_point, dst_point)
複製程式碼
仿射變換:
img_new = cv2.warpAffine(img, tranform, imagesize)
具體程式碼參考face_alignment.py檔案
產生特徵
對齊得到後的頭像,放入採用預訓練的facenet對檢測的人臉進行embedding,embedding成512維度的特徵,以(id,vector)的形式儲存在lmdb檔案中
facenet.load_model(facenet_model_checkpoint)
images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
face=self.dectection.find_faces(image)
prewhiten_face = facenet.prewhiten(face.image)
# Run forward pass to calculate embeddings
feed_dict = {images_placeholder: [prewhiten_face], phase_train_placeholder: False}
return self.sess.run(embeddings, feed_dict=feed_dict)[0]
複製程式碼
具體程式碼可參看face_encoder.py
人臉特徵索引:
人臉識別的時候不能對每一個人臉都進行比較,太慢了,相同的人得到的特徵索引都是比較類似,可以採用KNN分類演算法去識別,這裡採用是更高效annoy演算法對人臉特徵建立索引,annoy索引演算法的有個假設就是,每個人臉特徵可以看做是在高維空間的一個點,如果兩個很接近(相識),任何超平面 都無法把他們分開,也就是說如果空間的點很接近,用超平面去分隔,相似的點一定會分在同一個平面空間(具體參看:github.com/spotify/ann…
#人臉特徵先儲存在lmdb檔案中格式(id,vector),所以這裡從lmdb檔案中載入
lmdb_file = self.lmdb_file
if os.path.isdir(lmdb_file):
evn = lmdb.open(lmdb_file)
wfp = evn.begin()
annoy = AnnoyIndex(self.f)
for key, value in wfp.cursor():
key = int(key)
value = face_comm.str_to_embed(value)
annoy.add_item(key,value)
annoy.build(self.num_trees)
annoy.save(self.annoy_index_path)
複製程式碼
具體程式碼可參看face_annoy.py
人臉識別
經過上面三個步驟後,得到人臉特徵,在索引中查詢最近幾個點並就按歐式距離,如果距離小於0.6(更據實際情況設定的閾值)則認為是同一個人,然後根據id在資料庫查詢到對應人的資訊即可
#根據人臉特徵找到相似的
def query_vector(self,face_vector):
n=int(face_comm.get_conf('annoy','num_nn_nearst'))
return self.annoy.get_nns_by_vector(face_vector,n,include_distances=True)
複製程式碼
具體程式碼可參看face_annoy.py
安裝部署
系統採用有兩個模組組成:
- face_web:提供使用者註冊登入,人臉採集,php語言實現
- face_server: 提供人臉檢測,裁剪,對齊,識別功能,python語言實現
模組間採用socket方式通訊通訊格式為: length+content
face_server相關的配置在config.ini檔案中
1.使用映象
- face_serverdocker映象: shareclz/python2.7.10-face-image
- face_web映象: skiychan/nginx-php7
假設專案路徑為/data1/face-login
2.安裝face_server容器
docker run -it --name=face_server --net=host -v /data1:/data1 shareclz/python2.7.10-face-image /bin/bash
cd /data1/face-login
python face_server.py
複製程式碼
3.安裝face_web容器
docker run -it --name=face_web --net=host -v /data1:/data1 skiychan/nginx-php7 /bin/bash
cd /data1/face-login;
php -S 0.0.0.0:9988 -t ./web/
複製程式碼
最終效果:
face_server載入mtcnn模型和facenet模型後等待人臉請求
未註冊識別失敗
人臉註冊
註冊後登入成功