1. 程式人生 > >使用opencv的Haar訓練自己的人臉分類器

使用opencv的Haar訓練自己的人臉分類器

以下是我學習的幾個博文:

http://blog.csdn.net/u010603823/article/details/52557760

1.準備訓練樣本圖片

1.1樣本的採集:

      樣本圖片最好使用灰度圖,且最好根據實際情況做一定的預處理;樣本數量越多越好,儘量高於1000,樣本間差異性越大越好

正負樣本比例為13最佳尺寸為20x20最佳。

1.1.1正樣本

        訓練樣本的尺寸為20*20(opencv推薦的最佳尺寸),且所有樣本的尺寸必須一致。如果不一致的或者尺寸較大的,可以先將所有樣本統一縮放到20*20。   

        以下就是我用來訓練的樣本

    

1.1.2負樣本

       這裡要提醒一下,雖然負樣本就是樣本中不存在正樣本的內容。但也不能隨意的找些圖片來作為負樣本,比如什麼天空,大海,森林等等。最好是根據不同的專案選擇不同的負樣本,比如一個專案是做機場的人臉檢測,那麼就最好從現場拍攝一些圖片資料回來,從中採集負樣本。其實正樣本的採集也應該這樣。不同的專案,就採集不同的正樣本和負樣本。因為專案不同,往往相機的安裝規範不同,場景的拍攝角度就不同。

        關於負樣本的尺寸,只要不小於正樣本的尺寸就好。至於為什麼,我沒有深究,知道原因的也可以講講。

       以下就是我用來訓練的樣本


1.1.3 準備好工作目錄

negdata目錄:

放負樣本的目錄

posdata目錄: 放正樣本的目錄

xml目錄: 新建的一個目錄,為之後存放分類器檔案使用

negdata.txt: 負樣本路徑列表

posdata.txt: 正樣本路徑列表

pos.vec: 後續自動生成的樣本描述檔案

opencv_createsamples.exe: 生成樣本描述檔案的可執行程式(opencv自帶)

opencv_haartraining.exe: 樣本訓練的可執行程式(opencv自帶)


1.1.4獲取樣本路徑列表

        接下來就需要或者正負樣本的路徑列表,方便訓練的時候能夠找到每一個樣本。

       先進入正樣本資料的目錄下(posdata

目錄),新建一個文字文件,然後把副檔名修改為bat,然後編輯文字如下:


     編輯完成後,雙擊該檔案。就會在該目錄下生成一個num.txt,如下:


       開啟num.txt,使用文字編輯器的替換功能,做一些替換工作

       替換1:將絕對路徑替換成相對路徑

      

  

        替換21代表個數,後四個分別對應left top width height

      

       將替換好的num.txt複製到posdata目錄的同級目錄下,重新命名為posdata.txt

       同理,負樣本的路徑列表按照上述方法進行。在negdata目錄下準備好num.txt後,複製到negdata目錄的同級目錄下,重命令為negdata.txt

2.準備樣本描述檔案

      樣本描述檔案就是一個.vec檔案opencv訓練準備的,只有正樣本需要,負樣本不需要

       開啟cmd.exe,進入到工作目錄下,執行以下命令

opencv_createsamples.exe-info  posdata.txt-vec pos.vec -num 17 -w 20 -h 20

-info,指樣本說明檔案

-vec,樣本描述檔案的名字及路徑

-num,總共幾個樣本,要注意,這裡的樣本數是指標定後的20x20的樣本數,而不是大圖的數目,其實就是樣本說明檔案第2列的所有數字累加

-w-h指明想讓樣本縮放到什麼尺寸。這裡的奧妙在於你不必另外去處理第1步中被矩形框出的圖片的尺寸,因為這個引數幫你統一縮放!(我們這裡準備的樣本都是20*20)

   

        執行結束後,會在工作目錄下自動生成一個pos.vec檔案

3.樣本訓練

       樣本訓練需要用到的工具:(1)opencv_haartraining.exe,opencv自帶的一個工具該工具封裝了haar特徵提取以及adaboost分類器訓練過程 (2)convert_casade.exe用於合併各級分類器成為最終的xml檔案.

      開啟cmd.exe進入工作目錄,執行以下命令

opcnv_haartraining.exe–data xml –vec pos.vec –bg negdata.txt –nsplits 1 –sym –w24 –h 24 –mode all –mem 1280

-data:           指定生成的檔案目錄(將來存放各級分類的地方)

-vec:           樣本描述檔案
-bg              負樣本描述檔名稱,就是負樣本的路徑列表
-nstage 20       指定訓練層數,推薦15~20,層數越高,耗時越長。
-nsplits         分裂子節點數目,選取預設值 2 1表示使用簡單的stump classfier分類
-minhitrate      最小命中率,即訓練目標準確度。
-maxfalsealarm   最大虛警(誤檢率),每一層訓練到這個值小於0.5時訓練結束,進入下一層訓練
-npos            每個階段用來訓練的正樣本數目
-nneg          每個階段用來訓練的負樣本數目
-mode          all指定haar特徵的種類,basic僅僅使用垂直特徵,all表示使用垂直以及45度旋轉特徵
-sym或者-nonsym:後面不用跟其他引數,用於指定目標物件是否垂直對稱,若你的物件是垂直對稱的,比如臉,則垂直對稱有利於提高訓練速度

