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 目标检测


分享到:


相關文章: