OpenCV C++案例实战二十八《手写数字识别》

OpenCV C++案例实战二十八《手写数字识别》


前言

本案例通过使用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(ipic_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());
          
          	<span class="token keyword">float</span> f <span class="token operator">=</span> knn<span class="token operator">-&gt;</span><span class="token function">predict</span><span class="token punctuation">(</span>sample<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//预测</span>
          	<span class="token keyword">if</span> <span class="token punctuation">(</span>f <span class="token operator">==</span> TestData<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>label<span class="token punctuation">)</span>
          

          {
          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(jpic_w, jpic_w + pic_w), Range(ipic_h, ipic_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(ipic_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、进行模型预测以及结果显示

              • 0
                点赞
              • 0
                收藏
                觉得还不错? 一键收藏
              • 0
                评论
              好的,这是一个非常有趣的问题。MNIST是一个非常著名的手写数字数据集,用于测试机器学习算法的性能。在C++中,我们可以使用OpenCV库来读取和处理图像数据,并使用机器学习库(如TensorFlow)来构建数字识别模型。下面是一个基本的MNIST手写数字识别示例: 1. 首先,我们需要下载MNIST数据集,可以从官方网站下载:http://yann.lecun.com/exdb/mnist/ 2. 接下来,我们需要使用OpenCV库来读取图像数据。可以使用以下代码: ``` cv::Mat image = cv::imread("mnist_image.png", CV_LOAD_IMAGE_GRAYSCALE); ``` 这将读取图像数据并将其转换为灰度图像。 3. 然后,我们需要将图像数据转换为适合机器学习算法的格式。可以使用以下代码: ``` cv::Mat_<float> image_data = image.reshape(1, 1); image_data /= 255.0f; ``` 这将将图像数据转换为一个浮点数矩阵,并将其归一化为0到1之间的值。 4. 接下来,我们可以使用机器学习库来构建数字识别模型。这里假设我们使用TensorFlow库。可以使用以下代码: ``` tensorflow::GraphDef graph_def; tensorflow::Session* session; tensorflow::SessionOptions session_options; tensorflow::ReadBinaryProto(tensorflow::Env::Default(), "mnist_model.pb", &graph_def); session_options.config.mutable_gpu_options()->set_allow_growth(true); session = tensorflow::NewSession(session_options); tensorflow::Status status = session->Create(graph_def); ``` 这将读取预训练的数字识别模型,并创建一个TensorFlow会话。 5. 最后,我们可以使用以下代码来预测图像中的数字: ``` tensorflow::Tensor input_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, 784})); auto input_tensor_mapped = input_tensor.tensor<float, 2>(); for (int i = 0; i < 784; i++) { input_tensor_mapped(0, i) = image_data(0, i); } std::vector<tensorflow::Tensor> output_tensors; tensorflow::Status status = session->Run({{"input", input_tensor}}, {"output"}, {}, &output_tensors); float* prediction = output_tensors[0].flat<float>().data(); ``` 这将将图像数据输入到数字识别模型中,并返回一个浮点数数组,表示每个数字的概率。我们可以选择具有最高概率的数字作为预测结果。 希望这个示例可以帮助你了解如何在C++中基于OpenCV实现MNIST手写数字识别
              评论
              添加红包

              请填写红包祝福语或标题

              红包个数最小为10个

              红包金额最低5元

              当前余额3.43前往充值 >
              需支付:10.00
              成就一亿技术人!
              领取后你会自动成为博主和红包主的粉丝 规则
              hope_wisdom
              发出的红包
              实付
              使用余额支付
              点击重新获取
              扫码支付
              钱包余额 0

              抵扣说明:

              1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
              2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

              余额充值