opencv學習筆記五十三:訓練自己的級聯分類器
訓練工作主要分為如下幾步:
- 配置訓練環境
- 製作訓練資料集
- 獲取樣本路徑列表
- 生成正樣本描述檔案(.vec)
- 訓練人臉分類器
- 使用分類器進行人臉檢測
配置訓練環境
訓練過程主要依靠OpenCV自帶的兩個可執行程式opencv_createsamples.exe和opencv_haartraining.exe操作完成的。新建一個資料夾命名為cascadeTrain,將opencv3.4.1\opencv\build\x64\vc14\bin中的上面兩個可執行程式及兩個dll檔案拷到該資料夾下,如下圖:
製作訓練資料集
1、準備正樣本:這裡只是熟悉一下怎麼訓練自己感興趣的東西,所以我還是進行簡單的人臉訓練,首先在網上下載了yale大學的人臉資料庫,由耶魯大學計算視覺與控制中心建立,包含15位志願者的165張圖片,包含光照,表情和姿態。下載網址為:
按住ctrl+A全選,重新命名第一張影象為(1),後面所有的影象會自動升序重新命名,如下所示:
圖片中還有白的背景頭髮等,訓練人臉最好只有臉部,所以我先用matlab中的級聯人臉檢測將上述圖片中的人臉檢測出來,並統一尺寸為24*24的(opencv推薦的),然後儲存為一組新的只包含臉部的圖片,將其作為我們的正樣本資料集。在cascadeTrain資料夾下新建posdata資料夾,將正樣本資料集放入其中。
temp = 'C:\Users\Administrator\Desktop\cascadeTrain\'; for i = 1:165 path1 = strcat(temp,'yalefaces\','(',num2str(i),')','.gif'); I = imread(path1); %人臉檢測 faceDetector = vision.CascadeObjectDetector; bboxes = step(faceDetector, I); I = imcrop(I,bboxes);%將檢測到的人臉區域bboxes從原圖中裁剪出來 %尺寸歸一化 I = imresize(I,[24,24]); %將檢測到的人臉圖片另存為 path2 = strcat(temp,'posdata\','(',num2str(i),')','.bmp'); imwrite(I,path2); end
生成的臉部影象如下所示:
2、準備負樣本:這裡我採用的負樣本是用的是weizmann團隊網站上的影象分割資料庫,裡面有灰色圖和彩色圖,這裡當然選取灰度圖了。下載網址為http://www.wisdom.weizmann.ac.il/~vision/Seg_Evaluation_DB/dl.html。用matlab遍歷出文件夾下所有灰度圖作為負樣本資料集。在cascadeTrain資料夾下新建negdata資料夾,將負樣本資料集放入其中。
temp = 'C:\Users\Administrator\Desktop\cascadeTrain\'; for i = 1:200 path1 = strcat(temp,'nonface\','(',num2str(i),')','\src_bw\'); imgdir = dir([path1,'*.png']);%遍歷該資料夾下所有png格式檔案 for j=1:length(imgdir) I = imread([path1,imgdir(j).name]); %另存為bmp格式 path2 = strcat(temp,'negative\','(',num2str(i),')','.bmp'); imwrite(I,path2); end end
這裡用了200幅圖片,負樣本中不能含有人臉且準備的數目必須要比正樣本多,大小大約在300*200畫素,負樣本尺寸不用歸一化,截圖如下所示:
獲取樣本路徑列表
先進入正樣本資料的目錄下(posdata目錄),新建一個文字文件posdir.txt,然後編輯文字如下:
編輯完成後退出儲存,然後把副檔名修改為bat(dos系統批處理的檔案格式),,雙擊該檔案,就會在該目錄下生成一個posdata.txt,如下:
開啟num.txt,使用文字編輯器的替換功能,做一些替換工作
替換1:將絕對路徑替換成相對路徑
替換2:1代表該圖片中有幾張人臉,後四個分別對應人臉起點座標及寬和高
將替換好的posdata.txt複製到posdata目錄的同級目錄下。
同理,負樣本的路徑列表按照上述方法進行,替換時只需要將絕對路徑改為相對路徑即可,將替換好的negdata.txt複製到negdata目錄的同級目錄下。
生成正樣本描述檔案(.vec)
樣本描述檔案就是一個.vec檔案,為opencv訓練準備的,只有正樣本需要,負樣本不需要。 開啟cmd.exe,強制進入到工作目錄下用cd /d
檢視opencv_createsamples.exe引數
- -info 正樣本路徑列表,這裡為-info posdata.txt
- -vec 生成的vec檔名,這裡取-vec posdata.vec
- -bg 負樣本路徑列表,這裡為-bg negdata.txt
- -num 正樣本數量,如實填寫,我這裡為-num 165
- -w -h 正樣本的尺寸,它預設為-w 24 -h 24
其它引數不用填,預設就好,執行以下命令在cascadeTrain資料夾下就會生成一個posdata.vec的檔案
訓練人臉分類器
檢視opencv_traincascade.exe引數
- -data: 先在cascadeTrain資料夾下建立一個haarxml的資料夾,將來生成的各級的xml檔案都將在裡面,-data haarxml
- -vec: 剛才生成的樣本描述檔案,-vec posdata.vec
- -numPos 用於訓練的正樣本數量,這裡填的要比實際正樣本少,因為每一級都可能有誤分類,即將正樣本當成負樣本,則在下一級中該正樣本就不能用了,所以要留點餘量,-numPos 155
- -numNeg 用於訓練的負樣本數量,這裡可以比實際負樣本大,它會自動從你負樣本中不斷地各種裁剪,-numNeg 600
- -numStages 指定訓練層數(決策樹),推薦15~20,層數越多,錯誤率越低,耗時越長,-numStages 15
- featureType 指定訓練什麼特徵,LBP比HAAR快很多,精度稍低,這裡訓練haar特徵,不用填,預設就好
其它引數不用填,預設就好,執行以下命令進行訓練,得到xml檔案
訓練完成後,在haarxml資料夾下會生成各級的xml檔案,如下所示:
使用分類器進行人臉檢測
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main(int arc, char** argv) {
Mat src = imread("5.jpg");
namedWindow("input", CV_WINDOW_AUTOSIZE);
imshow("input", src);
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
CascadeClassifier faceclassifier;
faceclassifier.load("C:/Users/Administrator/Desktop/cascadeTrain/lbpxml/cascade.xml");//載入訓練好的xml檔案
vector<Rect>objects;
faceclassifier.detectMultiScale(gray, objects, 1.1, 1, 0, Size(24, 24));
for (int i = 0; i < objects.size(); i++) {
rectangle(src, objects[i], Scalar(0, 255, 0), 2);
}
imshow("output", src);
waitKey(0);
return 0;
}
可看到最後一張檢測不是很好,因為樣本資料實在是太少了,就一兩百個,實際應用中至少要訓練個四五千個吧,這裡考慮到訓練時間太久,只是做了個簡單的練習。