上一篇介紹了OPENCV中SVM的簡單使用,以及自帶的一個二分類問題。
例子中的標簽是程序手動寫的,輸入也是手動加的二維坐標點。
對於復雜問題就必須使用數據集中的圖片進行訓練,標簽使用TXT文件或程序設置好,下面以 IMM Face Database 中的人臉數據作為示例,
實現人臉的HOG特征提取及SVM識別人臉。
數據集參考我的http://www.cnblogs.com/chenzhefan/p/7624811.html;只選取其中5類人,每類5副圖片作為訓練。
提取人臉HOG特征的維數為1764,具體見代碼設置。
1 void HogSVM() 2 { 3 int ImgWidht = 64; 4 int ImgHeight = 64; 5 vector<string> img_path; 6 vector<int> img_catg; 7 int nLine = 0; 8 string buf; 9 ifstream svm_data("E:\\vswork\\car3\\train\\train.txt"); 10 unsigned long n; 11 12 for (int catg = 0; catg < 5; catg++) 13 { 14 for (int num = 0; num < 5; num++) 15 { 16 if (getline(svm_data, buf)) 17 { 18 img_catg.push_back(catg);//圖像類別 19 img_path.push_back(buf);//圖像路徑 20 nLine++; 21 } 22 } 23 24 } 25 26 svm_data.close();//關閉文件 27 28 Mat data_mat, res_mat; 29 int nImgNum = nLine; //讀入樣本數量 30 //樣本矩陣,nImgNum:行數代表樣本的數量,每一行就是由一張圖片計算得到HOG的特征向量, 31 data_mat = Mat::zeros(nImgNum, 1764, CV_32FC1); 32 res_mat = Mat::zeros(nImgNum, 1, CV_32FC1); 33 34 Mat src; 35 Mat trainImg = Mat::zeros(ImgHeight, ImgWidht, CV_8UC3);//需要分析的圖片 36 37 for (string::size_type i = 0; i != img_path.size(); i++) 38 { 39 src = imread(img_path[i].c_str(), 1); 40 41 cout << " processing " << img_path[i].c_str() << endl; 42 43 resize(src, trainImg, cv::Size(ImgWidht, ImgHeight), 0, 0, INTER_CUBIC); 44 HOGDescriptor *hog = new HOGDescriptor(cvSize(ImgWidht, ImgHeight), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9); //構造HOG,具體意思見參考文章1,2 45 vector<float>descriptors;//結果數組 46 hog->compute(trainImg, descriptors, Size(1, 1), Size(0, 0)); //調用計算函數開始計算 47 if (i == 0) 48 { 49 data_mat = Mat::zeros(nImgNum, descriptors.size(), CV_32FC1); //根據輸入圖片大小進行分配空間 50 } 51 cout << "HOG dims: " << descriptors.size() << endl; 52 n = 0; 53 for (vector<float>::iterator iter = descriptors.begin(); iter != descriptors.end(); iter++) 54 { 55 data_mat.at<float>(i, n) = *iter; 56 n++; 57 } 58 res_mat.at<float>(i, 0) = img_catg[i]; 59 cout << " end processing " << img_path[i].c_str() << " " << img_catg[i] << endl; 60 } 61 62 CvSVM svm; 63 CvSVMParams param; 64 CvTermCriteria criteria; 65 criteria = cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON); 66 param = CvSVMParams(CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria); 67 68 svm.train(data_mat, res_mat, Mat(), Mat(), param); 69 svm.save("SVM_DATA.xml"); 70 71 return; 72 }
上述函數主要完成提取訓練圖片的HOG特征,並用SVM訓練模型,保存為XML文件方便快速使用。
訓練結果如圖:
訓練完成后即可以使用測試圖片進行圖片識別了:
1 void HogSVMPre() 2 { 3 //檢測樣本 4 vector<string> img_tst_path; 5 string buf; 6 unsigned long n; 7 int ImgWidht = 64; 8 int ImgHeight = 64; 9 Mat TestImg = Mat::zeros(ImgHeight, ImgWidht, CV_8UC3); 10 ifstream img_tst("E:\\vswork\\car3\\val\\val.txt"); 11 while (img_tst) 12 { 13 if (getline(img_tst, buf)) 14 { 15 img_tst_path.push_back(buf); 16 } 17 } 18 img_tst.close(); 19 CvSVM svm; 20 svm.load("SVM_DATA.xml"); 21 Mat test; 22 char line[512]; 23 ofstream predict_txt("SVM_PREDICT.txt"); 24 for (string::size_type j = 0; j != img_tst_path.size(); j++) 25 { 26 test = imread(img_tst_path[j].c_str(), 1);//讀入圖像 27 resize(test, TestImg, cv::Size(ImgWidht, ImgHeight), 0, 0, INTER_CUBIC);//要搞成同樣的大小才可以檢測到 28 HOGDescriptor *hog = new HOGDescriptor(cvSize(ImgWidht, ImgHeight), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9); //窗口大小,塊大小,塊滑動增量,cell的大小,bins的個數 29 vector<float>descriptors;//結果數組 30 hog->compute(TestImg, descriptors, Size(1, 1), Size(0, 0)); //調用計算函數開始計算 31 cout << "The Detection Result:" << endl; 32 cout << "HOG dims: " << descriptors.size() << endl; 33 Mat SVMtrainMat = Mat::zeros(1, descriptors.size(), CV_32FC1); 34 n = 0; 35 for (vector<float>::iterator iter = descriptors.begin(); iter != descriptors.end(); iter++) 36 { 37 SVMtrainMat.at<float>(0, n) = *iter; 38 n++; 39 } 40 41 int ret = svm.predict(SVMtrainMat); 42 std::sprintf(line, "%s %d\r\n", img_tst_path[j].c_str(), ret); 43 printf("%s %d\r\n", img_tst_path[j].c_str(), ret);//輸出預測的結果,ret的值就代表類別 44 //getchar(); 45 predict_txt << line; 46 } 47 predict_txt.close(); 48 system("PAUSE"); 49 return; 50 }
預測結果:
小樣本圖片SVM的識別結果還是很不錯的。本文的測試圖片較少,也不能說明模型到底有多好,但基於opencv SVM的識別分類流程基本是這樣了。
有問題歡迎討論~