iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++OpenCV实战之手写数字识别
  • 317
分享到

C++OpenCV实战之手写数字识别

C++OpenCV手写数字识别C++OpenCV数字识别OpenCV数字识别 2022-11-13 14:11:22 317人浏览 薄情痞子
摘要

目录前言一、准备数据集二、KNN训练三、模型预测及结果显示四、源码总结前言 本案例通过使用Machine learning机器学习模块进行手写数字识别。源码注释也写得比较清楚啦,大家

前言

本案例通过使用Machine learning机器学习模块进行手写数字识别。源码注释也写得比较清楚啦,大家请看源码注释!!!

一、准备数据集

原图如图所示:总共有0~9数字类别,每个数字共20个。现在需要将下面图片切分成训练数据图片、测试数据图片。该图片尺寸为560x280,故将其切割成28x28大小数据图片。具体请看源码注释。

	const int classNum = 10;  //总共有0~9个数字类别
	const int picNum = 20;//每个类别共20张图片
	const int pic_w = 28;//图片宽
	const int pic_h = 28;//图片高

	//将数据集分为训练集、测试集
	double totalNum = classNum * picNum;//图片总数
	double per = 0.8;	//百分比--修改百分比可改变训练集、测试集比重
	double trainNum = totalNum * per;//训练图片数量
	double testNum = totalNum * (1.0 - per);//测试图片数量

下面需要将整张图像一一切割成28x28小尺寸图片作为数据集,填充至训练集与测试集。

	Mat Train_Data, Train_Label;//用于训练
	vector<MyNum>TestData;//用于测试
	for (int i = 0; i < picNum; i++)
	{
		for (int j = 0; j < classNum; j++)
		{
			//将所有图片数据都拷贝到Mat矩阵里
			Mat temp;
			gray(Range(j*pic_w, j*pic_w + pic_w), Range(i*pic_h, i*pic_h + pic_h)).copyTo(temp);
			Train_Data.push_back(temp.reshape(0, 1)); //将temp数字图像reshape成一行数据,然后一一追加到Train_Data矩阵中
			Train_Label.push_back(j);

			//而外用于测试
			if (i * classNum + j >= trainNum)
			{
				TestData.push_back({ temp,Rect(i*pic_w,j*pic_h,pic_w,pic_h),j });
			}
		}
	}

接下来就是要将数据集进行格式转换。

    //准备训练数据集
    Train_Data.convertTo(Train_Data, CV_32FC1); //转化为CV_32FC1类型
    Train_Label.convertTo(Train_Label, CV_32FC1);
    Mat TrainDataMat = Train_Data(Range(0, trainNum), Range::all()); //只取trainNum行训练
    Mat TrainLabelMat = Train_Label(Range(0, trainNum), Range::all());

二、KNN训练

这里使用OpenCV中的KNN算法进行训练。

    //KNN训练
    const int k = 3;  //k值,取奇数,影响最终识别率
    Ptr<KNearest>knn = KNearest::create();  //构造KNN模型
    knn->setDefaultK(k);//设定k值
    knn->setIsClassifier(true);//KNN算法可用于分类、回归。
    knn->setAlGorithmType(KNearest::BRUTE_FORCE);//字符匹配算法
    knn->train(TrainDataMat, ROW_SAMPLE, TrainLabelMat);//模型训练

