1. 程式人生 > >OpenCV+SVM+HOG進行影象二分類

OpenCV+SVM+HOG進行影象二分類

功能需求

其實就是一個比較簡單的影象二分類。

開發環境

軟體環境: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;
}

執行結果如圖: