02.29 opencv hog+svm 目標檢測

通俗來講,目標檢測就是從一副圖片中檢測特定的物體目標,獲得目標的類別信息和位置信息。目標特徵提取可以使用Hog算子,判斷物體的類別則可以使用SVM實現。




hog(方向梯度直方圖)

在一副圖像中,局部目標的表象和形狀能夠被梯度或邊緣的方向密度分佈很好地描述。其本質為梯度的統計信息,而梯度主要存在於邊緣的地方。例如貓和狗因為形狀不同,其hog特徵也就不同,可以分別表示為dogHog和catHog。

OpenCV中使用HOGDescriptor這個類封裝了hog特徵提取函數。

<code>HOGDescriptor hog;
hog.compute(img, dogHog, Size(8, 8));/<code>

svm(支持向量機)

支持向量機是一類按監督學習方式對數據進行二元分類的廣義線性分類器,其決策邊界是對學習樣本求解的最大邊距超平面。利用svm可以判斷一個hog特徵是否屬於一個類。

svm訓練時需要正樣本和負樣本,訓練結果是一個分類器。例如我們收集大量貓的圖片,提取每張圖中貓的hog特徵作為正樣本,再收集大量不帶有貓的圖片,提取他們的hog特徵作為負樣本。訓練將得到一個識別貓的分類器。

<code>  /*創建和初始化svm對象*/
Ptr<:svm> svm = ml::SVM::create();
\tsvm->setType(ml::SVM::C_SVC);
\tsvm->setKernel(ml::SVM::LINEAR);
\tsvm->setTermCriteria(TermCriteria(CV_TERMCRIT_ITER, 1000, FLT_EPSILON));

\t//使用SVM學習
\tsvm->train(sampleFeatureMat, ml::ROW_SAMPLE, sampleLabelVec);/<code>

案例

下面給出一個識別交通標誌的案例

待設別標誌


opencv hog+svm 目標檢測

識別效果

opencv hog+svm 目標檢測


正樣本集鏈接:https://pan.baidu.com/s/1sTCN6drFSWh8ExGWOibtXw 提取碼:ms2p

負樣本集鏈接:https://pan.baidu.com/s/1QvxqIYqc7mhwAd3xbx9uZA 提取碼:jyjj

訓練分類器


/************************

*

*trainHogSvm 訓練分類器

hog:hog對象

posPath:記錄正樣本圖片路徑的txt文本文件


opencv hog+svm 目標檢測


posPath:記錄負樣本圖片路徑的txt文本文件


opencv hog+svm 目標檢測


***********************/


