一、原理简述
HOG: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。HOG特征通过计算和统计图像局部区域的梯度方向直方图来构成特征.
SVM: (Support Vector Machine)指的是支持向量机,是常见的一种判别方法。在机器学习领域,是一个有监督的学习模型,通常用来进行模式识别、分类以及回归分析, 在行人检测中可以用作区分行人和非行人的分类器。
在使用HOG + SVM进行行人检测时, 采集HOG特征的主要思想是通过对一幅图像进行分析, 局部目标的表象和形状可以被剃度或者边缘密度方向分布很好的好的描述. 我们对图像的各个像素点采集土堆或者边缘的方向直方图, 根据直方图的信息就可以描述图片的特征. 好在OpenCv 中已经提供了计算HOG特征的方法, 根据采集到的HOG特征向量, 供SVM分类使用. SVM简单来说就是一个分类器, 在行人检测中就可以转化为行人与非行人的两类分类问题, 在OpenCv中运用到的是基于网格法的SVM.使用采集到的正样本(行人)和负样本(非行人, 可以是汽车, 树木, 路灯等等)的HOG特征, 然后使用SVM分类器进行训练, 得到行人检测模型, 进行行人检测.
二、数据准备
2.1 下载行人数据集:数据集
2.2 数据准备
将下载的数据集解压,里面有几个文件夹,点进去看内容差不多,都有train,test,pos,neg几个不同的文件夹,数据内容都是大同小异的,此时要注意我们的训练集图片的大小必须一致,例如pos的大小都为300*300,neg的大小都为64*128,否则训练过程会报错。我的数据集选择如下:
pos文件夹里都是包含人的图片,neg文件夹的图像是不包含人的任意图片,接下来就是生成图片的描述文件。
进入控制台:
进入你的pos/neg文件夹下,输入以下命令:
dir /b/s/p/w *.jpg > pos.txt,dir /b/s/p/w *.jpg > neg.txt,如果你的图片是png就需要将jpg改成png,然后再对应的文件加下看到以下文件。
三、计算hog特征并训练SVM模型
3.1 装在数据集
void load_images(const String & dirname, vector< Mat > & img_lst)
{ //载入目录下的图片样本
vector< String > files;
glob(dirname, files); //返回一个包含有匹配文件/目录的数组。出错则返回false
for (size_t i = 0; i < files.size(); ++i)
{
Mat img = imread(files[i]); // load the image
if (img.empty()) // invalid image, skip it.
{
cout << files[i] << " is invalid!" << endl;
continue;
}
img_lst.push_back(img);//将Img压入img_lst
}
}
上面的代码就是将训练图片对应的地址传入,返回的是训练图片的向量。
3.2 计算hog特征
void computeHOGs(const Size wsize, const vector< Mat > & img_lst, vector< Mat > & gradient_lst)
{ //计算HOG特征
HOGDescriptor hog;
hog.winSize = wsize;
Rect r = Rect(0, 0, wsize.width, wsize.height);
r.x += (img_lst[0].cols - r.width) / 2; //正样本图片的尺寸减去检测器的尺寸,再除以2
r.y += (img_lst[0].rows - r.height) / 2;
Mat gray;
vector< float > descriptors;
for (size_t i = 0; i< img_lst.size(); i++)
{
cvtColor(img_lst[i](r), gray, COLOR_BGR2GRAY);
hog.compute(gray, descriptors, Size(8, 8), Size(0, 0)); //Size(8,8)为窗口移动步长,
gradient_lst.push_back(Mat(descriptors).clone());
}
}
上面就是常规的hog特征计算,就不一一解释了。
3.3 数据转换
void convert_to_ml(const vector< Mat > & train_samples, Mat& trainData)
{
//--Convert data
const int rows = (int)train_samples.size(); //行数等于训练样本个数
const int cols = (int)std::max(train_samples[0].cols, train_samples[0].rows); //列数取样本图片中宽度与高度中较大的那一个
Mat tmp(1, cols, CV_32FC1); //< used for transposition if needed
trainData = Mat(rows, cols, CV_32FC1);
for (size_t i = 0; i < train_samples.size(); ++i)
{
CV_Assert(train_samples[i].cols == 1 || train_samples[i].rows == 1);
if (train_samples[i].cols == 1)
{
transpose(train_samples[i], tmp);
tmp.copyTo(trainData.row((int)i));
}
else if (train_samples[i].rows == 1)
{
train_samples[i].copyTo(trainData.row((int)i));
}
}
}
将hog特征向量转换为SVM训练所需的数据格式。
3.4 训练自己的分类器
int main()
{
string pos_dir = "D:\\data\\pos";
string neg_dir = "D:\\data\\neg";
string test_dir = "D:\\data\\test";
String obj_det_filename = "D:/data/mydector.xml";
//int detector_width = 64;
//int detector_height = 128;
//vector< Mat > pos_lst, //正样本图片向量
// full_neg_lst, //负样本图片向量
// neg_lst, //采样后的负样本图片向量
// gradient_lst; //HOG描述符存入到该梯度信息里面
//vector< int > labels;
//cout << "load postive image" << endl;
//load_images(pos_dir, pos_lst);
//if (pos_lst.size() > 0)
//{
// cout <<"pos的大小:"<< pos_lst.size()<<"...[done]" << endl;
//}
//else
//{
// cout<< "no image in " << pos_dir << endl;
// return 1;
//}
//Size pos_image_size = pos_lst[0].size();
//pos_image_size = pos_image_size / 8 * 8;
//if (detector_width && detector_height)
//{
// pos_image_size = Size(detector_width, detector_height);
//}
//labels.assign(pos_lst.size(), +1); //assign()为labels分配pos_lst.size()大小的容器,用+1填充 表示为正样本
//const unsigned int old = (unsigned int)labels.size(); //旧标签大小
//cout<< "Negative images are being loaded...";
//load_images(neg_dir, neg_lst); //加载负样本图片
// //sample_neg(full_neg_lst, neg_lst, pos_image_size);
//cout<< "...[done]" << endl;
//labels.insert(labels.end(), neg_lst.size(), -1); //在labels向量的尾部添加neg_lst.size()大小的容器,用-1填充 表示为负样本
//CV_Assert(old < labels.size()); //CV_Assert()作用:CV_Assert()若括号中的表达式值为false,则返回一个错误信息。
//cout<< "Histogram of Gradients are being calculated for positive images...";
//computeHOGs(pos_image_size, pos_lst, gradient_lst); //计算正样本图片的HOG特征
//cout << "...[done]" << endl;
//cout<< "Histogram of Gradients are being calculated for negative images...";
//computeHOGs(pos_image_size, neg_lst, gradient_lst); //计算负样本图片的HOG特征
//cout << "...[done]" << endl;
//Mat train_data;
//convert_to_ml(gradient_lst, train_data); //转化为ml所需的训练数据形式
//cout << "Training SVM...";
//Ptr< SVM > svm = SVM::create();
///* Default values to train SVM */
//svm->setCoef0(0.0);
//svm->setDegree(3);
//svm->setTermCriteria(TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 1000, 1e-3));
//svm->setGamma(0);
//svm->setKernel(SVM::LINEAR); //采用线性核函,其他的sigmoid 和RBF 可自行设置,其值由0-5。
//svm->setNu(0.5);
//svm->setP(0.1); // for EPSILON_SVR, epsilon in loss function?
//svm->setC(0.01); // From paper, soft classifier
//svm->setType(SVM::EPS_SVR); // C_SVC; // EPSILON_SVR; // may be also NU_SVR; // do regression task
//svm->train(train_data, ROW_SAMPLE, Mat(labels));
//cout << "...[done]" << endl;
//vector< float > hog_detector; //定义hog检测器
//get_svm_detector(svm, hog_detector); //得到训练好的检测器
//HOGDescriptor hog;
//hog.winSize = pos_image_size; //窗口大小
//hog.setSVMDetector(hog_detector);
//hog.save(obj_det_filename);
return 0;
}
按照以上步骤就能顺利训练出自己的模型。
由于时间关系,还有些内容没有讲到,有兴趣的小伙伴可以给我留言,我看到会回复的。