1. 程式人生 > >opencv——基於SVM的數字識別(2)

opencv——基於SVM的數字識別(2)

上篇文章我們用的特徵是訓練樣本的所有畫素點值,雖然方便但不準確。

這篇文章主要介紹用SVM+HOG特徵對數字進行識別。

詳細請看上篇文章,它們主要區別在於訓練樣本HOG特徵的提取,其他基本一樣,所以我直接附上程式碼。

下面程式碼是opencv3和C++

可以根據自己需要修改訓練樣本類別,數目,尺寸。oss的訓練樣本路徑,src的檢測圖片路徑。


#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <io.h> //查詢檔案相關函式


using namespace std;
using namespace cv;
using namespace ml;
ostringstream oss;//結合字串和數字
int num = -1;
Mat dealimage;
Mat src;
Mat yangben_gray;
Mat yangben_thresh;
void feature(Mat dealimage);

////===============================讀取訓練資料===============================////
const int classsum = 10;//訓練圖片共有10類,可修改
const int imagesSum = 500;//每類有500張圖片,可修改	
int yangben_data_position = -1;
Mat data_mat = Mat(classsum*imagesSum, 8100, CV_32FC1);
//data_mat用來儲存所有訓練樣本的HOG特徵,每一列為一副影象的HOG特徵
//每一行為每一幅訓練影象
//必須為CV_32FC1的型別
//行、列、型別;第二個引數,即矩陣的列是由下面的descriptors的大小決定的,
//可以由descriptors.size()得到,且對於不同大小的輸入訓練圖片,這個值是不同的 
//descriptors為提取到的HOG特徵
int main()
{


	//訓練資料,每一行一個訓練圖片
	Mat trainingData;
	//訓練樣本標籤
	Mat labels;
	//最終的訓練樣本標籤
	Mat clas;
	//最終的訓練資料
	Mat traindata;

	//////////////////////從指定資料夾下提取圖片//////////////////
	for (int p = 0; p < classsum; p++)//依次提取0到9資料夾中的圖片
	{
		oss << "C:/Users/zhang/Desktop/opencv——例項/小案例/車牌檢測/基於機器學習/模版匹配樣本/";
		num += 1;//num從0到9
		int label = num;
		oss << num << "/*.jpg";//圖片名字字尾,oss可以結合數字與字串
		string pattern = oss.str();//oss.str()輸出oss字串,並且賦給pattern
		oss.str("");//每次迴圈後把oss字串清空
		vector<Mat> input_images;
		vector<String> input_images_name;
		glob(pattern, input_images_name, false);
		//為false時,僅僅遍歷指定資料夾內符合模式的檔案,當為true時,會同時遍歷指定資料夾的子資料夾
		//此時input_images_name存放符合條件的圖片地址
		int all_num = input_images_name.size();
		//檔案下總共有幾個圖片
		//cout << num << ":總共有" << all_num << "個圖片待測試" << endl;

		for (int i = 0; i < imagesSum; i++)//依次迴圈遍歷每個資料夾中的圖片
		{
			cvtColor(imread(input_images_name[i]), yangben_gray, COLOR_BGR2GRAY);//灰度變換
			threshold(yangben_gray, yangben_thresh, 0, 255, THRESH_OTSU);//二值化
																		 //迴圈讀取每張圖片並且依次放在vector<Mat> input_images內
			input_images.push_back(yangben_thresh);
			dealimage = input_images[i];

			//選擇了HOG的方式完成特徵提取工作
			yangben_data_position += 1;//代表為第幾幅影象
			feature(dealimage);//圖片特徵提取
			labels.push_back(label);//把每個圖片對應的標籤依次存入
			cout << "第" << yangben_data_position << "樣本正在提取HOG特徵" << endl;
		}
	}


	cout << "樣本特徵提取完畢,等待建立SVM模型" << endl;
	////===============================建立SVM模型===============================////
	// 建立分類器並設定引數
	Ptr<SVM> SVM_params = SVM::create();
	SVM_params->setType(SVM::C_SVC);//C_SVC用於分類,C_SVR用於迴歸
	SVM_params->setKernel(SVM::LINEAR);  //LINEAR線性核函式。SIGMOID為高斯核函式

	SVM_params->setDegree(0);//核函式中的引數degree,針對多項式核函式;
	SVM_params->setGamma(1);//核函式中的引數gamma,針對多項式/RBF/SIGMOID核函式; 
	SVM_params->setCoef0(0);//核函式中的引數,針對多項式/SIGMOID核函式;
	SVM_params->setC(1);//SVM最優問題引數,設定C-SVC,EPS_SVR和NU_SVR的引數;
	SVM_params->setNu(0);//SVM最優問題引數,設定NU_SVC, ONE_CLASS 和NU_SVR的引數; 
	SVM_params->setP(0);//SVM最優問題引數,設定EPS_SVR 中損失函式p的值. 
						//結束條件,即訓練1000次或者誤差小於0.01結束
	SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01));

	//訓練資料和標籤的結合
	Ptr<TrainData> tData = TrainData::create(data_mat, ROW_SAMPLE, labels);

	// 訓練分類器
	SVM_params->train(tData);//訓練

	//儲存模型
	SVM_params->save("C:/Users/zhang/Desktop/opencv——例項/小案例/車牌檢測/基於機器學習/字元識別svm.xml");
	cout << "訓練好了!!!" << endl;


	cout << "等待識別" << endl;
	////===============================預測部分===============================////
	Mat src = imread("C:/Users/zhang/Desktop/opencv——例項/小案例/車牌檢測/基於機器學習/檢測到的車牌字元/字元7.jpg");
	cvtColor(src, src, COLOR_BGR2GRAY);
	threshold(src, src, 0, 255, CV_THRESH_OTSU);
	imshow("原影象", src);

	//輸入影象取特徵點
	Mat trainTempImg = Mat::zeros(Size(128, 128), CV_8UC1);
	resize(src, trainTempImg, trainTempImg.size());

	HOGDescriptor *hog = new HOGDescriptor(Size(128, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);
	vector<float>descriptors;//結果陣列         
	hog->compute(trainTempImg, descriptors, Size(1, 1), Size(0, 0));
	//cout << "HOG描述子向量維數    " << descriptors.size() << endl;
	Mat SVMtrainMat = Mat(1, descriptors.size(), CV_32FC1);

	int  number1 = descriptors.size();
	//將計算好的HOG描述子複製到樣本特徵矩陣SVMtrainMat  
	for (int i = 0; i < number1; i++)
	{
		//把一幅影象的HOG描述子向量依次存入data_mat矩陣的同一列
		//因為輸入影象只有一個,即SVMtrainMat只有一列,則為0
		SVMtrainMat.at<float>(0, i) = descriptors[i];  									  //  n++;
	}

	SVMtrainMat.convertTo(SVMtrainMat, CV_32FC1);//更改圖片資料的型別,必要,不然會出錯
	int ret = (int)SVM_params->predict(SVMtrainMat);//檢測結果  
	cout << "識別的數字為:" << ret << endl;
	waitKey(0);
	return 0;
}



void feature(Mat dealimage)
{
	//把訓練樣本放大到128,128。便於HOG提取特徵 
	Mat trainImg = Mat(Size(128, 128), CV_8UC1); 
	resize(dealimage, trainImg, trainImg.size());
	//處理HOG特徵 
	//檢測視窗(64,128),塊尺寸(16,16),塊步長(8,8),cell尺寸(8,8),直方圖bin個數9  ,需要修改
	HOGDescriptor *hog = new HOGDescriptor(Size(128, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);
	vector<float>descriptors;//存放結果    為HOG描述子向量    
	hog->compute(trainImg, descriptors, Size(1, 1), Size(0, 0)); //Hog特徵計算,檢測視窗移動步長(1,1)     

	//cout << "HOG描述子向量維數    : " << descriptors.size() << endl;


	for (vector<float>::size_type j = 0; j < descriptors.size(); j++)
	{
		//把一幅影象的HOG描述子向量依次存入data_mat矩陣的同一列
		data_mat.at<float>(yangben_data_position, j) = descriptors[j];
	}
}

 

識別結果: