採用opencv_cascadetrain進行訓練的步驟及注意事項
OpenCV中有兩個程式可以訓練級聯分類器: opencv_haartraining 和opencv_traincascade。opencv_traincascade 是一個新程式,使用OpenCV 2.x API 以C++ 編寫。這二者主要的區別是 opencv_traincascade 支援 Haar、Hog和 LBP(Local Binary Patterns) 三種特徵,並易於增加其他的特徵。與Haar特徵相比,LBP特徵是整數特徵,因此訓練和檢測過程都會比Haar特徵快幾倍。LBP和Haar特徵用於檢測的準確率,是依賴訓練過程中的訓練資料的質量和訓練引數。訓練一個與基於Haar特徵同樣準確度的LBP的分類器是可能的。
opencv_traincascade and opencv_haartraining 所輸出的分類器檔案格式並不相同。注意,新的級聯檢測介面(參考 objdetect 模組中的 CascadeClassifier 類)支援這兩種格式。 opencv_traincascade 可以舊格式匯出訓練好的級聯分類器。但是在訓練過程被中斷後再重啟訓練過程, opencv_traincascade and opencv_haartraining 不能裝載與中斷前不同的檔案格式。
opencv_traincascade 程式使用TBB來處理多執行緒。如果希望使用多核並行運算加速,請使用TBB來編譯OpenCV。還有一些與訓練相關的輔助程式。
opencv_createsamples 用來準備訓練用的正樣本資料和測試資料。 opencv_createsamples 能夠生成能被 opencv_haartraining 和 opencv_traincascade 程式支援的正樣本資料。它的輸出為以 *.vec 為副檔名的檔案,該檔案以二進位制方式儲存影象。
opencv_performance 可以用來評估分類器的質量。它讀入一組標註好的影象,執行分類器並報告效能,如檢測到物體的數目,漏檢的數目,誤檢的數目,以及其他資訊。
1 建立樣本集pos和neg
pos中存放正樣本的影象,可以是一張影象或多張影象。neg中存放包含負樣本的影象,可以是任意影象,但是這些影象中不能包含待檢測的物體。
注意:
一般需要一個很大的負樣本庫送給訓練程式進行訓練。如果是絕對剛性的物體,如OpenCV的標誌,只有一張正樣本影象,那麼可以通過對物體影象的隨機旋轉、改變標誌亮度以及將標誌放在任意的背景上而獲得大量的正樣本;如果是人臉,需要幾百甚至幾千個正樣本。在待檢測物體是人臉的情況下,需要考慮所有的人種、年齡、表情甚至鬍子的樣式。
2 生成正\負樣本描述檔案negdata.txt
(1) 負樣本描述檔案negdata.txt
在命令列視窗輸入“cd d:\%....%\pos”路徑切換到neg資料夾下,輸入“dir/b>negdata.txt”就會在neg資料夾下生成描述檔案negdata.txt,存放neg中所有圖片的檔名,注意要去掉最後的negdata.txt 。
注意:
如果把negdata.txt儲存在neg資料夾外面,需要在每一行外面新增相對路徑或絕對路徑來指出樣本的所在位置。一種方法是將negdata.txt 檔案中的內容複製到word中,使用替換功能實現快速修改。neg替換為neg\neg
(2) 正樣本描述檔案posdata.txt
與建立negdata.txt一樣建立posdata.txt,只不過在正樣本描述檔案中需要指出目標在每個樣本中的數量和位置如pos\1.bmp 1 x1 y1 x2 y2,其中(x1,y1,x2,y2)為目標所在的矩形框,又如pos\1.bmp2 x1 y1 x2 y2 x1’ y1’ x2’ y2’
因為我們準備的正樣本基本都是目標,因此只需在檔名後增加1 0 0 width height即可。
注意:
1若正樣本影象是不同尺寸的,一方面可以使用ImageResize或matlab將影象統一成同一尺寸在生成posdata.txt,或程式ImageToTxt直接生成具有不同尺寸影象的正樣本描述檔案。在此階段不歸一化的話,後續生成.vec檔案的時候在程式中自動歸一化。
2樣本描述檔案與影象要一致,可以存在影象但沒有寫在描述檔案中,即有多餘的影象,但千萬不要在描述檔案中寫不存在的影象。
問題:
歸一化尺寸的方法是否對訓練結果有影響? opencv_createsamples 採用何種方法進行歸一化?
sample = cvCreateImage( cvSize(winwidth, winheight ), IPL_DEPTH_8U, 1 );
fscanf( info, "%d %d %d%d", &x, &y, &width, &height )
cvSetImageROI( src, cvRect( x, y, width,height ) );
cvResize( src, sample,
width >=sample->width && height >= sample->height ? CV_INTER_AREA :CV_INTER_LINEAR );
CV_INTER_NN 最近鄰插值
CV_INTER_LINER 雙線性插值,預設情況
CV_INTER_AREA 使用畫素關係重取樣,當影象縮小時候可以避免波紋出現,當影象方法時類似CV_INTER_NN
CV_INTER_CUBIC 立方插值
3 生成.vec檔案
生成的正樣本數目以及隨機的程度都可以通過 opencv_createsamples 的命令列引數控制。
使用create.dat呼叫%Opencv%\vs2008\bin\Release\ opencv_createsamples.exe
在createsamples.cpp中查閱引數設定
-info 輸入正樣本描述檔案,預設NULL
-img 輸入影象檔名,預設NULL
-bg 負樣本描述檔案,檔案中包含一系列的被隨機選作物體背景的影象檔名,預設NULL
-num 生成正樣本的數目,預設1000
-bgcolor 背景顏色,表示透明顏色,預設0
-bgthresh 顏色容差,所有處於bgcolor-bgthresh和bgcolor+bgthresh之間的畫素被置為透明畫素,也就是將白噪聲加到前景影象上,預設80
-inv 前景影象顏色翻轉標誌,如果指定顏色翻轉,預設0(不翻轉)
-randinv 如果指定顏色將隨機翻轉,預設0
-maxidev 前景影象中畫素的亮度梯度最大值,預設40
-maxxangle X軸最大旋轉角度,以弧度為單位,預設1.1
-maxyangle Y軸最大旋轉角度,以弧度為單位,預設1.1
-maxzangle Z軸最大旋轉角度,以弧度為單位,預設0.5
輸入影象沿著三個軸進行旋轉,旋轉角度由上述3個值限定。
-show 如果指定,每個樣本都將被顯示,按下Esc鍵,程式將繼續建立樣本而不在顯示,預設為0(不顯示)
-scale 顯示影象的縮放比例,預設4.0
-w 輸出樣本寬度,預設24
-h 輸出樣本高度,預設24
-vec 輸出用於訓練的.vec檔案,預設NULL
將正樣本描述檔案中的正樣本轉換為灰度影象並縮放到-w-h尺寸存入vec檔案中。
(1)如果設定-img和-vec
呼叫cvCreateTrainingSamples,採用一張影象建立訓練樣本
(2)如果設定-img、-bg和-info
呼叫cvCreateTestSamples,採用一張影象建立測試樣本。-bg在這裡又有什麼作用?目的是作為背景建立測試影象。
(3)如果設定-info和-vec(採用正樣本描述檔案中的影象建立訓練樣本)
呼叫cvCreateTrainingSamplesFromInfo,在cvCreateTrainingSamplesFromInfo中將讀取樣本,並resize後呼叫icvWriteVecHeader和icvWriteVecSample建立vec檔案。
(4)如果只設置-vec(只顯示vec檔案中的樣本)
呼叫cvShowVecSamples檢視和檢查儲存在vec檔案中正樣本
上述引數在create.dat中設定好,最後有一個pause,等待顯示結果:Done.Created num samples
4 訓練過程
使用train.dat呼叫%Opencv%\vs2008\bin\Release\ opencv_traincascade.exe
在traincascade.cpp中查閱引數設定
1基本引數
-data 目錄名,存放訓練好的分類器,如果不存在訓練程式自行建立
-vec 正樣本.vec檔案,由opencv_createsamples生成
-bg 負樣本描述檔案
-numPos 每級分類器訓練時所用到的正樣本數目
-numNeg 每級分類器訓練時所用到的負樣本數目,可以大於-bg指定的圖片數目
-numStages 訓練分類器的級數
-precalcValBufSize 快取大小,用於儲存預先計算的特徵值,單位MB
-precalcIdxBufSize 快取大小,用於儲存預先計算的特徵索引,單位M幣
-baseFormatSave 僅在使用Haar特徵時有效,如果指定,級聯分類器將以老格式儲存
2級聯引數cascadeParams
-stageType 級聯型別,staticconst char* stageTypes[] = { CC_BOOST };
-featureType 特徵型別,staticconst char* featureTypes[] = { CC_HAAR, CC_LBP, CC_HOG };
-w
-h 訓練樣本的尺寸,必須跟使用opencv_createsamples建立的訓練樣本尺寸保持一致
3Boosted分類器引數stageParams
-bt Boosted分類器型別
DAB-discrete Adaboost, RAB-RealAdaboost, LB-LogiBoost, GAB-Gentle Adaboost
-minHitRate 分類器的每一級希望得到的最小檢測率,總的最大檢測率大約為
min_hit_rate^number_of_stages
-maxFalseAlarmRate 分類器的每一級希望得到的最大誤檢率,總的誤檢率大約為
max_false_rate^number_of_stages
-weightTrimRate Specifies whether trimming should beused and its weight. 一個還不錯的數值是0.95
-maxDepth 弱分類器的最大深度,一個不錯數值是1,二叉樹
-maxWeightCount 每一級中弱分類器的最大數目
4Haar特徵引數featureParams
-mode 訓練過程使用的Haar特徵型別,CORE-Allupright ALL-All Features BASIC-Viola
上述引數設定好後呼叫CvCascadeClassifier::train進行訓練
將上述內容在train.dat中編輯好,執行即可。訓練最終生成一個-data指定級聯分類器的資料夾和一個cascade.xml檔案,其餘檔案都是中間結果,當訓練程式被中斷之後,再重新執行訓練程式將讀入之前的訓練結果,無需從頭重新訓練,訓練結束後可以刪除這些檔案。
在cascade.xml檔案中主要有stageType,featureType,width,height,stageParams,featureParams,stageNum,stages和features節點。
stages中的stage數目是自己設定的,每個stage又包含多個weakClassifiers,每個weakClassifier又包含一個internalNodes和一個leafValues。internalNodes中四個變數代表一個node,分別為node中的left/right標記,特徵池中的ID和threshold。leafValues中兩個變數代表一個node,分別為leftleaf和right leaf值。
features是分類器的特徵池,每個Haar特徵包含一個矩形rect和要提取的特徵序號,每個Hog特徵/LBP特徵包含一個矩形。
注意:
1 包含負樣本的影象一定不小於在create中設定的尺寸
負樣本影象可以是不同的尺寸,但是影象尺寸應該比訓練視窗的尺寸大,在使用負樣本影象時,OpenCV自動從負樣本影象中摳出一塊和正樣本同樣大小的區域作為負樣本。具體可查閱icvGetNextFromBackgroundData,具體摳圖過程為:
1) 確定摳圖區域的左上角座標(Point.x, Point.y)
2) 確定一個最小縮放比例,使得原負樣本影象縮放後恰好包含選中負樣本區域
3) 對原負樣本圖象按計算好的縮放比例進行縮放
4) 在縮放後的影象上摳出負樣本。
2 –numPos一般比實際正樣本數量少200-300,-numNeg是否存在同樣的情況?正負樣本選擇規則?
如果出現:訓練停留在一個分類器長達幾小時沒有相應,問題出現在取負樣本的那個函式 icvGetHaarTrainingDataFromBG中;只有當之前的強分類器對負樣本集內的樣本全部分類正確時才會出現死迴圈,因為只要有一個樣本會被錯分為正樣本,那麼通過count次掃描整個負樣本集就能得到count個負樣本,當然這count個負樣本實際上就是一個負樣本的count個拷貝。為避免這種情況,負樣本集中的樣本數需要足夠多 。
不過此時的分類器已經完全額、可以使用,因為它的誤檢率已經很低,從實用性上時沒有任何問題的。所以我們可以通過設定-nstages 這個引數來限制分類器級數,適當時候停止並生成xml檔案。
從CvCascadeBoost::train中去查閱
函式 poscount= icvGetHaarTrainingDataFromVec( training_data, 0, npos,
(CvIntHaarClassifier*)tcc, vecfilename, &consumed )負責從正樣本集*.vec 檔案中載入 count(npos)個正樣本。在程式第一次執行到此(即訓練第一個分類器之前)時,只要正樣本集中有 count 個樣本,就一定能取出 count 個正樣本。在以後執行到此時,有可能取不到 count 個樣本,因為
必須是用前面的級聯強分類器((CvIntHaarClassifier*) tcc)分類為正樣本(即分類正確的樣本)的樣本才會被取出作為下一個強分類器訓練樣本,具體可參考 icvGetHaarTrainingData和icvEvalTreeCascadeClassifierFilter函式。
訓練負樣本,具體可參考icvGetHaarTrainingDataFromBG和icvEvalTreeCascadeClassifierFilter函式。
int icvGetHaarTrainingDataFromBG(CvHaarTrainingData* data, int first, int count,
CvIntHaarClassifier*cascade, double* acceptance_ratio, const char * filename = NULL )
傳遞返回值的 acceptance_ratio 引數記錄的是實際取出的負樣本數與查詢過的負樣本數(如果通過前面級聯stage強分類器的負樣本數很少時,那麼程式會迴圈重複讀取負樣本,並用thread_consumed_count計數)之比(acceptance_ratio = ((double) count) / consumed_count),也就是虛警率,用於判斷已訓練的級聯分類器是否達到指標,若達到指標,則停止訓練過程。
注意函式 icvGetHaarTrainingData中一個主要的 For 迴圈:
for( i = first; i < first +count; i++ ) //共讀取 count 個負樣本,當讀取不到
{ //這麼多負樣本時將出現死迴圈!
對上面程式碼中的註釋有必要進一步說明一下:只有當之前的強分類器對負樣本集內的樣本全部分類正確時才會出現死迴圈。因為只要有一個樣本會被錯分為正樣本,那麼通過 count次掃描整個負樣本集就能得到 count 個負樣本,當然這 count 個負樣本實際上就是一個負樣本的 count 個拷貝。為避免這些情況的發生,負樣本集中的樣本數需要足夠多。
在負樣本影象大小與正樣本大小完全一致時,假設最終的分類器虛警率要求是falsealarm,參加訓練的負樣本要求是 count 個,則需要的負樣本總數可計算如下: TotalCount = count / falsealarm
以 Rainer Lienhart 的文章中的一些引數為例,falsealarm=0.5^20=9.6e-07, count=3000,
則 TotalCount=3000/(0.5^20)= 3,145,728,000=31 億。
函式 icvGetHaarTrainingDataFromBG ()負責從負樣本集中載入 count 個負樣本。在程式第一次執行到此(即訓練第一個分類器之前)時,只要負樣本集中有 count 個樣本,就一定能取出 count 個負樣本。在以後執行到此時,有可能取不到 count 個樣本,因為必須是用前面的級聯強分類器分類為正樣本的樣本(即分類錯誤的樣本)才會被取出作為下一個強分類器的負樣本輸入。
對於int icvGetHaarTrainingData( CvHaarTrainingData* data,int first, int count,
CvIntHaarClassifier*cascade,
CvGetHaarTrainingDataCallbackcallback, void* userdata,
int*consumed, double* acceptance_ratio )
這個函式的解釋:
這是個對於讀取正負樣本通用的函式,區別在於callback的呼叫。在這個函式中有個變數thread_getcount,表示將樣本分為正樣本的數目(不論這個樣本是負樣本還是正樣本)。
傳遞返回值的 Consumed 引數表示為取 count 個正樣本,查詢過的正樣本總數。對於負樣本為空(null),沒有返回值。
3 之前遇到過10*20的不能訓練Hog特徵的分類器?Hog特徵是否存在尺寸限制?
查閱Hog特徵的計算方法,Opencv中HogDescriptor