OpenCV+SVM+HOG進行影象二分類
阿新 • • 發佈:2019-01-01
功能需求
其實就是一個比較簡單的影象二分類。
開發環境
軟體環境:Win 10 64位作業系統,Visual Studio 2015,OpenCV3.1.0。
總體思路
本系統的總體思路分為三步:
第一步進行資料採集,系統中用到的資料集可以是一張圖片或者整個資料集,這些資料是通過在現場採集的。
第二步檢測提取影象的特徵,該部分採用的底層特徵是HOG特徵,提取目標圖片,將圖片資訊轉換成所需要的圖片效果,並對底層特徵利用區域性座標編碼演算法進行編碼,同時通過特徵金字塔對這些編碼進行匹配。
第三步投入SVM分類器進行訓練,得到model,結合最終收集到的HOG特徵向量進行分類。
實現方法
本系統主要針對複雜場景下對影象物體進行分類,系統用VS2015,OpenCV來進行實驗,首先在特徵提取部分採用的是HOG特徵描述子,計算正負樣本影象的HOG描述子,組成一個特徵向量矩陣,對應的要有一個指定每個特徵向量的類別的類標向量,然後訓練SVM分類器,利用訓練樣本訓練出來的分類器進行影象分類。主要是闡述該系統如何實現從特徵提取到分類的演算法,以及所涉及到的技術的理論知識的敘述。
實驗過程
準備工作
1、建立正陽本資料夾“pos”將正樣本(無噴濺圖片)放入;
2、建立負樣本資料夾“neg”將負樣本(有噴濺圖片)放入;
3、製作正陽本檔案列表postive_samples.txt;
4、製作負樣本檔案列表negative_samplest.txt;
5、製作測試樣本檔案列表test_samples.txt;
6、執行程式進行訓練,執行。
樣本檔案列表如下:
#include <stdio.h> #include <iostream> #include <fstream> #include <opencv2/opencv.hpp> #include <string> using namespace cv::ml; #define PosSamNO 30 //正樣本個數 #define NegSamNO 30 //負樣本個數 #define TestSamNO 5 //測試個數 void train_svm_hog() { //HOG檢測器,用來計算HOG描述子的 //檢測視窗(48,48),塊尺寸(16,16),塊步長(8,8),cell尺寸(8,8),直方圖bin個數9 cv::HOGDescriptor hog(cv::Size(48, 48), cv::Size(16, 16), cv::Size(8, 8), cv::Size(8, 8), 9); int DescriptorDim;//HOG描述子的維數,由圖片大小、檢測視窗大小、塊大小、細胞單元中直方圖bin個數決定 //設定SVM引數 cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::create(); svm->setType(cv::ml::SVM::Types::C_SVC); svm->setKernel(cv::ml::SVM::KernelTypes::LINEAR); svm->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER, 100, 1e-6)); std::string ImgName; //正樣本圖片的檔案列表 std::ifstream finPos("positive_samples.txt"); //負樣本圖片的檔案列表 std::ifstream finNeg("negative_samples.txt"); //所有訓練樣本的特徵向量組成的矩陣,行數等於所有樣本的個數,列數等於HOG描述子維數 cv::Mat sampleFeatureMat; //訓練樣本的類別向量,行數等於所有樣本的個數,列數等於1;1表示有目標,-1表示無目標 cv::Mat sampleLabelMat; //依次讀取正樣本圖片,生成HOG描述子 for (int num = 0; num < PosSamNO && getline(finPos, ImgName); num++) { std::cout << "Processing:" << ImgName << std::endl; cv::Mat image = cv::imread(ImgName); cv::resize(image, image, cv::Size(48, 48)); //HOG描述子向量 std::vector<float> descriptors; //計算HOG描述子,檢測視窗移動步長(8,8) hog.compute(image, descriptors, cv::Size(8, 8)); //處理第一個樣本時初始化特徵向量矩陣和類別矩陣,因為只有知道了特徵向量的維數才能初始化特徵向量矩陣 if (0 == num) { //HOG描述子的維數 DescriptorDim = descriptors.size(); //初始化所有訓練樣本的特徵向量組成的矩陣,行數等於所有樣本的個數,列數等於HOG描述子維數sampleFeatureMat sampleFeatureMat = cv::Mat::zeros(PosSamNO + NegSamNO, DescriptorDim, CV_32FC1); //初始化訓練樣本的類別向量,行數等於所有樣本的個數,列數等於1 sampleLabelMat = cv::Mat::zeros(PosSamNO + NegSamNO, 1, CV_32SC1); } //將計算好的HOG描述子複製到樣本特徵矩陣sampleFeatureMat for (int i = 0; i < DescriptorDim; i++) { //第num個樣本的特徵向量中的第i個元素 sampleFeatureMat.at<float>(num, i) = descriptors[i]; } //正樣本類別為1,判別為無噴濺 sampleLabelMat.at<float>(num, 0) = 1; } //依次讀取負樣本圖片,生成HOG描述子 for (int num = 0; num < NegSamNO && getline(finNeg, ImgName); num++) { std::cout << "Processing:" << ImgName << std::endl; cv::Mat src = cv::imread(ImgName); cv::resize(src, src, cv::Size(48, 48)); //HOG描述子向量 std::vector<float> descriptors; //計算HOG描述子,檢測視窗移動步長(8,8) hog.compute(src, descriptors, cv::Size(8, 8)); //處理第一個樣本時初始化特徵向量矩陣和類別矩陣,因為只有知道了特徵向量的維數才能初始化特徵向量矩陣 //std::cout << "descriptor dimention:" << descriptors.size() << std::endl; //將計算好的HOG描述子複製到樣本特徵矩陣sampleFeatureMat for (int i = 0; i < DescriptorDim; i++) { //第PosSamNO+num個樣本的特徵向量中的第i個元素 sampleFeatureMat.at<float>(num + PosSamNO, i) = descriptors[i]; } //負樣本類別為-1,判別為噴濺 sampleLabelMat.at<float>(num + PosSamNO, 0) = -1; } //訓練SVM分類器 std::cout << "開始訓練SVM分類器" << std::endl; cv::Ptr<cv::ml::TrainData> td = cv::ml::TrainData::create(sampleFeatureMat, cv::ml::SampleTypes::ROW_SAMPLE, sampleLabelMat); svm->train(td); std::cout << "SVM分類器訓練完成" << std::endl; //將訓練好的SVM模型儲存為xml檔案 svm->save("SVM_HOG.xml"); return; } void svm_hog_classification() { //HOG檢測器,用來計算HOG描述子的 //檢測視窗(48,48),塊尺寸(16,16),塊步長(8,8),cell尺寸(8,8),直方圖bin個數9 cv::HOGDescriptor hog(cv::Size(48, 48), cv::Size(16, 16), cv::Size(8, 8), cv::Size(8, 8), 9); //HOG描述子的維數,由圖片大小、檢測視窗大小、塊大小、細胞單元中直方圖bin個數決定 int DescriptorDim; //測試樣本圖片的檔案列表 std::ifstream finTest("test_samples.txt"); std::string ImgName; for (int num = 0; num < TestSamNO && getline(finTest, ImgName); num++) { //從XML檔案讀取訓練好的SVM模型 cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::load<cv::ml::SVM>("SVM_HOG.xml "); if (svm->empty()) { std::cout << "load svm detector failed!!!" << std::endl; return; } //針對測試集進行識別 std::cout << "開始識別..." << std::endl; std::cout << "Processing:" << ImgName << std::endl; cv::Mat test = cv::imread(ImgName); cv::resize(test, test, cv::Size(48, 48)); std::vector<float> descriptors; hog.compute(test, descriptors); cv::Mat testDescriptor = cv::Mat::zeros(1, descriptors.size(), CV_32FC1); for (size_t i = 0; i < descriptors.size(); i++) { testDescriptor.at<float>(0, i) = descriptors[i]; } float label = svm->predict(testDescriptor); imshow("test image", test); std::cout << "這張圖屬於:" << label << std::endl; cv::waitKey(0); } return; } int main(int argc, char** argv) { train_svm_hog(); svm_hog_classification(); return 0; }
執行結果如圖: