1. 程式人生 > >OPENCV自帶級聯分類器程式的訓練與測試

OPENCV自帶級聯分類器程式的訓練與測試

在本文中我將利用 opencv 3.0 自帶級聯分類器將圖片集訓練得出訓練好的分類器,通過程式呼叫的方法呼叫訓練好的分類器進而檢測行人。

首先應找到級聯分類器的位置所在,其位置一般應在opencv安裝的根目錄中,選中我劃出的兩個程式複製到訓練集資料夾中。


兩者的作用分別是:

opencv_createsamples用於準備訓練用的正樣本資料和測試資料,能夠生成能被opencv_traincascade程式支援的正樣本資料。其輸出是以".vec"為副檔名的檔案,該檔案以二進位制方式儲存影象。

opencv_traincascade用於訓練自己的級聯分類器,支援 Haar和 LBP (Local Binary Patterns)兩種特徵,並易於增加其他的特徵。訓練需要訓練資料和測試資料,其中訓練資料包含正樣本pos和負樣本neg。


訓練過程可以分為三個階段:

階段一:訓練資料的準備

階段二:訓練級聯分類器

階段三:利用訓練好的分類器進行檢測

**************************************************************************************************************************

階段一:訓練資料的準備

正樣本是指含有識別物體的圖片,負樣本是不含有識別物體的圖片。負樣本最好是選擇與正樣本關聯性強的資料。

1準備正樣本

正樣本由 opencv_createsamples 程式生成。正樣本可以由包含待檢測物體的一張圖片生成,也可以由一系列標記好的影象生成。

在準備正樣本前應當將所有正樣本圖片進行統一縮放,該尺寸將作為訓練視窗的尺寸。

將所有的正樣本圖片置於一個資料夾中,然後在dos視窗中進入pos資料夾,輸入命令"dir/b > pos.dat"生成檔名列表,通過記事本開啟該檔案,手動修改檔案為如下格式:
檔名 待檢測目標數 ** ** width(輸出樣本的寬度) height(輸出樣本的高度)   

注:**表示未知引數,建議填0,下圖為pos.dat檔案格式


pos.dat檔案準備好後,在dos命令視窗執行opencv_createsamples.exe程式,格式如下:

"opencv_createsamples.exe" -info "pos\pos.dat"   -vec pos.vec -num 500 -w 64 -h 128 
 

opencv_createsamples.exe程式的命令列引數:

  • -info <collection_file_name> 描述物體所在影象以及大小位置的描述檔案。
  •  -vec <vec_file_name>輸出檔案,內含用於訓練的正樣本。
  •  -img <image_file_name>輸入影象檔名(例如一個公司的標誌)。
  •  -bg<background_file_name>背景影象的描述檔案,檔案中包含一系列的影象檔名,這些影象將被隨機選作物體的背景。
  • -num<number_of_samples>生成的正樣本的數目。
  •  -bgcolor<background_color>背景顏色(目前為灰度圖);背景顏色表示透明顏色。因為影象壓縮可造成顏色偏差,顏色的容差可以由-bgthresh指定。所有處於bgcolor-bgthreshbgcolor+bgthresh之間的畫素都被設定為透明畫素。
  • -bgthresh <background_color_threshold>
  •  -inv如果指定該標誌,前景影象的顏色將翻轉。
  • -randinv如果指定該標誌,顏色將隨機地翻轉。
  •  -maxidev<max_intensity_deviation>前景樣本里畫素的亮度梯度的最大值。
  • -maxxangle <max_x_rotation_angle>X軸最大旋轉角度,必須以弧度為單位。
  •  -maxyangle <max_y_rotation_angle>Y軸最大旋轉角度,必須以弧度為單位。
  •  -maxzangle<max_z_rotation_angle>Z軸最大旋轉角度,必須以弧度為單位。
  •  -show很有用的除錯選項。如果指定該選項,每個樣本都將被顯示。如果按下Esc鍵,程式將繼續建立樣本但不再顯示。
  •  -w <sample_width>輸出樣本的寬度(以畫素為單位)。
  • -h<sample_height>輸出樣本的高度(以畫素為單位)。

如下圖(圖是取自其他博主的,做的時候忘了截圖了)


2、準備負樣本

 負樣本可以是任意影象,但是這些影象中不能包含待檢測的物體。用於摳取負樣本的影象檔名被列在一個neg.dat檔案中。生成方式與正樣本相同,但僅僅包含檔名列表就可以了。這個檔案是純文字檔案,每行是一個檔名(包括相對目錄和檔名)這些影象可以是不同的尺寸,但是影象尺寸應該比正樣本圖片的尺寸大,因為這些影象將被用於摳取負樣本,並將負樣本縮小到訓練視窗大小。

階段二:訓練級聯分類器

在DOS視窗中輸入以下語句進行分類器的訓練

"opencv_traincascade.exe"  -data data -vec pos.vec -bg neg\neg.dat -numPos 29 -numNeg 29 -numStages 20 -mem 200 -featureType LBP  -w 64 -h 128
欄位說明如下:
-data:指定儲存訓練結果的資料夾;
-vec:指定正樣本集;
-bg:指定負樣本的描述資料夾;
-numPos:指定每一級參與訓練的正樣本的數目(要小於正樣本總數);
-numNeg:指定每一級參與訓練的負樣本的數目(可以大於負樣本圖片的總數);
-numStage:訓練的級數;
-w:正樣本的寬;
-h:正樣本的高;
-minHitRate:每一級需要達到的命中率(一般取值0.95-0.995);
-maxFalseAlarmRate:每一級所允許的最大誤檢率;
-mode:使用Haar-like特徵時使用,可選BASIC、CORE或者ALL;
另外,還可指定以下欄位:
-featureType:可選HAAR或LBP,預設為HAAR;

其他欄位將不再說明。

儲存,雙擊執行,開始漫長的訓練過程(訓練可中斷,中斷後再執行,會繼續中斷前的訓練)

下圖是取自其他博主的,做的時候忘了截圖了


訓練結束後,data資料夾下會生成如下訓練結果:


階段三:利用訓練好的分類器進行檢測

#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv; 
  
string Cascade_mode ="cascade.xml";   //已訓練好的分類器
CascadeClassifier Mycascade;  
string window_name = "Pedestrain";  
  
Mat detectAndDisplay(Mat);  
  
int main()  
{  
    Mat image,ROI;  
	
    if(!Mycascade.load( Cascade_mode ))  { printf("[error] 無法載入級聯分類器檔案!\n");   return -1;    }  
    image= imread("road.jpg");//讀取圖片  
    if(!image.data)  {   printf("[error] 沒有圖片\n");   return -5;  }  
    ROI= detectAndDisplay(image);  
    waitKey(0);   
    return 4;  
}  
  
Mat detectAndDisplay( Mat frame)  
{  
    std::vector<Rect> pedestrain;  
	Mat frame_gray(frame.size(),CV_8U);  
    cvtColor( frame, frame_gray, CV_BGR2GRAY );  
    equalizeHist( frame_gray, frame_gray );  
  
    Mycascade.detectMultiScale( frame_gray, pedestrain, 1.1, 2, 0, Size(64, 128) );  
  
    for( int i = 0; i < pedestrain.size(); i++ )  {    
        rectangle(frame,                   //影象.  
			      pedestrain[i],
                  Scalar(0, 255, 0),     //線條顏色 (RGB) 或亮度(灰度影象 )(grayscale image)  
                  1);                   //組成矩形的線條的粗細程度。取負值時(如 CV_FILLED)函式繪製填充了色彩的矩形 
    }  
 
  imshow( window_name, frame );  
  return frame;  
}