【dlib代碼解讀】人臉檢測器的訓練【轉】
轉自:http://blog.csdn.net/elaine_bao/article/details/53046542
版權聲明:本文為博主原創文章,轉載請註明。
目錄(?)[-]
- 綜述
- 代碼解讀 step by step
- 1 預處理階段
- 11 載入訓練集測試集
- 12 圖片上采樣
- 13 鏡像圖片
- 2 訓練階段
- 21 定義scanner用於掃描圖片並提取特征
- 22 設置scanner掃描窗口大小
- 23 定義trainer用於訓練人臉檢測器
- 24 訓練生成人臉檢測器
- 25 測試
- 3 tips
- 31 模型的存儲
- 32 多個detector聯合使用
1. 綜述
首先給出實驗結果。訓練得到的基於hog的人臉檢測器如圖左,可以看出這是一個主要針對正臉的人臉檢測器。對幾張測試圖片的人臉檢測效果如圖右:
以下給出完整的人臉檢測器訓練代碼(詳細代碼解讀請看第2部分):
/*faceDetectorTrain.cpp
function:借助dlib訓練自己的人臉檢測器(參考dlib/examples/fhog_object_detector_ex)
date:2016/11/5
author:Elaine_Bao
*/
#include <dlib/svm_threaded.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_processing.h>
#include <dlib/data_io.h>
#include <iostream>
#include <fstream>
using namespace std;
using namespace dlib;
// ----------------------------------------------------------------------------------------
int main(int argc, char** argv)
{
try
{
//一、preprocessing
//1. 載入訓練集,測試集
const std::string faces_directory = "faces";
dlib::array<array2d<unsigned char> > images_train, images_test;
std::vector<std::vector<rectangle> > face_boxes_train, face_boxes_test;
load_image_dataset(images_train, face_boxes_train, faces_directory + "/training.xml");
load_image_dataset(images_test, face_boxes_test, faces_directory + "/testing.xml");
//2.圖片上采樣
upsample_image_dataset<pyramid_down<2> >(images_train, face_boxes_train);
upsample_image_dataset<pyramid_down<2> >(images_test, face_boxes_test);
//3.訓練圖片做鏡像處理,擴充訓練集
add_image_left_right_flips(images_train, face_boxes_train);
//二、training
//1.定義scanner類型,用於掃描圖片並提取特征(HOG)
typedef scan_fhog_pyramid<pyramid_down<6> > image_scanner_type;
image_scanner_type scanner;
//2. 設置scanner掃描窗口大小
scanner.set_detection_window_size(80, 80);
//3.定義trainer類型(SVM),用於訓練人臉檢測器
structural_object_detection_trainer<image_scanner_type> trainer(scanner);
// Set this to the number of processing cores on your machine.
trainer.set_num_threads(4);
// 設置SVM的參數C,C越大表示更好地去擬合訓練集,當然也有可能造成過擬合。通過嘗試不同C在測試集上的效果得到最佳值
trainer.set_c(1);
trainer.be_verbose();
//設置訓練結束條件,"risk gap"<0.01時訓練結束,值越小表示SVM優化問題越精確,訓練時間也會越久。
//通常取0.1-0.01.在verbose模式下每一輪的risk gap都會打印出來。
trainer.set_epsilon(0.01);
//4.訓練,生成object_detector
object_detector<image_scanner_type> detector = trainer.train(images_train, face_boxes_train);
//三、測試
// 輸出precision, recall, average precision.
cout << "training results: " << test_object_detection_function(detector, images_train, face_boxes_train) << endl;
cout << "testing results: " << test_object_detection_function(detector, images_test, face_boxes_test) << endl;
//顯示hog
image_window hogwin(draw_fhog(detector), "Learned fHOG detector");
// 顯示測試集的人臉檢測結果
image_window win;
for (unsigned long i = 0; i < images_test.size(); ++i)
{
// Run the detector and get the face detections.
std::vector<rectangle> dets = detector(images_test[i]);
win.clear_overlay();
win.set_image(images_test[i]);
win.add_overlay(dets, rgb_pixel(255, 0, 0));
cout << "Hit enter to process the next image..." << endl;
cin.get();
}
//四、模型存儲
serialize("face_detector.svm") << detector;
// you can recall it using the deserialize() function.
object_detector<image_scanner_type> detector2;
deserialize("face_detector.svm") >> detector2;
}
catch (exception& e)
{
cout << "\nexception thrown!" << endl;
cout << e.what() << endl;
}
}
2. 代碼解讀 step by step
2.1 預處理階段
2.1.1 載入訓練集、測試集
const std::string faces_directory = "faces";
dlib::array<array2d<unsigned char> > images_train, images_test;
std::vector<std::vector<rectangle> > face_boxes_train, face_boxes_test;
load_image_dataset(images_train, face_boxes_train, faces_directory + "/training.xml");
load_image_dataset(images_test, face_boxes_test, faces_directory + "/testing.xml");
訓練集和測試集圖片存儲在”faces”文件夾下,另外該文件夾下還需包含training.xml,testing.xml,包含圖片中人臉bounding box的位置。組織形式為:
2.1.2 圖片上采樣
upsample_image_dataset<pyramid_down<2> >(images_train, face_boxes_train);
upsample_image_dataset<pyramid_down<2> >(images_test, face_boxes_test);
即圖片放大兩倍,這樣有助於檢測較小的人臉。
上述函數在對圖片進行上采樣的同時,也相應地調整了人臉bounding box的位置。
2.1.3 鏡像圖片
add_image_left_right_flips(images_train, face_boxes_train);
- 1
- 1
對訓練所用圖片做鏡像處理,擴充訓練集的訓練樣本。
2.2 訓練階段
2.2.1 定義scanner,用於掃描圖片並提取特征
typedef scan_fhog_pyramid<pyramid_down<6>> image_scanner_type;
image_scanner_type scanner;
class scan_fhog_pyramid定義來自於scan_fhog_pyramid.h,原型如下:
template <typename Pyramid_type, typename Feature_extractor_type = default_fhog_feature_extractor>
class scan_fhog_pyramid{...}
- 1
- 2
- 1
- 2
類模板中參數1表示圖像金字塔的類型,本文使用的是pyramid_down<6>,表示圖像金字塔進行下采樣的比率為5/6,即對原圖像不斷縮小5/6,構成多級金字塔。什麽時候停止下采樣呢?當圖像的大小<掃描窗口大小的時候。
參數2表示特征提取器,默認情況下使用fhog.h中的extract_fhog_feature()提取特征,函數原型為:
void extract_fhog_features(
const image_type& img,
array2d<matrix<T,31,1>,mm>& hog,
int cell_size = 8,
int filter_rows_padding = 1,
int filter_cols_padding = 1
);
此函數提取的HOG特征來自於Felzenszwalb版本的HOG [1] (簡稱fhog),它是對每個8*8像素大小的cell提取31維的fhog算子,然後保存到上述hog array中供後續計算使用。
31維fhog如何得到?如下圖(此圖來自於http://blog.csdn.net/qq_14845119/article/details/52625426)。
31D fhog=18D+9D+4D。
(1) 18D來自於對cell做18個bin的梯度方向直方圖,即將360°劃分為18個bin,然後令cell中的每個像素根據其梯度方向加權投影到直方圖相應的bin中,這樣就得到了18維有符號的fhog梯度。
(2) 9D來自於對cell做9個bin的梯度方向直方圖,此時是將180°劃分為9個bin,則得到無符號的9維fhog梯度。
(3) 最後的4D是來自於當前cell和其對角線臨域的4個領域cell的歸一化操作。具體地,取block=2*2 cell,則得到無符號fhog梯度4*9維,將其看成矩陣做按行按列累加可得到1D特征,4個領域則可得到4個block,共4維特征。
最終,每個cell的31維fhog特征就來自於上述三部分的串聯。
2.2.2 設置scanner掃描窗口大小
scanner.set_detection_window_size(80, 80);
- 1
- 1
設置掃描窗口大小為80*80,即掃描窗口是固定大小的,通過放縮圖像(圖像金字塔)以達到在不同尺度上檢測人臉的目的。
由於在預處理中圖像放大了2倍,則圖像中最小能檢測到的人臉大小為40*40。
2.2.3 定義trainer,用於訓練人臉檢測器
structural_object_detection_trainer<image_scanner_type> trainer(scanner);
// 設置訓練參數
trainer.set_num_threads(4);
// 設置SVM的參數C,C越大表示更好地去擬合訓練集,當然也有可能造成過擬合。通過嘗試不同C在測試集上的效果得到最佳值
trainer.set_c(1);
trainer.be_verbose();
//設置訓練結束條件,"risk gap"<0.01時訓練結束,值越小表示SVM優化問題越精確,訓練時間也會越久。
//通常取0.1-0.01.在verbose模式下每一輪的risk gap都會打印出來。
trainer.set_epsilon(0.01);
structural_object_detection_trainer定義來自於structural_object_detection_trainer.h,通過scanner來初始化trainer。
2.2.4 訓練,生成人臉檢測器
object_detector<image_scanner_type> detector = trainer.train(images_train, face_boxes_train);
- 1
- 1
train()的函數原型在structural_object_detection_trainer.h有4個,這裏用的是其中一個,即輸入參數為圖片和圖片中人臉的正確位置,輸出一個object_detector。
註意這種情況下除了已經標出的人臉位置為正樣本以外,圖片的其他位置隨機取負樣本,因此在標註訓練圖片的人臉時,應確保所有人臉都已標註出來。如果出現不太確定是不是人臉的圖片,應調用另一個版本的train()函數並設置ignore區域。
關於train()的內部實現,其實就是訓練一個SVM模型,當模型loss<之前所設的epsilon的時候,輸出訓練好的模型到object_detector。
2.2.5 測試
// 顯示測試集的人臉檢測結果
image_window win;
for (unsigned long i = 0; i < images_test.size(); ++i)
{
// Run the detector and get the face detections.
std::vector<rectangle> dets = detector(images_test[i]);
win.clear_overlay();
win.set_image(images_test[i]);
win.add_overlay(dets, rgb_pixel(255, 0, 0));
cout << "Hit enter to process the next image..." << endl;
cin.get();
}
detector()函數直接返回圖片中所有檢測到的人臉的bounding box信息。
2.3 tips
2.3.1 模型的存儲
通過serialize()可以實現對模型的保存,而deserialize()可以將磁盤中保存的模型取出來用。
serialize("face_detector.svm") << detector;
// Then you can recall it using the deserialize() function.
object_detector<image_scanner_type> detector2;
deserialize("face_detector.svm") >> detector2;
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
2.3.2 多個detector聯合使用
在文章開篇的時候提到上述人臉檢測器比較適用於正臉的檢測,對於側臉的檢測效果並不好。那麽我們可以單獨訓練側臉的人臉檢測器,然後多個detector聯合使用。
std::vector<object_detector<image_scanner_type> > my_detectors;
my_detectors.push_back(detector);
std::vector<rectangle> dets = evaluate_detectors(my_detectors, images_train[0]);
- 1
- 2
- 3
- 1
- 2
- 3
串聯所有人臉檢測器一起使用的好處是,全圖的fhog特征只需要提取一遍即可。
例如,在dlib自帶的人臉檢測器中就用了5個HOG-based detector,分別用於檢測front looking, left looking, right looking, front looking but rotated left, and a front looking but rotated right one. 下面是這五個detector的訓練參數log:
/*
The front detector:
trained on mirrored set of labeled_faces_in_the_wild/frontal_faces.xml
upsampled each image by 2:1
used pyramid_down<6>
loss per missed target: 1
epsilon: 0.05
padding: 0
detection window size: 80 80
C: 700
nuclear norm regularizer: 9
cell_size: 8
num filters: 78
num images: 4748
Train detector (precision,recall,AP): 0.999793 0.895517 0.895368
singular value threshold: 0.15
The left detector:
trained on labeled_faces_in_the_wild/left_faces.xml
upsampled each image by 2:1
used pyramid_down<6>
loss per missed target: 2
epsilon: 0.05
padding: 0
detection window size: 80 80
C: 250
nuclear norm regularizer: 8
cell_size: 8
num filters: 63
num images: 493
Train detector (precision,recall,AP): 0.991803 0.86019 0.859486
singular value threshold: 0.15
The right detector:
trained left-right flip of labeled_faces_in_the_wild/left_faces.xml
upsampled each image by 2:1
used pyramid_down<6>
loss per missed target: 2
epsilon: 0.05
padding: 0
detection window size: 80 80
C: 250
nuclear norm regularizer: 8
cell_size: 8
num filters: 66
num images: 493
Train detector (precision,recall,AP): 0.991781 0.85782 0.857341
singular value threshold: 0.19
The front-rotate-left detector:
trained on mirrored set of labeled_faces_in_the_wild/frontal_faces.xml
upsampled each image by 2:1
used pyramid_down<6>
rotated left 27 degrees
loss per missed target: 1
epsilon: 0.05
padding: 0
detection window size: 80 80
C: 700
nuclear norm regularizer: 9
cell_size: 8
num images: 4748
singular value threshold: 0.12
The front-rotate-right detector:
trained on mirrored set of labeled_faces_in_the_wild/frontal_faces.xml
upsampled each image by 2:1
used pyramid_down<6>
rotated right 27 degrees
loss per missed target: 1
epsilon: 0.05
padding: 0
detection window size: 80 80
C: 700
nuclear norm regularizer: 9
cell_size: 8
num filters: 89
num images: 4748
Train detector (precision,recall,AP): 1 0.897369 0.897369
singular value threshold: 0.15
*/
[1] Object Detection with Discriminatively Trained Part Based Models by P. Felzenszwalb, R. Girshick, D. McAllester, D. Ramanan, IEEE Transactions on Pattern Analysis and Machine Intelligence, Vol. 32, No. 9, Sep. 2010。
【dlib代碼解讀】人臉檢測器的訓練【轉】