opencv 学习之 基于K近邻的数字识别

本文参考两篇博客,都对KNN思路做了详细说明

(1) http://blog.csdn.net/wangyaninglm/article/details/17091901

(2) http://blog.csdn.net/xiaowei_cqu/article/details/23782561#

(3) http://download.csdn.net/download/hust_bochu_xuchao/9581796

个人对于K近邻编码实现的一点理解。主要基于第一篇,第三篇是将第一篇基于OpenCV2实现,可参考。

int  trainsamples;  每类样本数

int  clesses;  样本种类数

如数字识别,0-9计10个数字,则 clesses 为10,如果每个样本库中有100个样本,如100个数字 0 样本,则 trainsamples 为100.

Mat  traindata;  存储样本数据

mat  trainclass;  样本标识

traindata = Mat(trainsamples*traincless,  templet_w*templet_h);

trainclass = Mat(trainsamples*traincless,  1);

将所有样本数据存储在 traindata 中,每个样本转换为 1*(w*h) 型矩阵,且二值化处理成只有 0 和 1 两种像素值模式

trainclass 保存每类样本标识,在 traindata 赋值过程中 赋值。如例 trainclass 为 1000*1 矩阵,那么其前100行为0,最后100行为9

训练直接调用 train() 函数即可

train(traindata, trainclass, Mat(), false, K);

Mat  testimg;  待测数据

float  result = CvKNearest::find_nearest(testimg, K)

上述为规模样本采用K近邻训练识别的理解。

网上有一篇基于K近邻的手写字符识别,样本是方形,实际中数字多为矩形。


第二篇博客也同理

for (int i = 0; i < image.rows; ++i)
{  
	for (int j = 0; j < image.cols; ++j)
	{  
		const Mat sampleMat = (Mat_<float>(1,2) << i,j);  
		Mat response;  
		float result = knn.find_nearest(sampleMat,1);  
		if (result !=0)
		{  
			image.at<Vec3b>(j, i)  = green;  
		}  
		else    
		image.at<Vec3b>(j, i)  = blue;  
	}  
}


创建 512*512 大小的矩阵,生成10个样本,实际为10对坐标。

坐标在0-256之间的,标签为0,在256-512之间的,标签为1.

将坐标分类,非 0 类绿色,0 类蓝色。



代码补充:

knnTest.h

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/ml/ml.hpp>

#include <iostream>

using namespace cv;
using namespace std;

class knnRec
{
public:
	knnRec();
	float digRec(Mat img);		//数字识别
	void  samTest();		//样本训练

private:
	char  folder[255];		//模板路径
	int   sampl_num;		//每类样本数
	int   class_num;		//样本种类数
	Mat   trainData;		//样本数据存储矩阵
	Mat   trainClass;		//样本类别存储矩阵
	static const int K = 6;         //最大邻居个数
	KNearest *	knn;
	void  getData();		//获取样本数据
	void  knnTrain();		//训练
};


knnTest.cpp


#include "knnTest.h"

knnRec::knnRec()//构造函数
{
	sprintf(folder, "..\\pics\\Template\\");
	sampl_num = 10;	//训练样本,总共10个
	class_num = 10;	//暂时识别十个数字

	int samp_w = 14;	//模板的宽和高	
	int samp_h = 27;

	trainData.create(sampl_num*class_num, samp_w*samp_h, CV_32FC1);	//训练数据的矩阵
	trainClass.create(sampl_num*class_num, 1, CV_32FC1);

	getData();
	knnTrain();
}

void knnRec::getData()
{
	char filepath[255];

	for (int i=0; i<class_num; i++)
	{
		for (int j=0; j<sampl_num; j++)
		{
			sprintf(filepath, "%s%d\\%d%d.bmp", folder, i,  i, j);

			Mat  sampl = imread(filepath, 0);
			if (sampl.empty())
			{
				printf("Error: Cant load image %s\n", filepath);
				return;
			}

			//将 MxN 矩阵 转为 1x(MxN)
			Mat  templ = sampl.clone();
			sampl.release();
			sampl.create(1, templ.cols*templ.rows, CV_32FC1);
			//sampl 当前为1行,未初始化,templ 存储 sampl 源数据
			float* data_sampl = sampl.ptr<float>(0);
			//确保模板均为二值图,否则进行二值化
			for (int j = 0; j<templ.rows;  j++)
			{
				uchar * data_templ = templ.ptr<uchar>(j);
				for (int i = 0; i<templ.cols; i++)
				{
					if (data_templ[i] == 255) 
					{
						data_sampl[j*templ.rows + i] = 1;
					}
					else 
					{
						data_sampl[j*templ.rows + i] = 0;
					}
				}
			}

			//记录模板数据
			float * data1 = trainData.ptr<float>(i*sampl_num+j);		//定位第 i 个类中第 j 个样本
			float * data2 = sampl.ptr<float>(0);
			for (int k = 0; k < sampl.cols; k++)
			{
				data1[k] = data2[k];
			}

			//记录模板标志
			trainClass.at<float>(i*sampl_num+j, 0) = i;		//定位第 i 个类中第 j 个样本,标为 i
		}
	}
}

void knnRec::knnTrain()
{    
	knn = new KNearest(trainData, trainClass, Mat(), false, K);
}

//数字识别,img 为数字区域
float knnRec::digRec(Mat src)
{	
	Mat knnImg; 
	knnImg.create(1, K, CV_32FC1);
		
	//处理输入的图像
	Mat tmp = src.clone();
	src.release();
	src.create(1, tmp.cols*tmp.rows, CV_32FC1);
	float* data_src = src.ptr<float>(0);

	for (int j = 0; j<tmp.rows; j++)
	{
		uchar* data_tmp = tmp.ptr<uchar>(j);
		for (int i = 0; i<tmp.cols; i++)
		{
			if (data_tmp[i] == 255) 
			{
				data_src[j*tmp.rows + i] = 1;
			}
			else 
			{
					data_src[j*tmp.rows + i] = 0;
			}
		}
	}

	float result = knn->find_nearest(src, K, Mat(), knnImg, Mat());

	int checkNum = 0;
	for (int i = 0; i<K; i++)
	{
		if (knnImg.at<float>(0, i) == result)
		{
			checkNum++;
		}
	}
	float pre = 100 * ((float)checkNum / (float)K);

	return result;
}


说明:

调用之前,数字区域需完成处理,即完成裁剪、二值化等。

knnRec  rec;

double  result = rec.digRec(img);





猜你喜欢

转载自blog.csdn.net/u010477528/article/details/53707845
今日推荐