1. 程式人生 > >opencv——基於KNN的數字識別

opencv——基於KNN的數字識別

KNN即K個最近鄰,網上有很多關於KNN的文章。我大概總結下核心:假設有A圖片,讓A與訓練樣本依次計算相似度(可用歐式距離),挑選出K個與A圖片相似度最大的圖片,這K個圖片中,哪種型別最多那麼定義A圖片也屬於該型別。

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

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

 

KNN的數字識別程式碼與基於SVM的數字識別大體一致

https://blog.csdn.net/weixin_41721222/article/details/84953788

核心思路:

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

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

3:圖片特徵資料得轉換成CV_32FC1的資料格式。

4:所有訓練樣本與測試樣本的尺寸都應該一樣(這裡我都選擇20*20)。

下面程式碼是opencv3和C++

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

 

/// 字元識別——基於模版匹配.cpp: 定義控制檯應用程式的入口點。
//


#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;
using namespace ml;
ostringstream oss;
int num = -1;
Mat dealimage;
Mat src;
int k = 0;
Mat yangben_gray;
Mat yangben_thresh;

int main()
{

	////===============================讀取訓練資料===============================////
	const int classsum = 10;//圖片共有10類
	const int imagesSum =500;//每類有500張圖片			   
	const int imageRows = 20;//圖片尺寸
	const int imageCols = 20;
	//訓練資料,每一行一個訓練圖片
	Mat trainingData;
	//訓練樣本標籤
	Mat labels;
	//最終的訓練樣本標籤
	Mat clas;
	//最終的訓練資料
	Mat traindata;
	//////////////////////從指定資料夾下提取圖片//////////////////
	for (int p = 0; p < classsum; p++)
	{
		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);
			input_images.push_back(yangben_thresh);
			//迴圈讀取每張圖片並且依次放在vector<Mat> input_images內
			dealimage = input_images[i];


			//注意:我們簡單粗暴將整個圖的所有畫素作為了特徵,因為我們關注更多的是整個的訓練過程
			//,所以選擇了最簡單的方式完成特徵提取工作,除此中外,
			//特徵提取的方式有很多,比如LBP,HOG等等
			//我們利用reshape()函式完成特徵提取,
			//reshape(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);//複製

	////===============================建立KNN模型===============================////
	Ptr<KNearest>knn = KNearest::create();
	knn->setDefaultK(10);//k個最近領
	knn->setIsClassifier(true);//true為分類,false為迴歸
	//訓練資料和標籤的結合
	Ptr<TrainData>trainData = TrainData::create(traindata, ROW_SAMPLE, clas);
	//訓練
	knn->train(trainData);

	//model->save("E:/image/KNearestModel.xml"); 

	////===============================預測部分===============================////
	//預測分類
	Mat src = imread("C:/Users/zhang/Desktop/opencv——例項/小案例/車牌檢測/基於adaboost機器學習/檢測到的車牌字元/4.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 = knn->predict(input);   //對所有行進行預測
	cout << r << endl;
	waitKey(0);
	return 0;
}




 

 

結果: