OpenCV基於殘差網絡實現人臉檢測
OpenCV3.3版本第一次把深度神經網絡(DNN)模塊引入到正式發佈版本中,最新的OpenCV3.4中DNN模塊發佈了兩個必殺技,一個支持Faster R-CNN的對象檢測,比SSD與YOLO這些模型有更好的檢測精度與小對象檢測能力,另一個是支持基於SSD+Resnet模型的人臉檢測,雖然速度還達不到HAAR級聯檢測器的實時性,可是準確性與模型泛化能力能夠說完爆HAAR級聯檢測器方式的人臉檢測算法。做爲OpenCV開發者須要人臉檢測功能時候又多了一種更加可靠的選擇,這裏咱們首先簡單介紹一下什麼是殘差網絡,而後給出其人臉檢測模型在OpenCV基於攝像頭實時人臉檢測演示。
一:殘差網絡(Resnet)
最初的CNN網絡LeNet與AlexNet卷積層都比較少,VGG經過小的卷積覈實現了網絡深度的增長取得了顯著效果,可是當層數過分增長的時候就發現訓練錯誤與測試錯誤都在增長,圖示以下:
html
最開始人們覺得是由於梯度消失或者梯度爆炸致使的,不過隨着你們的努力,認爲這個不是一個過擬合問題,而是網絡褪化現象,因此針對這種狀況,MSRA何凱明團隊提出了一種新的網絡模型-Residual Networks,其主要思想是使用殘差結構來訓練網絡,一個殘差結構以下:
git
做者認爲F(x) = H(x)-x因此獲得H(x) = F(x) + x這樣的恆等映射,而後做者就創建34層plain網絡與34層的殘差網絡做爲對比,而最左邊的VGG-19網絡做爲參考,整個的網絡結構顯示以下:
--- 圖太大啦!!!
模型創建好的以後,做者在不一樣的數據集上進行了訓練與測試,均觀察到殘差網絡的效果要明顯優於34層plain網絡,並且發現基於殘差結構的網絡層數越深效果越好,而34層plain網絡跟18層的plain網絡相比有明顯的褪化現象出現。對比訓練的結果以下:
github
在殘差網絡沒有出來以前,不多有網絡的層數會超過100層,可是殘差網絡能夠達到上千層,毫無疑問何凱明團隊也憑藉殘差網絡模型在2015年的ImageNet圖像分類比賽中得到了冠軍,當時使用152層的殘差網絡。OpenCV中人臉檢測的殘差網絡模型是基於SSD實現的,因此速度仍是挺快的,並且效果是特別的好。廢話很少說了,下面我就看看OpenCV中如何使用它實現人臉檢測。
二:人臉檢測代碼實現
模型是基於Caffe網絡訓練生成的,因此在開始寫程序以前的第一件事情就是要下載模型文件與描述文件,這個我已經下載好了,你們就不用×××了,直接去個人github地址上下載模型文件便可
https://github.com/gloomyfish1998/opencv_tutorial
下載好模型以後放在本地的一個文件夾下便可,而後就能夠開始編程工做啦。
首先須要加載模型成網絡:算法
String modelDesc = "D:/vcprojects/images/dnn/face/deploy.prototxt"; String modelBinary = "D:/vcprojects/images/dnn/face/res10_300x300_ssd_iter_140000.caffemodel"; // 初始化網絡 dnn::Net net = readNetFromCaffe(modelDesc, modelBinary); if (net.empty()) { printf("could not load net...\n"); return -1; }
而後要打開本地相機或者一段視頻文件,使用VideoCapture對象便可,代碼以下:編程
// 打開攝像頭 VideoCapture capture(0); if (!capture.isOpened()) { printf("could not load camera...\n"); return -1; }
打開相機成功以後就能夠讀寫每幀圖像,而後轉換成網絡能夠接受的數據類型,代碼以下:網絡
// 輸入數據調整 Mat inputBlob = blobFromImage(frame, inScaleFactor, Size(inWidth, inHeight), meanVal, false, false); net.setInput(inputBlob, "data");
而後在OpenCV中經過調用net.forward實現檢測,對結果提取置信分數(0~1)之間,對大於閾值(假設0.5)的提取BOX位置,就能夠繪製矩形框顯示了,這部分的代碼以下:ide
// 人臉檢測 Mat detection = net.forward("detection_out"); vector<double> layersTimings; double freq = getTickFrequency() / 1000; double time = net.getPerfProfile(layersTimings) / freq; Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>()); ostringstream ss; ss << "FPS: " << 1000 / time << " ; time: " << time << " ms"; putText(frame, ss.str(), Point(20, 20), 0, 0.5, Scalar(0, 0, 255)); for (int i = 0; i < detectionMat.rows; i++) { float confidence = detectionMat.at<float>(i, 2); if (confidence > confidenceThreshold) { int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols); int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows); int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols); int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows); Rect object((int)xLeftBottom, (int)yLeftBottom, (int)(xRightTop - xLeftBottom), (int)(yRightTop - yLeftBottom)); rectangle(frame, object, Scalar(0, 255, 0)); ss.str(""); ss << confidence; String conf(ss.str()); String label = "Face: " + conf; int baseLine = 0; Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); rectangle(frame, Rect(Point(xLeftBottom, yLeftBottom - labelSize.height), Size(labelSize.width, labelSize.height + baseLine)), Scalar(255, 255, 255), CV_FILLED); putText(frame, label, Point(xLeftBottom, yLeftBottom), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0)); } }
最終運行顯示結果以下, 臉部無遮擋,正常狀況下:
臉部無遮擋,頭部傾斜的狀況下:
臉部有遮擋的狀況下:
更多傾斜、側臉、模糊等各類狀況下:
可見殘差網絡模型是何等的強大,到這裏是否是該點一首《涼涼》送給HAAR級聯檢測器了。上述demo完整源代碼,能夠在GITHUB上下載。
https://github.com/gloomyfish1998/opencv_tutorial學習