<code>vector<float> trainHogSvm(HOGDescriptor &hog, string posPath, string negPath, string resultPath, int PosNum, int NegNum)
{

\tsize_t hogDescriptorDim; // hogDesDim ,與imgSize、blockSize、cellSize、paddingBlock、bin 有關

\tstring imgName;
\tifstream filePos(posPath);
\tifstream fileNeg(negPath);

\tMat sampleFeatureMat; //所有訓練樣本的特徵組成的矩陣,每個樣本佔一行,列數等於hogDescriptorDim
\t//Mat sampleLabelMat; //樣本標籤,正樣本=1,負樣本=-1
\tvector sampleLabelVec;

\tint inp = 0;
\t//讀取正樣本,生成hog描述符

\tfor (int i = 0; i < PosNum && getline(filePos, imgName); i++)
\t{
\t\t//cout << "處理正樣本:" << imgName << endl;


\t\tMat img1 = imread(imgName, 1);
\t\tMat img;
\t\tif (img1.empty())
\t\t{
\t\t\tcout << "can not load the image:" << imgName << endl;
\t\t\tcontinue;
\t\t}
\t\tinp++;
\t\t
\t\tresize(img1, img, Size(128, 128));//圖像修改為128*128尺寸

\t\tvector<float> descriptors;//hog描述符 向量
\t\thog.compute(img, descriptors, Size(8, 8));//計算hog描述子,檢測窗口移動步長(8,8)
\t\t//cout << "描述子維數:" << descriptors.size() << endl;

\t\t//根據第一個樣本,計算出維數,創建特徵矩陣和標籤矩陣
\t\tif (0 == inp - 1)
\t\t{
\t\t\thogDescriptorDim = descriptors.size();

\t\t\tsampleFeatureMat = Mat::zeros(PosNum + NegNum, hogDescriptorDim, CV_32FC1);
\t\t}
\t\t//將計算得到的描述子複製到樣本特徵矩陣
\t\tfor (int j = 0; j < hogDescriptorDim; j++)
\t\t{
\t\t\tsampleFeatureMat.at<float>(inp - 1, j) = descriptors[j];//第i個樣本的特徵向量中的第j個元素
\t\t}
\t\t//sampleLabelMat.at(0, i) = 1;//正樣本標籤
\t\tsampleLabelVec.push_back(1);


\t}
\tcout << "pos ok!" << endl;

\t//讀取負樣本,生成hog描述符
\tint ind = 0;
\tfor (int i1 = 0; i1 < NegNum && getline(fileNeg, imgName); i1++)
\t{
\t\t//cout << "處理正樣本:" << imgName << endl;


\t\tMat img = imread(imgName, 1);
\t\tresize(img, img, Size(128, 128));
\t\tif (img.empty())
\t\t{
\t\t\tcout << "can not load the image:" << imgName << endl;
\t\t\tcontinue;
\t\t}
\t\tind++;
\t\tvector<float> descriptors;//hog描述符 向量
\t\thog.compute(img, descriptors, Size(8, 8));//計算hog描述子,檢測窗口移動步長(8,8)
\t\t//cout << "負樣本描述子維數:" << descriptors.size() << endl;

\t\t/**
\t\tCV_WRAP virtual void compute(InputArray img,CV_OUT std::vector<float>& descriptors,\tSize winStride = Size(), Size padding = Size(),
\t\tconst std::vector<point>& locations = std::vector<point>()) const;
\t\t@brief Computes HOG descriptors of given image.
\t\t@param img Matrix of the type CV_8U containing an image where HOG features will be calculated.
\t\t@param descriptors Matrix of the type CV_32F
\t\t@param winStride Window stride. It must be a multiple of block stride.
\t\t@param padding Padding

\t\t@param locations Vector of Point
\t\t*/



\t\t//將計算得到的描述子複製到樣本特徵矩陣

\t\tfor (int j = 0; j < hogDescriptorDim; j++)
\t\t{


\t\t\tsampleFeatureMat.at<float>(ind + PosNum - 1, j) = descriptors[j];//第i個樣本的特徵向量中的第j個元素
\t\t}
\t\t//sampleLabelMat.at(0, i) = -1;//負樣本標籤
\t\tsampleLabelVec.push_back(-1);

\t}
\tcout << "neg ok!" << endl;

\t//輸出樣本的HOG特徵向量矩陣到文件
\tofstream fout("data\\\\hogDescriptor.txt");
\tfor (int i = 0; i < PosNum + NegNum; i++)
\t{
\t\tfout << i << endl;
\t\tfor (int j = 0; j < hogDescriptorDim; j++)
\t\t\tfout << sampleFeatureMat.at<float>(i, j) << " ";
\t\tfout << endl;
\t}
\tfout.close();

\t///////////////////////////////////使用SVM分類器訓練///////////////////////////////////////////////////
\t//設置參數,注意Ptr的使用
\tPtr<:svm> svm = ml::SVM::create();
\tsvm->setType(ml::SVM::C_SVC);
\tsvm->setKernel(ml::SVM::LINEAR);//注意必須使用線性SVM進行訓練,因為HogDescriptor檢測函數只支持線性檢測!!!
\tsvm->setTermCriteria(TermCriteria(CV_TERMCRIT_ITER, 1000, FLT_EPSILON));

\t//使用SVM學習
\tsvm->train(sampleFeatureMat, ml::ROW_SAMPLE, sampleLabelVec);

\t/**
\ttrain(InputArray samples, int layout, InputArray responses);
\t@brief Trains the statistical model

\t@param samples training samples
\t@param layout See ml::SampleTypes.
\t@param responses vector of responses associated with the training samples.
\t*/


\t//保存分類器(裡面包括了SVM的參數,支持向量support vector , α和rho)
\tsvm->save(resultPath);

\t/*
\tSVM訓練完成後得到的XML文件裡面,有一個數組,叫做support vector,還有一個數組,叫做alpha,有一個浮點數,叫做rho;
\t將alpha矩陣同support vector相乘,注意,alpha*supportVector,將得到一個行向量,將該向量前面乘以-1。
\t之後,在該行向量的最後添加一個元素rho。如此,便得到了一個分類器,利用該分類器,直接替換opencv中行人檢測默認的
\t那個分類器(cv::HOGDescriptor::setSVMDetector()),
\t*/
\t//獲取支持向量機:矩陣默認是CV_32F浮點型
\tMat supportVector = svm->getSupportVectors();
\t/**
\tgetSupportVectors();
\t@brief Retrieves all the support vectors
\tthe method returns all the support vectors as a floating-point matrix, where support vectors are
\tstored as matrix rows.
\t*/

\t//獲取alpha和rho
\tMat alpha;//每個支持向量對應的參數α(拉格朗日乘子),默認alpha是float64的
\tMat svIndex;//支持向量所在的索引

\tfloat rho = svm->getDecisionFunction(0, alpha, svIndex);
\t/**
\tCV_WRAP virtual double getDecisionFunction(int i, OutputArray alpha, OutputArray svidx) const = 0;
\t@brief Retrieves the decision function決策函數

\t@param i the index of the decision function. If the problem solved is regression, 1-class or
\t2-class classification, then there will be just one decision function and the index should
\talways be 0. Otherwise, in the case of N-class classification, there will be N(N-1)/2 (ovr一對多)
\tdecision functions.
\t@param alpha the optional output vector for weights, corresponding to different support vectors.
\tIn the case of linear %SVM all the alpha's will be 1's.
\t@param svidx the optional output vector of indices of support vectors within the matrix of
\tsupport vectors (which can be retrieved by SVM::getSupportVectors). In the case of linear
\t%SVM each decision function consists of a single "compressed" support vector.

\tThe method returns rho parameter of the decision function, a scalar subtracted from the weighted
\tsum of kernel responses.
\t*/


\t//轉換類型:這裡一定要注意,需要轉換為32的
\tMat alpha2;
\talpha.convertTo(alpha2, CV_32FC1);

\t//結果矩陣,兩個矩陣相乘
\tMat resultMat(1, hogDescriptorDim, CV_32FC1);
\tresultMat = alpha2 * supportVector;

\t//乘以-1,這裡為什麼會乘以-1?
\t//注意因為svm.predict使用的是alpha*sv*another-rho,如果為負的話則認為是正樣本,在HOG的檢測函數中,使用rho+alpha*sv*another(another為-1)
\tfor (int i = 0; i < hogDescriptorDim; ++i)
\t\tresultMat.at<float>(0, i) *= -1;


\t//得到最終的setSVMDetector(const vector<float>& detector)參數中可用的檢測子
\tvector<float> myDetector;
\t//將resultMat中的數據複製到數組myDetector中
\tfor (int i = 0; i < hogDescriptorDim; i++)
\t{
\t\tmyDetector.push_back(resultMat.at<float>(0, i));

\t}
\t//最後添加偏移量rho,得到檢測子
\tmyDetector.push_back(rho);
\tcout << "檢測子維數:" << myDetector.size() << endl;

\t//將分類器保存到文件,便於HOG識別
\t//這個才是真正的判別函數的參數(ω),HOG可以直接使用該參數進行識別
\tofstream fopen1("data\\\\HOG_SVM.txt");
\tfor (int i = 0; i < myDetector.size(); i++)
\t{
\t\tfopen1 << myDetector[i] << endl;
\t}
\tfopen1.close();

\t//設置HOGDescriptor的檢測子
\t/*HOGDescriptor myHOG;
\tmyHOG.setSVMDetector(myDetector);*/
\t//myHOG.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
\treturn myDetector;

}/<float>/<float>/<float>/<float>/<float>
/<float>/<point>/<point>/<float>/<float>
/<float>/<float>
/<float>/<code>