三、模型预测及结果显示

	//预测及结果显示
	double count = 0.0;
	Scalar color;
	for (int i = 0; i < TestData.size(); i++)
	{
		//将测试图片转成CV_32FC1,单行形式
		Mat data = TestData[i].mat.reshape(0, 1);
		data.convertTo(data, CV_32FC1);
		Mat sample = data(Range(0, data.rows), Range::all());

		float f = knn->predict(sample); //预测
		if (f == TestData[i].label)
		{
			color = Scalar(0, 255, 0); //如果预测正确,绘制绿色,并且结果+1
			count++;
		}
		else
		{
			color = Scalar(0, 0, 255);//如果预测错误,绘制红色
		}

		rectangle(src, TestData[i].rect, color, 2);
	}

	//将绘制结果拷贝到一张新图上
	Mat result(Size(src.cols, src.rows + 50), CV_8UC3, Scalar::all(255));
	src.copyTo(result(Rect(0, 0, src.cols, src.rows)));
	//将得分在结果图上显示
	char text[10];
	int score = (count / testNum) * 100;
	sprintf_s(text, "%s%d%s", "Score:", score, "%");
	putText(result, text, Point((result.cols / 2) - 80, result.rows - 15), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
	

如图为不同比重训练集与测试集识别结果。

四、源码

#include<iOStream>
#include<opencv2/opencv.hpp>
#include<opencv2/ml.hpp>
using namespace std;
using namespace cv;
using namespace cv::ml;


//**自定义结构体
struct MyNum
{
	cv::Mat mat; //数字图片
	cv::Rect rect;//相对整张图所在矩形
	int label;//数字标签
};

int main()
{
	Mat src = imread("digit.png");
	if (src.empty())
	{
		cout << "No Image..." << endl;
		system("pause");
		return -1;
	}

	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);

	const int classNum = 10;  //总共有0~9个数字类别
	const int picNum = 20;//每个类别共20张图片
	const int pic_w = 28;//图片宽
	const int pic_h = 28;//图片高

	//将数据集分为训练集、测试集
	double totalNum = classNum * picNum;//图片总数
	double per = 0.8;	//百分比--修改百分比可改变训练集、测试集比重
	double trainNum = totalNum * per;//训练图片数量
	double testNum = totalNum * (1.0 - per);//测试图片数量

	Mat Train_Data, Train_Label;//用于训练
	vector<MyNum>TestData;//用于测试
	for (int i = 0; i < picNum; i++)
	{
		for (int j = 0; j < classNum; j++)
		{
			//将所有图片数据都拷贝到Mat矩阵里
			Mat temp;
			gray(Range(j*pic_w, j*pic_w + pic_w), Range(i*pic_h, i*pic_h + pic_h)).copyTo(temp);
			Train_Data.push_back(temp.reshape(0, 1)); //将temp数字图像reshape成一行数据,然后一一追加到Train_Data矩阵中
			Train_Label.push_back(j);

			//额外用于测试
			if (i * classNum + j >= trainNum)
			{
				TestData.push_back({ temp,Rect(i*pic_w,j*pic_h,pic_w,pic_h),j });
			}
		}
	}

	//准备训练数据集
	Train_Data.convertTo(Train_Data, CV_32FC1); //转化为CV_32FC1类型
	Train_Label.convertTo(Train_Label, CV_32FC1);
	Mat TrainDataMat = Train_Data(Range(0, trainNum), Range::all()); //只取trainNum行训练
	Mat TrainLabelMat = Train_Label(Range(0, trainNum), Range::all());

	//KNN训练
	const int k = 3;  //k值,取奇数,影响最终识别率
	Ptr<KNearest>knn = KNearest::create();  //构造KNN模型
	knn->setDefaultK(k);//设定k值
	knn->setIsClassifier(true);//KNN算法可用于分类、回归。
	knn->setAlgorithmType(KNearest::BRUTE_FORCE);//字符匹配算法
	knn->train(TrainDataMat, ROW_SAMPLE, TrainLabelMat);//模型训练

	//预测及结果显示
	double count = 0.0;
	Scalar color;
	for (int i = 0; i < TestData.size(); i++)
	{
		//将测试图片转成CV_32FC1,单行形式
		Mat data = TestData[i].mat.reshape(0, 1);
		data.convertTo(data, CV_32FC1);
		Mat sample = data(Range(0, data.rows), Range::all());

		float f = knn->predict(sample); //预测
		if (f == TestData[i].label)
		{
			color = Scalar(0, 255, 0); //如果预测正确,绘制绿色,并且结果+1
			count++;
		}
		else
		{
			color = Scalar(0, 0, 255);//如果预测错误,绘制红色
		}

		rectangle(src, TestData[i].rect, color, 2);
	}

	//将绘制结果拷贝到一张新图上
	Mat result(Size(src.cols, src.rows + 50), CV_8UC3, Scalar::all(255));
	src.copyTo(result(Rect(0, 0, src.cols, src.rows)));
	//将得分在结果图上显示
	char text[10];
	int score = (count / testNum) * 100;
	sprintf_s(text, "%s%d%s", "Score:", score, "%");
	putText(result, text, Point((result.cols / 2) - 80, result.rows - 15), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
	imshow("test", result);
	imwrite("result.jpg", result);
	waiTKEy(0);
	system("pause");
	return 0;
}

总结

本文使用OpenCV c++ 利用ml模块进行手写数字识别,源码注释也比较详细,主要操作有以下几点。

1、数据集划分为训练集与测试集

2、进行KNN训练

3、进行模型预测以及结果显示

以上就是C++ OpenCV实战之手写数字识别的详细内容,更多关于C++ OpenCV手写数字识别的资料请关注编程网其它相关文章!

--结束END--

本文标题: C++OpenCV实战之手写数字识别

本文链接: https://www.lsjlt.com/news/171998.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c语言怎么计算字符串长度
    在C语言中,可以使用内置的strlen()函数来计算字符串的长度。该函数位于string.h头文件中。以下是一个示例代码: #inc...
    99+
    2024-05-14
    c语言
  • c语言怎么判断三个数的大小
    可以通过比较三个数之间的大小关系来判断它们的大小。以下是一个示例代码: #include <stdio.h> int ...
    99+
    2024-05-14
    c语言
  • c++排序函数sort怎么使用
    在C++中,使用sort函数对容器中的元素进行排序,可以按照默认的升序排列,也可以自定义排序规则。 下面是一个简单的例子,对一个ve...
    99+
    2024-05-14
    c++
  • c++中string的compare函数怎么使用
    在C++中,可以使用string的compare函数来比较两个string对象的大小。该函数的原型如下: int compare(c...
    99+
    2024-05-14
    c++
  • c++中string的compare函数有什么用
    c++中string的compare函数用于比较两个字符串的大小关系。该函数返回一个整数值,表示两个字符串的比较结果。 具体来说,如...
    99+
    2024-05-14
    c++
  • c++中print函数的功能有哪些
    在C++中,通常使用std::cout来进行输出操作,而不是使用print函数。std::cout是C++标准库中的一个输出流对象,...
    99+
    2024-05-14
    c++
  • c语言数据结构怎么使用
    在C语言中,数据结构可以通过定义结构体来实现。以下是一些常见的数据结构的使用方法示例: 链表: #include <std...
    99+
    2024-05-14
    c语言
  • c++中print函数的用法是什么
    在C++中没有内置的print函数,但可以使用cout来输出内容到控制台。cout是C++标准库中的一个输出流对象,可以使用<...
    99+
    2024-05-14
    c++
  • c++中concept的用法是什么
    在C++20中,Concept是一种新的语言特性,用于定义类型要求和约束。Concept可以被用来约束函数模板、类模板和普通函数的参...
    99+
    2024-05-14
    c++
  • c++中concept的作用是什么
    在C++中,concept的作用是定义一种通用的约束,用于限制模板参数的类型范围。通过使用concept,可以在编译时对模板参数进行...
    99+
    2024-05-14
    c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作