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

關於SVM的原理有很多優秀的視頻和資料,這裏我主要說下利用SVM對數字識別的具體應用

首先,需要有數字的訓練樣本

https://download.csdn.net/download/weixin_41721222/10784418

把0-9文件夾放入模版匹配樣本之中,自己可修改。

 

核心思路:

1:獲取一張訓練圖片後會將圖片特徵寫入到容器中,緊接着會將標籤寫入另一個容器中,這樣就保證了特徵和標籤是一一對應的關係。

2:特徵可用LBP,HOG等提取,但是我們這裏主要說SVM訓練過程,所以用最簡單的方法,即把訓練圖片的全部像素序列成一行像素作爲特徵,用reshape(1,1)。

3:圖片特徵數據得轉換成CV_32FC1的數據格式。

下面代碼是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;

int main()
{
	//核心思路://獲取一張圖片後會將圖片特徵寫入到容器中,
				//緊接着會將標籤寫入另一個容器中,這樣就保證了特徵
				//  和標籤是一一對應的關係。
	////===============================讀取訓練數據===============================////
	const int classsum = 10;//圖片共有10類,可修改
	const int imagesSum = 500;//每類有張圖片,可修改	
	//訓了樣本圖片與測試圖片的尺寸應該一樣
	const int imageRows = 20;//圖片尺寸
	const int imageCols = 20;
	//訓練數據,每一行一個訓練圖片
	Mat trainingData;
	//訓練樣本標籤
	Mat labels;
	//最終的訓練樣本標籤
	Mat clas;
	//最終的訓練數據
	Mat traindata;
	//////////////////////從指定文件夾下提取圖片//////////////////
	for (int p = 0; p < classsum; p++)//依次提取0到9文件夾中的圖片
	{
		oss << "C:/Users/zhang/Desktop/opencv——實例/小案例/車牌檢測/基於adaboost機器學習/模版匹配樣本/";
		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];

		
		//注意:我們簡單粗暴將整個圖的所有像素作爲了特徵,因爲我們關注更多的是整個的訓練過程
		//,所以選擇了最簡單的方式完成特徵提取工作,除此中外,
		//特徵提取的方式有很多,比如LBP,HOG等等
		//我們利用reshape()函數完成特徵提取,
		//eshape(1, 1)的結果就是原圖像對應的矩陣將被拉伸成一個一行的向量,作爲特徵向量。 
			dealimage = dealimage.reshape(1, 1);//圖片序列化
			trainingData.push_back(dealimage);//序列化後的圖片依次存入
			labels.push_back(label);//把每個圖片對應的標籤依次存入
		}
	}
	//圖片數據和標籤轉變下
	Mat(trainingData).copyTo(traindata);//複製
	traindata.convertTo(traindata, CV_32FC1);//更改圖片數據的類型,必要,不然會出錯
	Mat(labels).copyTo(clas);//複製


	////===============================創建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(traindata, ROW_SAMPLE, clas);

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

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


	////===============================預測部分===============================////
	Mat src = imread("C:/Users/zhang/Desktop/opencv——實例/小案例/車牌檢測/基於adaboost機器學習/檢測到的車牌字符/7.jpg");
	cvtColor(src, src, COLOR_BGR2GRAY);
	threshold(src, src, 0, 255, CV_THRESH_OTSU);
	imshow("原圖像", src);
	Mat input;
	src = src.reshape(1, 1);//輸入圖片序列化
	input.push_back(src);
	input.convertTo(input, CV_32FC1);//更改圖片數據的類型,必要,不然會出錯

	float r = SVM_params->predict(input);   //對所有行進行預測
	cout << r << endl;
	waitKey(0);
	return 0;
}

 

識別結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章