檢測和繪製矩形框

<code>void detectAndDraw(HOGDescriptor &hog, Mat &img)
{
\tvector<rect> found, found_filtered;
\tdouble t = (double)getTickCount();

\thog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2);//多尺度檢測目標,返回的矩形從大到小排列
\tt = (double)getTickCount() - t;
\tcout << "detection time = " << (t*1000. / cv::getTickFrequency()) << " ms" << endl;
\tcout << "detection result = " << found.size() << " Rects" << endl;

\tfor (size_t i = 0; i < found.size(); i++)
\t{
\t\tRect r = found[i];


\t\tsize_t j;
\t\t// Do not add small detections inside a bigger detection. 如果有嵌套的話,則取外面最大的那個矩形框放入found_filtered中
\t\tfor (j = 0; j < found.size(); j++)
\t\t\tif (j != i && (r & found[j]) == r)
\t\t\t\tbreak;

\t\tif (j == found.size())
\t\t\tfound_filtered.push_back(r);
\t}

\tcout << "Real detection result = " << found_filtered.size() << " Rects" << endl;
\tfor (size_t i = 0; i < found_filtered.size(); i++)
\t{
\t\tRect r = found_filtered[i];

\t\t// The HOG detector returns slightly larger rectangles than the real objects,
\t\t// hog檢測結果返回的矩形比實際的要大一些
\t\t// so we slightly shrink the rectangles to get a nicer output.
\t\t// r.x += cvRound(r.width*0.1);
\t\t// r.width = cvRound(r.width*0.8);
\t\t// r.y += cvRound(r.height*0.07);
\t\t// r.height = cvRound(r.height*0.8);
\t\trectangle(img, r.tl(), r.br(), cv::Scalar(0, 255, 0), 3);
\t}

}/<rect>/<code>

