opencv下svm训练图片分类器学习记录

  最近在尝试运用opencv的svm训练器来实现图片分类,选用的是hog特征和sift特征两种,过程大致相同的,都是要把每一个样品提取出来的特征化成同样维度的一维向量,因为opencv的svm是以一个行向量作为一个训练数据。

     sift特征每一幅图提取的特征向量的个数是不同的,在网上搜索后选择使用pca降维的方式来归一化特征向量的维度。参考文章

     hog特征在统一图像尺寸以及hog特征提取的检测窗口大小,得出的特征向量的维度是一定的。

使用svm检测时要按照训练的方式来对待检测样品进行处理,即将图像提取成与训练模型同样的维度的特征向量。

例如hog特征向量是1*588维,sift特征经过pca方式降维后是1*128维的。

 

#include 
#include   //头文件
#include 
#include   
#include   
#include   
#include   
#include   
#include   
#include  //查找文件相关函数

using namespace cv::ml;
using namespace cv;  //包含cv命名空间
using namespace std;

void getFiles(string path, vector& files);
void getBubble(Mat& trainingImages, vector& trainingLabels);
void getNoBubble(Mat& trainingImages, vector& trainingLabels);

Mat detector_HOG(Mat image) {
	HOGDescriptor hog(Size(128, 128), Size(16, 16), Size(8, 8), Size(8, 8), 3);

	Mat featureMat;
        vector descriptors;
	hog.compute(image, descriptors);
	featureMat = Mat::zeros(1, descriptors.size(), CV_32FC1);

	for (int j = 0; j(0, j) = descriptors[j];
	}
	return featureMat;
}

int main() {
	
	Mat classes;
	Mat trainingData;
	Mat trainingImages;

	vector trainingLabels;
	getPositive(trainingImages, trainingLabels);
        getNegative(trainingImages, trainingLabels);

	Mat(trainingImages).copyTo(trainingData);
	trainingData.convertTo(trainingData, CV_32FC1);
	Mat(trainingLabels).copyTo(classes);

	// 创建分类器并设置参数
	Ptr SVM_params = SVM::create();
	SVM_params->setType(SVM::C_SVC);
	SVM_params->setKernel(SVM::LINEAR);  //核函数

	SVM_params->setDegree(0);
	SVM_params->setGamma(1);
	SVM_params->setCoef0(0);
	SVM_params->setC(1);
	SVM_params->setNu(0);
	SVM_params->setP(0);
	SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01));
	cout << "start" << endl;

	Ptr tData = TrainData::create(trainingData, ROW_SAMPLE, classes);

	// 训练分类器
	clock_t start, end;
	start = clock();
	SVM_params->train(tData);
	end = clock();

	cout << (float)(end - start) / CLOCKS_PER_SEC << "s" << endl;
	//保存模型
	SVM_params->save("F:\\testdata\\svm_hog_726_2.xml"); //svm模型存放路径
	cout << "训练好了!!!" << endl;
	getchar();
	return 0;

}