-mem:           表示允許使用計算機的1280M記憶體


3.1訓練過程

  在訓練過程中可能會遇到很多問題,大家如果遇到什麼問題,就直接到網搜,肯定是搜得到的,我這裡把我遇到的問題貼出來,我開始訓練後,大約過了20幾分鐘,程式就卡住了,感覺進入死迴圈,出不來了。如下圖所示。


       但是進入xml目錄下,已經生成了10個目錄(這10個就是弱分類器),通過網上查詢,解決如下:

       訓練到第10層後再也沒有反應,這是由於FA值已經達到0,沒有負樣本能夠進入下一層進行訓練了。

         解決方案之一是增加負樣本的數量。

解決方案之二是convert_cascade.exe生成xml檔案。因為此時各層的訓練資訊已經有了,FA值達到0也說明訓練結果可用。同樣   適用於FA值已經很低,而下一層的訓練時間過長不想等待的情況。

    因為沒有找到convert_cascade.exe這個可執行檔案,在網上搜到了這個原始碼,將其寫成一個函式,然後直接呼叫即可,程式碼如下

intcascade()

{

char*haartraining_ouput_dir ="G://圖片資料//人臉//face//xml"; //根據實際情況修改

char*ouput_file ="G://圖片資料//人臉//face//xml//haar_adaboost.xml";//根據實際情況修改

constchar*size_opt = "--size=";

charcomment[1024];

CvHaarClassifierCascade*cascade = 0;

CvSizesize;

size.width= 20; //根據實際情況修改

size.height= 20; //根據實際情況修改

cascade= cvLoadHaarClassifierCascade(haartraining_ouput_dir,size);

if(!cascade ){

fprintf(stderr,"Inputcascade could not be found/opened\n");

return-1;

}

sprintf(comment, "Automaticallyconverted from %s, window size = %dx%d",ouput_file, size.width, size.height );

cvSave(ouput_file, cascade, 0, comment, cvAttrList(0,0) );

return0;

}



4.開始測試


inthaarDection()

{

Matimage;

CascadeClassifiercascade, nestedCascade;//建立級聯分類器物件

doublescale = 1.3;

// image = imread("obama_gray.bmp",1);

image= imread("D://haarcascade_frontalface_alt.jpg",1);

namedWindow("result",1 );//opencv2.0以後用namedWindow函式會自動銷燬視窗

if(!cascade.load( cascadeName ) )//從指定的檔案目錄中載入級聯分類器

{

cerr<< "ERROR:Could not load classifier cascade"<< endl;

return0;

}

if(!image.empty() )//讀取圖片資料不能為空

{

detectAndDraw(image, cascade, scale );

IplImageqImg;

qImg= IplImage(image);

cvSaveImage("D://aaa.jpg",&qImg);

waitKey(0);

}

return0;

}

voiddetectAndDraw( Mat&img,

CascadeClassifier&cascade,

doublescale)

{

inti = 0;

doublet = 0;

vector<Rect>faces;

conststaticScalarcolors[] = { CV_RGB(0,0,255),

CV_RGB(0,128,255),

CV_RGB(0,255,255),

CV_RGB(0,255,0),

CV_RGB(255,128,0),

CV_RGB(255,255,0),

CV_RGB(255,0,0),

CV_RGB(255,0,255)};//用不同的顏色表示不同的人臉

Matgray, smallImg( cvRound (img.rows/scale),cvRound(img.cols/scale),CV_8UC1);//將圖片縮小,加快檢測速度

cvtColor(img,gray, CV_BGR2GRAY);//因為用的是類haar特徵,所以都是基於灰度影象的,這裡要轉換成灰度影象

resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);//將尺寸縮小到1/scale,用線性插值

equalizeHist(smallImg, smallImg );//直方圖均衡

t= (double)cvGetTickCount();//用來計算演算法執行時間

//檢測人臉

//detectMultiScale函式中smallImg表示的是要檢測的輸入影象為smallImgfaces表示檢測到的人臉目標序列,1.1表示

//每次影象尺寸減小的比例為1.12表示每一個候選矩形需要記錄2個鄰居,CV_HAAR_SCALE_IMAGE表示使用haar特徵,Size(30,30)

//為目標的最小最大尺寸

cascade.detectMultiScale(smallImg, faces,

1.1,2, 0

//|CV_HAAR_FIND_BIGGEST_OBJECT

//|CV_HAAR_DO_ROUGH_SEARCH

|CV_HAAR_SCALE_IMAGE

,

Size(30,30) );

t= (double)cvGetTickCount()- t;//相減為演算法執行的時間

printf("detectiontime = %g ms\n",t/((double)cvGetTickFrequency()*1000.));

/*

for(vector<Rect>::const_iterator r = faces.begin(); r !=faces.end(); r++, i++ )

{

MatsmallImgROI;

vector<Rect>nestedObjects;

Pointcenter;

Scalarcolor = colors[i%8];

intradius;