主函數

訓練時間挺長

<code>int main()
{
\t//Hog描述符:圖片尺寸128*128,block尺寸16*16,塊步長(8,8),cell尺寸8*8,方向梯度直方圖bin=
\t//40 41 32 28 22 21 19
\t//
\t//
\t///dir /b/s/p/w *.png > pos.txt dir /b/s/p/w *.png > neg.txt
\t//13 19 21 32 40 41
\t//116 312 85 447 283 258
\t//3463 3289 3333 3165 3329 3502
\t//crop_negsample_random();

\t//return 0;
\tHOGDescriptor hog(Size(128, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);
\tvector<float> myDetector;
\tstring posPath = "F:\\\\VSPROJECT\\\\DETECT\\\\jiaotong\\\\jiaotong\\\\正訓練集\\\\21\\\\pos.txt";
\tstring negPath = "F:\\\\VSPROJECT\\\\DETECT\\\\jiaotong\\\\jiaotong\\\\負訓練集21\\\\neg.txt";
\tstring resultPath = "data/HOG_SVM_Classifier41.xml";
\tint PosNum = 116;
\tint NegNum = 14851;
\tmyDetector = trainHogSvm(hog, posPath, negPath, resultPath, PosNum, NegNum);//HOG特徵 + SVM訓練
\thog.setSVMDetector(myDetector);//設置SVM檢測器
\tMat img3 = imread("F:\\\\VSPROJECT\\\\DETECT\\\\TARIN\\\\測試\\\\21.jpg");
\tdetectAndDraw(hog, img3);//檢測
\tnamedWindow("frame3");
\timshow("frame3", img3);
\tdestroyWindow("frame3");
\treturn 0;
}/<float>/<code>

結果


opencv hog+svm 目標檢測


分享到:


相關文章: