以及分析臉部辨識所需要的xml檔案
這一篇文章將會介紹如何產生出 xml 檔案提供給 openCV使用
獲取人臉資料庫
因為要重新訓練出模型, 因此需要有大量的人臉圖案
喵使用了FERET的資料庫
要獲取這人臉資料不用什麼太繁複的手續
但也沒有簡單地直接下載就可以了
按照網站內容的描述
只需要寫封 e-mail過去, 自動審核過就會提供下載點
簡易的方式如下
To: colorferet@nist.gov
標題: Color FERET download request
內容:Please create an account that will allow me to download the Color FERET database. I will abide by the Release Agreement version 1.
寄完信件之後, 就去散個步, 喝個下午茶
很快就會收到回信, 接著由回信當中的下載點將資料庫下載回來
另外, 在訓練模型的時候, 也需要提供 negative的圖檔
這圖檔可以在這邊取得negatives
標題: Color FERET download request
內容:Please create an account that will allow me to download the Color FERET database. I will abide by the Release Agreement version 1.
寄完信件之後, 就去散個步, 喝個下午茶
很快就會收到回信, 接著由回信當中的下載點將資料庫下載回來
另外, 在訓練模型的時候, 也需要提供 negative的圖檔
這圖檔可以在這邊取得negatives
萃取人臉資料
下載完的資料庫, 可以在當中找到資料夾名稱為 ground_truth
FERET/colorferet/dvd1/data/ground_truths
FERET/colorferet/dvd2/data/ground_truths
這ground_truths裡頭就有相關的人臉資料
喵隨意取一個當作範本
FERET/colorferet/dvd1/data/ground_truths/xml/00001/00001_930831_fb_a.xml
範本
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Recordings SYSTEM "http://hbase.humanid.org/hbase/dtd/recording.dtd">
<Recordings>
<Recording id="cfrR00003">
<URL root="Disc1" relative="data/images/00001/00001_930831_fb_a.ppm.bz2"/>
<CaptureDate>08/31/1993</CaptureDate>
<CaptureTime>00:00:00</CaptureTime>
<Format value="ppm" scanning="Progressive" compression="bzip2"/>
<Subject id="cfrS00001">
<Application>
<Face>
<Pose name="fb" yaw="0" pitch="0" roll="0"/>
<Wearing glasses="Yes"/>
<Hair beard="No" mustache="No" source="Retrospectively"/>
<Expression name="fb"/>
<LeftEye x="328" y="324"/>
<RightEye x="204" y="328"/>
<Nose x="270" y="392"/>
<Mouth x="272" y="460"/>
</Face>
</Application>
<Stage id="cfrT00001"/>
</Subject>
<Collection id="cfrC00001"/>
<Environment id="cfrE00001"/>
<Sensor id="cfrN00002"/>
<Illuminant id="cfrI00001"/>
<Illuminant id="cfrI00002"/>
<Illuminant id="cfrI00003"/>
<Weather condition="Inside"/>
</Recording>
</Recordings>
由這xml檔案萃取出左眼, 右眼, 鼻子 以及嘴巴的位子, 當中也有檔案所在位置
注意, 位置大小是以左上角為(0,0)
注意, 位置大小是以左上角為(0,0)
接著就是寫一個python script將這些資料轉化成 openCV 訓練模型所用的檔案
在詳細寫python script之前, 先了解一下openCV已經提供了哪些執行檔可以幫助我們訓練模型, 這樣再回頭寫 script才知道要產生出什麼
OpenCV 模型訓練相關程式
非常好心的openCV不只提供給了face detection的library, 其實也內含了幾個幫助訓練模型的程式, 喵一個個介紹
opencv_annotation
使用在手動勾選出人臉的位置來產生出 annotation file, 這檔案將可以成為 opencv_createsamples所用
相關的參數
--annotations : file path to output annotations
--images: image path, it could be file path or folder path
相關的參數
--annotations : file path to output annotations
--images: image path, it could be file path or folder path
EXx
opencv_annotation --annotations=annotations_manual.txt --images="out/00002_940928_fa.png"
可以先拿一個檔案玩一下, 類似上述的範例, 在圖形介面上可以使用
c : confirm
n : next image
畫面會是這樣
產生出來的 file則是
$cat annotations_manual.txt
out/00002_940928_fa.png 1 122 259 287 333
可以先拿一個檔案玩一下, 類似上述的範例, 在圖形介面上可以使用
c : confirm
n : next image
畫面會是這樣
產生出來的 file則是
$cat annotations_manual.txt
out/00002_940928_fa.png 1 122 259 287 333
排列方式是
file_path number_of_object x y width height
這邊要注意一下的是圖的左上方是為 (x,y) = (0, 0)的點
opencv_createsamples
需有 annotations檔案以及原始的圖檔, 加上 negatives的圖庫之後, 可以產生出 vec檔案, 這檔案將會被 opencv_traincascade 使用
相關參數
-vec : output vector file
-info : annotations file
-w : vector image width
-h : vector image height
-num : positive file's number, 可以看作是從annotations.xml當中所拿取出來使用的檔案數
-bg : negative file
相關參數
-vec : output vector file
-info : annotations file
-w : vector image width
-h : vector image height
-num : positive file's number, 可以看作是從annotations.xml當中所拿取出來使用的檔案數
-bg : negative file
Ex:
opencv_createsamples -vec positive_trainingfaces_24-24.vec -info annotations.txt -w 24 -h 24 -num 2500 -bg negative.txt
對了, negative.txt當中只需要記載image file path, 範例如下
negative.txt
---
-featureType : 預設為 Haar, 可以使用 LBP (可以不用這參數)
Ex:
opencv_traincascade -data cascade_out -vec positive_trainingfaces_24-24.vec -bg negative.txt -numStages 22 -w 24 -h 24 -numPos 2200 -numNeg 1000 -featureType LBP
opencv_createsamples -vec positive_trainingfaces_24-24.vec -info annotations.txt -w 24 -h 24 -num 2500 -bg negative.txt
對了, negative.txt當中只需要記載image file path, 範例如下
negative.txt
---
negatives/neg-4850.jpg
negatives/neg-4851.jpg
negatives/neg-4852.jpg
negatives/neg-4854.jpg
negatives/neg-4855.jpg
negatives/neg-4856.jpg
negatives/neg-4857.jpg
opencv_traincascade
開始訓練模型, 需要輸入 vector file, negative file
相關參數
-data : 輸出 xml file的資料夾
-vec : vector file
-bg : negative file
-numStages : 所要訓練的階層數目
-w : vector image width (需與vector file當時所創立的 width一樣)
-h : vector image height (需與vector file當時所創立的 height一樣)
-numPos : postive file number (必須要小於 vector 所含的數量)-featureType : 預設為 Haar, 可以使用 LBP (可以不用這參數)
Ex:
opencv_traincascade -data cascade_out -vec positive_trainingfaces_24-24.vec -bg negative.txt -numStages 22 -w 24 -h 24 -numPos 2200 -numNeg 1000 -featureType LBP
1. annotations.txt
2. image collection (png format)
接著第二步跟第三步驟都接著上述的範例做就可以了
最後將生成出來的 cascade.xml 拿來取代https://codacat.blogspot.tw/2017/11/opencv-facedetection.html 當中所寫的 xml file
但是呢
事情有時候就是沒這麼順利....
問題與對策
按照這些步驟跟步調, 喵花了點時間寫了一個python可以從FERET下載的資料庫中截取出幾千個annotations file以及 image file.但是不管怎樣都無法正確的辨識, 花了喵幾個星期 (喵都是星期一放著跑training, 隔幾天看看狀況)
所以問題來了
Q1. 為什麼按照步驟做了, 卻連一丁點都無法辨識出人臉? 喵的勒
對策: 回頭好好思考過之後, 發現vector的大小是正方形, 可是我們萃取出來的annotations.txt當中所記載的 width, height不是正方形, 所以... 仔細地思考一下之後驚覺~
annotations.txt當中所記載的 width and height 必須是正方形才能夠提升辨識度
Q2. 按照步驟Q1的對策做了之後, 確實可以提升了辨識度, 但是還是不夠準確, 該怎樣提升?
對策: 辨識度要提升到很高, 有幾個方向可以努力
- 增加 postive 以及 negative 量
- 使用當地的資料庫, 避免膚色, 輪廓等的差異 (就像是我們使用外國人的資料庫訓練出來模型之後, 拿他來辨識在地人, 辨識度一定不夠好)
- 將 vector size提高
- 改變 stage numbers
Q3. 每次訓練起來都很久, 有什麼辦法可以加快速度
對策: 當然最快的方式就是升級硬體, 但是喵還是推薦免錢的方法, 那就是先使用 LBP的方式訓練一次之後, 如果方向對了, 在把 featureType這選項去掉, 放著給他跑
Q4. 我怎麼知道annotations.txt當中所載明的區域就是人臉所在? 尤其使用程式萃取往往會有出乎意外的結果...
對策: 自己寫一個讀取 annotations.txt, 然後標示出方框的位置, 喵寫的就在這邊給大家參考
#include <opencv2/opencv.hpp> #include <stdio.h> #include <iostream> #include <string> #include <fstream> using namespace std; using namespace cv; String window_name = "Face Window"; /** * Detects faces and draws an ellipse around them */ int main(int argc, char** argv) { string annotationFile; string directory; if (argc < 3) { // Check the value of argc. If not enough parameters have been passed, inform user and exit. std::cout << "Usage is -f <annotation file>\n"; // Inform the user of how to use the program std::cin.get(); exit(0); } else { // if we got enough parameters... char *myPath; char *myOutPath; std::cout << argv[0]; for (int i = 1; i < argc; i++) { if (i + 1 != argc) { if (strcmp(argv[i] , "-f") == 0) { // We know the next argument *should* be the filename: annotationFile = string(argv[i + 1]); const size_t last_slash_idx = annotationFile.rfind('/'); if (std::string::npos != last_slash_idx) { directory = annotationFile.substr(0, last_slash_idx+1); } } else { std::cout << "Not enough or invalid arguments, please try again.\n"; exit(0); } } } std::cout << annotationFile ; printf("annotatilFile is %s\n", annotationFile.c_str()); printf("directory is %s\n", directory.c_str()); } printf("Jay test 1\n"); // open file std::ifstream infile(annotationFile.c_str()); std::string line; #if 1 while (std::getline(infile, line)) { std::istringstream iss(line); string imgFilePath; int objectCount = 0; int x = 0; int y = 0; int width = 0; int height = 0; if (!(iss >> imgFilePath >> objectCount >> x >> y >> width >> height)) { break; }; //error string absolutePath = directory + (imgFilePath); printf("%s, x %d, y %d, width %d, height %d\n", absolutePath.c_str(), x, y, width, height); // printf("%s, x %d, y %d, width %d, height %d\n", absolutePath.c_str(), x, y, width, height); printf("%s\n", line.c_str()); { Mat inputImage = imread(absolutePath); Point origin(x, y); Point diagonal(x+width, y+height); rectangle(inputImage, origin, diagonal, Scalar( 255, 0, 255 )); imshow(window_name, inputImage); // Display frame int k = waitKey(0); if(k == 27){ //ESC is 27 printf("%s, x %d, y %d, width %d, height %d\n", absolutePath.c_str(), x, y, width, height); break; } } } #endif return 0; }
./Debug/P06-ValidateImg -f /home/jay/face_detection/annotations.txt
其中會去 parsing annotations 所記載相對位置的 image, 所以這邊也要請各位注意~
這篇的文章有點長, 但也是因為幾個星期積累下來的東西比較多
大家看完應該感覺對 face dectection更了解一些了吧~
喵~~
沒有留言:
張貼留言