opencv中利用KNN进行手写数字识别

环境:opencv3.3 + C++ +win10 64位

利用KNN进行手写数字识别,在opencv的文件夹中提供了一个可以用来训练的照片,一共有5000个小样本每个数字对应的有500个图片 。对应的文件夹应该是 opencv/sources/samples/data/digits.png



其中每个小图片的样本是20*20 作为训练集和预测集的图片大小必须一致,所以用程序把每一个数字都切出来,所切的图片大小为20*20放入矩阵,原图的大小为1000*2000。

Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);
	int b = 20;
	int m = gray.rows / b;   //每一行图片的个数
	int n = gray.cols / b;   //每一列图片的个数
	Mat data, labels;   //特征矩阵
	for (int i = 0; i < n; i++)
	{
		int offsetCol = i * b; //列上的偏移量
		for (int j = 0; j < m; j++)
		{
			int offsetRow = j * b;  //行上的偏移量
									//截取20*20的小块
			Mat tmp;
			gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
			data.push_back(tmp.reshape(0, 1));  //序列化后放入特征矩阵
			labels.push_back((int)j / 5);  //对应的标注
		}

	}
 
 

把每个特征都加入向量data,把对应的标注加入向量labels。然后调用opencv中的KNN算法对样本进行训练。

int K = 7;
	Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabels);
	Ptr<KNearest> model = KNearest::create();
	model->setDefaultK(K);
	model->setIsClassifier(true);
	model->train(tData);
	model->save("KnnTest.xml");

用4000的样本作为训练集,用1000的样本作为验证集。训练完毕后调用训练好的模型对数据进验证,发现对本身的验证准确度能达到90%

double train_hr = 0, test_hr = 0;
	//Mat response;
	//// compute prediction error on train and test data
	//for (int i = 3000; i < samplesNum; i++)
	//{
	//	Mat sample = data.row(i);
	//	float r = model->predict(sample);   //对所有行进行预测
	//										//预测结果与原结果相比,相等为1,不等为0
	//	r = std::abs(r - labels.at<int>(i)) <= FLT_EPSILON ? 1.f : 0.f;

后来用自己手写的数据进行测试发现准准确度较低,所以对样本的数据加入自己写的数据在进行测试发现效果比较差-,-


于是我对手写的样本又加入了周围人的字体样式,每个样本大约增加20个数据后进行测试,准确度明显提高(如下图所示)。其中对加入的样本一定要保证都为CV_32F格式。


下面是代码

	//加入自己写的数据集
	Mat src = imread("E:/picture/shuziceshi.png");
	//imshow("Input img", src);
	Mat gray_frame, thres_img, blur_img;
	cvtColor(src, gray_frame, COLOR_BGR2GRAY);//对图像进行预处理(图像去燥二值化)
	//medianBlur(gray_frame, gray_frame, 3);
	GaussianBlur(gray_frame, blur_img, Size(3, 3), 3, 3);
	//threshold(gray_frame, thres_img, 0, 255, THRESH_BINARY | THRESH_OTSU);
	adaptiveThreshold(blur_img, thres_img, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 151, 10);
	Mat morph_img, tmp2,tmp3;
	Mat kernerl = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	morphologyEx(thres_img, morph_img, MORPH_OPEN, kernerl, Point(-1, -1));
	//imshow("Bin", morph_img);
	vector<vector<Point>> contours;
	vector<Vec4i> hiearachy;
	int k = 0;//显示想要的轮廓数这样就可以方便把新数据压入数据集 不用单独保存
	findContours(morph_img, contours, hiearachy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
	for (int i = 0; i < contours.size(); ++i)
	{
		
		Rect minrect = boundingRect(contours[i]);
		float area = contourArea(contours[i]);
		float ckbi = minrect.width / minrect.height;
		//cout << ckbi << endl;
		if(ckbi<4&& area>50)
		{ 
		//cout << minrect.height << endl << minrect.width;
			rectangle(src, minrect, Scalar(0, 255, 0), 1, 8);
			Rect ROI =minrect;
			Mat ROI_img = morph_img(ROI);
			resize(ROI_img, ROI_img, Size(20, 20));
			//ROI_img.convertTo(ROI_img, CV_32F);
			ROI_img.copyTo(tmp2);
			/*predict_mat.push_back(tmp2.reshape(0, 1));
			Mat predict_simple = predict_mat.row(i);*/
			//float r = model->predict(predict_simple);
			stringstream stream;
			stream << k;
			string str;
			stream >> str;
			//string_temp = stream.str();
			putText(src, str, ROI.tl(), FONT_HERSHEY_PLAIN, 1, Scalar(255, 0, 0), 1, 8);
			data.push_back(tmp2.reshape(0, 1));  //序列化后放入特征矩阵
			labels.push_back((int)k / 15);  //对应的标注
			k++;
		}
	}
	//imshow("Out", src);

	data.convertTo(data, CV_32F); //uchar型转换为cv_32f
	int samplesNum = data.rows;
	int trainNum = 5000;
	Mat trainData, trainLabels;
	trainData = data(Range(0, samplesNum), Range::all());   //前3000个样本为训练数据
	trainLabels = labels(Range(0, samplesNum), Range::all());//前三千个训练标签
	//使用KNN算法
	int K = 7;
	Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabels);
	Ptr<KNearest> model = KNearest::create();
	model->setDefaultK(K);
	model->setIsClassifier(true);
	model->train(tData);
	model->save("KnnTest.xml");

	//用样本进行测试
	Mat src_test = imread("E:/picture/shuzi4.png");
	imshow("Input img", src_test);
	Mat gray_test, thres_test, blur_test;
	cvtColor(src_test, gray_test, COLOR_BGR2GRAY);
	GaussianBlur(gray_test, blur_test, Size(3, 3), 3, 3);
	adaptiveThreshold(blur_test, thres_test, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 151, 10);
	Mat morph_test, predict_mat;
	morphologyEx(thres_test, morph_test, MORPH_OPEN, kernerl, Point(-1, -1));
	imshow("Bin", morph_test);
	vector<vector<Point>> contours_test;
	vector<Vec4i> hiearachy_test;
	findContours(morph_test, contours_test, hiearachy_test, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
	for (int i = 0; i < contours_test.size(); ++i)
	{

		Rect minrect_test = boundingRect(contours_test[i]);
		float area_test = contourArea(contours_test[i]);
		float ckbi_test = minrect_test.width / minrect_test.height;
		if (ckbi_test<4 && area_test>50)
		{
			rectangle(src_test, minrect_test, Scalar(0, 255, 0), 1, 8);
			Rect ROI_test = minrect_test;
			Mat ROI_img_test = morph_test(ROI_test);
			resize(ROI_img_test, ROI_img_test, Size(20, 20));
			ROI_img_test.convertTo(ROI_img_test, CV_32F);
			ROI_img_test.copyTo(tmp3);
			predict_mat.push_back(tmp3.reshape(0, 1));
			Mat predict_simple = predict_mat.row(i);
			float r = model->predict(predict_simple);
			stringstream stream;
			stream << r;
			string str;
			stream >> str;
			putText(src_test, str, ROI_test.tl(), FONT_HERSHEY_PLAIN, 1, Scalar(255, 0, 0), 1, 8);
		}

	}
	imshow("img_test", src_test)
 
 

有什么改进和不足欢迎批评指正。

参考资料:http://www.cnblogs.com/denny402/p/5033898.html


猜你喜欢

转载自blog.csdn.net/xiexu911/article/details/79685165