void getFiles(string path, vector& files)
{
	//文件句柄  
	long long hFile = 0;
	//文件信息  
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
		do
		{
			//如果是目录,迭代之  
			//如果不是,加入列表  
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
					getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
			}
			else
			{
				files.push_back(p.assign(path).append("\\").append(fileinfo.name));
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
}




//获取正样本
void getPositive(Mat& trainingImages, vector& trainingLabels)
{

	string filePath = "F:\\picture\\positive"; //正样本路径
	vector files;
	getFiles(filePath, files);
	
	int number = files.size();
	for (int i = 0; i < number; i++)
	{
		Mat  image = imread(files[i], 0);//读入灰度图
		equalizeHist(image, SrcImage);//直方图均衡
		resize(image, image, Size(128, 128));

		Mat  hogFeactureMat = detector_HOG(image);

		if (!hogFeactureMat.empty()) {
			trainingImages.push_back(hogFeactureMat);
			trainingLabels.push_back(1);
		}

		cout << "remain   " << (number - i) << "    count:  " << i << endl;
	}

	cout << "positive  ok" << endl;
}

//获取负样本
void getNegative(Mat& trainingImages, vector& trainingLabels)
{

	string filePath = "F:\\picture\\negative"; //负样本路径
	vector files;
	getFiles(filePath, files);
	int number = files.size();
	for (int i = 0; i < number; i++)
	{
		Mat  image = imread(files[i], 0);
		equalizeHist(image, image);
		resize(image, image, Size(128, 128));
		

		Mat  hogFeactureMat = detector_HOG(SrcImage);
		if (!hogFeactureMat.empty()) {
			trainingImages.push_back(hogFeactureMat);
			trainingLabels.push_back(0);
		}
		cout << "remain   " << (number - i) << "    count:  " << i << endl;
	}

	cout << "negative  ok" << endl;

}

 

sift特征分类仅在特征处理上有不同

 

Mat detector_SIFI(Mat image) {
	//Create SIFT class pointer
	Ptr f2d = xfeatures2d::SIFT::create(1000,3,0.04,10);//sift的参数,会影响一幅图提取出的特征点的个数	
	
	vector keypoints_1, keypoints_2;
	f2d->detect(image, keypoints_1);
	
	Mat descriptors_1;
	f2d->compute(image, keypoints_1, descriptors_1);

	if (keypoints_1.size() == 0) {
		return Mat();
	}
	PCA pca(descriptors_1, noArray(), CV_PCA_DATA_AS_ROW);
	Mat eigenvalues;//特征值
	Mat eigenvectors;//特征向量

	DoPca(descriptors_1, 3, eigenvalues, eigenvectors);
	eigenvectors = eigenvectors.reshape(0, 1);
	//cout << eigenvalues.rows << "     " << eigenvectors.cols << endl;
	return eigenvectors;	
}
//这个函数在前文的资料有提及,降维特征向量
void DoPca(const Mat &_data, int dim, Mat &eigenvalues, Mat &eigenvectors)
{
	assert(dim>0);
	Mat data = cv::Mat_(_data);
	int R = data.rows;
	int C = data.cols;
	if (dim>C)
		dim = C;

	//计算均值
	Mat m = Mat::zeros(1, C, data.type());

	for (int j = 0; j(0, j) += data.at(i, j);
		}
	}
	m = m / R;
	//求取6列数据对应的均值存放在m矩阵中,均值: [1.67、2.01、1.67、2.01、1.67、2.01]
	//计算协方差矩阵
	Mat S = Mat::zeros(R, C, data.type());
	for (int i = 0; i(i, j) = data.at(i, j) - m.at(0, j); // 数据矩阵的值减去对应列的均值
		}
	}
	Mat Average = S.t() * S / (R);
	//计算协方差矩阵的方式----(S矩阵的转置 * S矩阵)/行数
	//使用opencv提供的eigen函数求特征值以及特征向量
	eigen(Average, eigenvalues, eigenvectors);

}

sift特征点构造方法可以参考opencv 中sift特征提取的参数介绍

训练完成后就是测试结果

void svm_pridect_HOG(Ptr &model, Mat test)
{
    Mat tmp;
    float validity = 0;
    float rst;
    tmp = detector_HOG(test);

    tmp.convertTo(tmp, CV_32F);

    rst = model->predict(tmp);

    cout << rst << "    ";

}

读入svm训练模型

Ptrsvm = StatModel::load("...your xml file path");

 

opencv下svm训练数据要存入一个矩阵中,一行为一个样品,对于每一种特征都应该尽可能将其转换成一个行向量,因为一副图像中特征点之间应该是有关联的,不能单纯将一个特征点作为一个训练样品。

同时这个矩阵不能过大,个人尝试过大概14000*50000左右的矩阵是会报错,没有了解到是否有扩大数据容量的方法。

参考资料

opencv下svm参数介绍

opencv3与2.x svm参数介绍

opencv下实现简单的图像分类器

sift特征点与特征向量的转换

你可能感兴趣的:(opencv下svm训练图片分类器学习记录)