git地址:https://github.com/chenlinzho...php
本文主要介紹了系統涉及的人臉檢測與識別的詳細方法,該系統基於python2.7.10/opencv2/tensorflow1.7.0環境,實現了從攝像頭讀取視頻,檢測人臉,識別人臉的功能
因爲模型文件過大,git沒法上傳,整個項目放在百度雲盤,地址:https://pan.baidu.com/s/1Taal...html
人臉識別是計算機視覺研究領域的一個熱點。目前,在實驗室環境下,許多人臉識別已經遇上(超過)人工識別精度(準確率:0.9427~0.9920),好比face++,DeepID3,FaceNet等(詳情能夠參考:基於深度學習的人臉識別技術綜述)。可是,因爲光線,角度,表情,年齡等多種因素,致使人臉識別技術沒法在現實生活中普遍應用。本文基於python/opencv/tensorflow環境,採用FaceNet(LFW:0.9963 )爲基礎來構建實時人臉檢測與識別系統,探索人臉識別系統在現實應用中的難點。下文主要內容以下 :html5
採用html5 video標籤能夠很方便的實現從攝像頭讀取視頻幀,下文代碼實現了從攝像頭讀取視頻幀,faceDection識別人臉後截取圖像上傳到服務器功能
在html文件中添加video,canvas標籤python
<div class="booth"> <video id="video" width="400" height="300" muted class="abs" ></video> <canvas id="canvas" width="400" height="300"></canvas> </div>
打開網絡攝像頭jquery
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組件檢測人臉nginx
$('#canvas').faceDetection()
檢測出人連臉的話截圖,並把圖片轉換爲base64的格式,方便上傳git
context.drawImage(video, 0, 0, video.width, video.height); var base64 = canvas.toDataURL('images/png');
將base64格式的圖片上傳到服務器github
//上傳人臉圖片 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語言實現web
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)ajax
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索引算法的有個假設就是,每一個人臉特徵能夠看作是在高維空間的一個點,若是兩個很接近(相識),任何超平面
都沒法把他們分開,也就是說若是空間的點很接近,用超平面去分隔,類似的點必定會分在同一個平面空間(具體參看:https://github.com/spotify/annoy)
#人臉特徵先存儲在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
系統採用有兩個模塊組成:
模塊間採用socket方式通訊通訊格式爲: length+content
face_server相關的配置在config.ini文件中
1.使用鏡像
假設項目路徑爲/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模型後等待人臉請求
未註冊識別失敗
人臉註冊
註冊後登陸成功
https://zhuanlan.zhihu.com/p/...
https://github.com/spotify/annoy