多任務卷積神經網絡(MTCNN)實現人臉檢測與對齊是在一個網絡裏實現了人臉檢測與五點標定的模型,主要是經過CNN模型級聯實現了多任務學習網絡。整個模型分爲三個階段,第一階段經過一個淺層的CNN網絡快速產生一系列的候選窗口;第二階段經過一個能力更強的CNN網絡過濾掉絕大部分非人臉候選窗口;第三階段經過一個能力更增強的網絡找到人臉上面的五個標記點;完整的MTCNN模型級聯以下:html
該模型的特徵跟HAAR級聯檢測在某些程度上有必定的相通之處,都是採用了級聯方式,都是在初期就拒絕了絕大多數的圖像區域,有效的下降了後期CNN網絡的計算量與計算時間。MTCNN模型主要貢獻在於:網絡
1.提供一種基於CNN方式的級聯檢測方法,基於輕量級的CNN模型就實現了人 臉檢測與點位標定,並且性能實時。 2.實現了對難樣本挖掘在線訓練提高性能 3.一次能夠完成多個任務。
第一階段
網絡是全卷積神經網絡是一個推薦網絡簡稱 P-Net, 主要功能是得到臉部區域的窗口與邊界Box迴歸,得到的臉部區域窗口會經過BB迴歸的結果進行校訂,而後使用非最大壓制(NMS)合併重疊窗口。
第二階段
網絡模型稱爲優化網絡R-Net,大量過濾非人臉區域候選窗口,而後繼續校訂BB迴歸的結果,使用NMS進行合併。
第三階段
網絡模型稱爲O-Net,輸入第二階段數據進行更進一步的提取,最終輸出人臉標定的5個點位置。架構
網絡架構與訓練
對CNN網絡架構,論文做者發現影響網絡性能的因素主要緣由有兩個:ide
1.樣本的多樣性缺少會影響網絡的鑑別能力 2.相比其它的多類別的分類與檢測任務來講, 人臉檢測是一個二分類,每一層不須要太多filters, 也就是說每層網絡的feature maps個數不須要太多
根據上述兩個因素,做者設計網絡每層的filter個數有限,可是它增長了整個網絡的深度,這樣作的好處是能夠顯著減小計算量,提高整個網絡性能,同時所有改用3x3的filter更進一步下降計算量,在卷積層與全鏈接層使用PReLU做爲非線性激活函數(輸出層除外)整個網絡架構以下:函數
訓練這個網絡須要以下三任務獲得收斂性能
1.人臉二元分類 2.BB迴歸(bounding box regression) 3.標記定位(Landmark localization)
訓練時候對於人臉採用交叉熵損失:
BB迴歸損失:
對每一個候選窗口,計算它與標註框之間的offset,目標是進行位置迴歸,計算其平方差損失以下
學習
臉部landmark位置損失:
總計有五個點位座標分別爲左眼、右眼、鼻子、左嘴角、右嘴角
由於每一個CNN網絡完成不一樣的訓練任務,因此在網絡學習/訓練階段須要不一樣類型的訓練數據。因此在計算損失的時候須要區別對待,對待背景區域,在R-Net與O-Net中的訓練損失爲0,由於它沒有包含人臉區域,經過參數beta=0來表示這種類型。總的訓練損失能夠表示以下:
在P-Net中對人臉進行二元分類時候就能夠在線進行難樣本挖掘,在網絡前向傳播時候對每一個樣本計算獲得的損失進行排序(從高到低)而後選擇70%進行反向傳播,緣由在於好的樣本對網絡的性能提高有限,只有那些難樣本才能更加有效訓練,進行反向傳播以後纔會更好的提高整個網絡的人臉檢測準確率。做者的對比實驗數據代表這樣作能夠有效提高準確率。在訓練階段數據被分爲四種類型:測試
負樣本:並交比小於0.3 正樣本:並交比大於0.65 部分臉:並交比在0.4~0.65之間 Landmark臉:可以找到五個landmark位置的
其中在負樣本與部分臉之間並無明顯的差別鴻溝,做者選擇0.3與0.4做爲區間。優化
正負樣本被用來實現人臉分類任務訓練
正樣本與部分臉樣本訓練BB迴歸
Landmark臉用來訓練人臉五個點位置定位設計
整個訓練數的比例以下:
負樣本:正樣本:部分臉:landmark臉=3:1:1:2
加載網絡
print('Creating networks and loading parameters') with tf.Graph().as_default(): gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False)) with sess.as_default(): pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None)
人臉檢測
def detection(image): minsize = 20 # minimum size of face threshold = [0.6, 0.7, 0.7] # three steps's threshold factor = 0.709 # scale factor # detect with RGB image h, w = image.shape[:2] bounding_boxes, _ = align.detect_face.detect_face(image, minsize, pnet, rnet, onet, threshold, factor) if len(bounding_boxes) < 1: print("can't detect face in the frame") return None print("num %d faces detected"% len(bounding_boxes)) bgr = cv.cvtColor(image, cv.COLOR_RGB2BGR) for i in range(len(bounding_boxes)): det = np.squeeze(bounding_boxes[i, 0:4]) bb = np.zeros(4, dtype=np.int32) # x1, y1, x2, y2 bb[0] = np.maximum(det[0] - margin / 2, 0) bb[1] = np.maximum(det[1] - margin / 2, 0) bb[2] = np.minimum(det[2] + margin / 2, w) bb[3] = np.minimum(det[3] + margin / 2, h) cv.rectangle(bgr, (bb[0], bb[1]), (bb[2], bb[3]), (0, 0, 255), 2, 8, 0) cv.imshow("detected faces", bgr) return bgr
實時攝像頭檢測
capture = cv.VideoCapture(0) height = capture.get(cv.CAP_PROP_FRAME_HEIGHT) width = capture.get(cv.CAP_PROP_FRAME_WIDTH) out = cv.VideoWriter("D:/mtcnn_demo.mp4", cv.VideoWriter_fourcc('D', 'I', 'V', 'X'), 15, (np.int(width), np.int(height)), True) while True: ret, frame = capture.read() if ret is True: frame = cv.flip(frame, 1) cv.imshow("frame", frame) rgb = cv.cvtColor(frame, cv.COLOR_BGR2RGB) result = detection(rgb) out.write(result) c = cv.waitKey(10) if c == 27: break else: break cv.destroyAllWindows()
運行演示:
原本想上傳視頻的發現上傳不了了,因此就把視頻寫成多張連續的圖像,截屏顯示各類效果,其實視頻十分流暢,效果也很是的好。
有遮擋、部分臉檢測
側臉檢測
角度俯仰臉檢測
總結一下
整個模型的運行速度極快,即便在CPU上也能夠徹底達到實時性能,關鍵是其檢測準確率與穩定性跟HAAR/LBP的方式相比,你就會感受HAAR/LBP的方式就是渣,徹底涼啦!