過去在EmguCV (OpenCV)中
人臉偵測是常用且簡單的功能
過去都是使用Haar cascades的方法來實作
但在OpenCV 3.3.1之後,提供了DNN深度學習的人臉偵測方法
來比較看看吧!!
過去常用的Haar cascades方法需要讀入相對應的分類文件
在OpenCV的github中有提供相當多的類型可以下載
除了人臉之外還有相當多種類可以下載回來測試
https://github.com/opencv/opencv/tree/master/data/haarcascades
而人臉偵測中最常用的是:haarcascade_frontalface_alt2.xml
程式碼非常簡單,在EmguCV的Example裡面都有
這裡簡化一下FaceDetection的程式碼如下:
這個方法僅需帶入要偵測人臉的影像image、
Haar cascades的文件檔路徑、以及一個人臉資訊(Rectangle)的集合
程式的邏輯就是先建立一個CascadeClassifier (cascade分類器)
建構式載入Haar cascades的文件檔
接著將影像從RGB轉成Gray (灰階)
原本的Sample這裡會加入一個EqualizeHist的方法,但我實測是不加入的效果比較好
最後呼叫CascadeClassifier的DetectMultiScale的方法
帶入轉換後的灰階影像以及參數 (預設是1.1、3)
接著把所偵測到的人臉的資訊 (Rectangle)存入faces的List中
我們測試網路上抓的40張藝人照片
並把抓到人臉的部分另存成jpg方便進行比較
處理的程式碼如下:
DetectFace.Detect這個方法就是上面提供的人臉偵測的方法
把讀取資料夾中的影像傳入,並帶入haarcascade_frontalface_alt2.xml (檔案需放在執行目錄下)
若是偵測的人臉的長寬皆超過60 pixels才進行處理
這裡處理的動作是進行resize到160x160
(未來在解釋為什麼要resize到160x160)
接著另存檔案到result資料夾
然後來看看結果:
看起來是有抓到不少人臉
但是也有很多不是人臉的部分... XDD
這邊建議DetectMultiScale的參數改成帶入1.3、3 試試看
錯誤率降低了不少,來看看結果:
錯誤率是大幅降低了,但人臉偵測出來的結果就少了一些
統計一下執行人臉偵測方法的平均時間,以及抓取率
Haar cascades 1.1偵測方法: 526ms, 人臉抓取率: 33/40, 人臉錯誤率: 17/50
Haar cascades 1.3偵測方法: 327ms, 人臉抓取率: 30/40, 人臉錯誤率: 3/33
抓取率只有75%~82.5%,錯誤率9%~34%
這樣的結果,僅能算是堪用吧...
而且效能也不太好
過去若要提升人臉偵測的效能,需在影像傳入Detect前,先進行resize的動作
即可減少運算時間
而在OpenCV 3.3.1 後導入了深度學習(DNN)的模組,
包含了:tensorflow、caffe、darknet
(你沒看錯,連darknet都有!!)
而tensorflow、keras提供了人臉偵測的方法和模型
模型的下載位置可以參考這裡:
(此專案也有C++跟Python的程式碼可參考)
https://github.com/spmallick/learnopencv/tree/master/FaceDetectionComparison/models
主要下載這四個檔案
紅色圈選的是caffe的模型和參數檔
綠色則是tensorflow的模型和參數檔
執行的程式碼可參考下圖:
程式稍微複雜一點
這份程式,應該是世界上第一篇用EmguCV寫的DNN人臉偵測的範例分享了!
(網路上只找得到C++跟Python ...)
簡單來說,就是先建立Net物件,建立方法使用
DnnInvoke.ReadNetFromTensorflow({模型檔案(.pb)},{參數檔(.pbtxt)})
接著進行影像的轉換,把原先EmguCV的Image.Mat轉換成Net可以處理的Mat
使用方法為
Mat inputBlob = DnnInvoke.BlobFromImage(image, 1.0, new Size(300, 300),
new MCvScalar(104.0, 117.0, 123.0), true, false);
這裡Size設定為300x300,則是根據DNN model訓練時的影像大小
而剛剛下載的opencv_face_detector_uint8.pb就是使用300x300的大小
網路上很多參數給的都是錯誤的值
所以直接參考我這邊的寫法即可!
接著設定input,執行Forward後取得運算結果detection
這裡的結果會是一個4維陣列[1, 1, {face count}, 7]
我們只要取出第三跟第四維的資料
利用以下方法,把2維資料轉存到一個1維的陣列temp
int resultRows = detection.SizeOfDimemsion[2]; //偵測到的人臉數量
int resultCols = detection.SizeOfDimemsion[3]; //人臉偵測結果的值(7個)
float[] temp = new float[resultRows * resultCols];
Marshal.Copy(detection.DataPointer, temp, 0, temp.Length);
接著透過for迴圈,把所有人臉偵測的結果都取出
人臉偵測結果的值共七個[0, 1, 2, 3, 4, 5, 6, 7]
其中第三個值(2的位置):人臉偵測的信任分數,越高表示越像人臉
(越低就可能不是人臉)
float confidence = temp[i * resultCols + 2];
confidence 的閥值可設定在0.6~0.8之間
高過閥值的,再進行人臉位置(rectangle)的擷取
第四~七的值(3,4,5,6的位置)分別代表人臉的左上角x、y,右下角x、y的比例
因此計算方式把值取出後,需要再乘上影像的長或寬,才會是實際的座標值
例如其中一筆的結果如下:
[0, 1, 0.89, 0.51, 0.22, 0.59, 0.30]
紅色0.89就是人臉偵測的信任分數
這張測試照片的影像寬高分別是1364、2048,因此計算出來的座標會是:
(1364x0.51, 2048x0.22)、(1364x0.59, 2048x0.30) = (696, 467)、(811, 632)
這兩個點所包圍的矩形,就是人臉的位置
一樣透過之前的測試,把人臉偵測的方法改成剛剛DNN的方式
讀取40張藝人的照片並另存人臉的照片出來比較,結果如下:
有沒有看到這驚人的結果!! 幾乎沒有發生抓錯的!!
統計資料如下:
DNN tensorflow偵測方法: 79ms, 人臉抓取率: 39/40, 人臉錯誤率: 0
抓取率: 97.5%,錯誤率是0
若想要改用caffe的方法
就把剛剛tensorflow的程式碼
前面兩段改成:
(注意紅色標記的部分)
net = DnnInvoke.ReadNetFromCaffe(
@"model\deploy.prototxt",
@"model\res10_300x300_ssd_iter_140000_fp16.caffemodel");
Mat inputBlob = DnnInvoke.BlobFromImage(image, 1.0, new Size(300, 300),
new MCvScalar(104.0, 117.0, 123.0), false, false);
其他程式都是一樣的
DNN Caffe偵測方法: 78ms, 人臉抓取率: 38/40, 人臉錯誤率: 0
抓取率: 95%,也是非常高!
這個實測的結果真的讓人大吃一驚
採用DNN演算法的方式,偵測的結果比較好可以理解
但連效能竟然還比傳統的Haar cascades 還快!!
在同樣採用i7-8650U CPU的運算
Haar cascades 帶入1.3的參數,平均一張影像處理耗時要300多ms
而DNN演算法僅需70多ms
這真的是非常驚人的進步和成果呀!!
傳統Haar cascades演算法過去為人詬病的偵測率和錯誤率
在DNN深度學習的方法下
完全改進缺點,還提高效率,是不是非常令人驚艷呢!!
你好, 我照著你上方的指導, 但我在DNNINVOKE要把.pb那些讀出時,就會出現無法open的問題
回覆刪除Emgu.CV.Util.CvException: 'OpenCV: FAILED: fs.is_open(). Can't open "model\opencv_face_detector_uint8.pb"'
請問你也有遇到類似問題或者知道我哪出問題了呢?? 謝謝
要不要檢查一下model檔案的路徑,有確定把.pb跟pbtxt放在執行目錄裡面model資料夾中嗎?
刪除你好,看了你的文章有一些設定不太清楚,方便交換mail或是line請教嗎??
回覆刪除你好,我也是看你的文章執行,但還是沒辦法完成,方便交換mail或是line請教嗎??
回覆刪除