在上一篇博客《OpenCV4学习笔记(54)》中,整理了关于KNN最近邻算法的一些相关内容和一个手写体数字识别的例子。但是上次所实现的手写体数字识别,每次只能固定地输入测试图像进行预测,而且所使用的也是从OpenCV自带测试图中裁剪下来的数字。那么这一次笔记,我们就把上一次笔记的内容和OpenCV中的鼠标操作进行结合,实现一个可以利用鼠标书写数字并实时进行手写体数字识别的小程序。

关于KNN算法实现手写数字识别的部分内容,可以参阅我的上一篇博客,这里就不多赘述了。这里主要看一下OpenCV中关于鼠标操作的内容。

首先,我们需要声明一个鼠标的回调函数:

static void on_Mouse(int event, int x, int y, int flag, void*);

该函数的参数含义如下:
(1)参数event:鼠标事件
(2)参数x:鼠标当前点的x坐标
(3)参数y:鼠标当前的的y坐标
(4)参数flag:鼠标所处状态

然后,在程序中需要使用鼠标的时候,就加入一条定义鼠标操作的函数,当鼠标位于该指定窗口上时,执行回调函数:

 setMouseCallback("digit", on_Mouse, 0);

该函数参数含义如下:
(1)参数windowName:指定的窗口名,当鼠标位于该窗口上时就执行回调函数。
(2)参数MouseCallback:需要执行的鼠标回调函数。
(3)参数userdata:用户传入数据,不需要可以直接置零。

接着就需要定义我们的鼠标回调函数来进行所需的操作了,在窗口中按下鼠标左键进行书写数字,写完后按空格键进行识别,按鼠标右键是清空窗口。代码演示如下:

static void on_Mouse(int event, int x, int y, int flag, void*)
{if (event == EVENT_LBUTTONDOWN)          //当鼠标左键按下,获取该点坐标{prePoint = Point(x, y);}//当鼠标左键保持按下标志,且鼠标移动else if (event == EVENT_MOUSEMOVE && (flag & EVENT_FLAG_LBUTTON)){Point nowPoint(x, y);line(digit, nowPoint, prePoint, Scalar(255), 2, 8, 0);prePoint = nowPoint;     //将鼠标的当前点作为上个点位,以开始下一个线段区域的绘制imshow("digit", digit);           //在窗口更新目标图像}//当鼠标右键按下时else if (event == EVENT_RBUTTONDOWN){imshow("digit", canvas);digit = canvas.clone();}
}

到这里,就实现了在一个窗口中书写数字的功能了,接下来只需要把上次笔记中的训练、识别部分加进来就可以了,下面是完整代码演示:

#include<opencv2/opencv.hpp>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;
using namespace cv;Point prePoint;              //定义鼠标指向的上一个点
Mat canvas = Mat::zeros(Size(28, 28), CV_8UC1);            //黑底画布
Mat digit = canvas.clone();            //鼠标写下的手写体数字
static void on_Mouse(int event, int x, int y, int flag, void*);         //定义鼠标操作的回调函数
void training();            //训练函数int main()
{//未得到模型前需要调用训练函数//training();//加载训练好的knn模型Ptr<ml::KNearest> knn_test = ml::KNearest::load("knn_digits_model.yml");namedWindow("digit", WINDOW_NORMAL);resizeWindow("digit", Size(100, 100));imshow("digit", canvas);setMouseCallback("digit", on_Mouse, 0);           //定义鼠标操作,当鼠标位于该指定窗口上时,执行回调函数int i = 0;while (true){char ch = waitKey();if (ch == ' '){//resize(digit, digit, Size(28, 28));digit = digit.reshape(1, 1);digit.convertTo(digit, CV_32F);Mat results = Mat::zeros(Size(1, 1), CV_32F);knn_test->findNearest(digit, 11, results);cout << "预测手写数字结果:  " << results.at<float>(0, 0) << endl;/*     resize(digit, digit, Size(28, 28));string path1 = "D:/opencv_c++/Learning-OpenCV/KNN手写数字识别/KNN手写数字识别/train_data/ " + to_string(i) + "_" + to_string(260) + ".jpg";imwrite(path1, digit);i++;*/}else if (ch == 27){break;}}waitKey(0);return 0;
}static void on_Mouse(int event, int x, int y, int flag, void*)
{if (event == EVENT_LBUTTONDOWN)          //当鼠标左键按下,获取该点坐标{prePoint = Point(x, y);}//当鼠标左键保持按下标志,且鼠标移动else if (event == EVENT_MOUSEMOVE && (flag & EVENT_FLAG_LBUTTON)){Point nowPoint(x, y);line(digit, nowPoint, prePoint, Scalar(255), 2, 8, 0);prePoint = nowPoint;     //将鼠标的当前点作为上个点位,以开始下一个线段区域的绘制imshow("digit", digit);           //在窗口更新目标图像}//当鼠标右键按下时else if (event == EVENT_RBUTTONDOWN){imshow("digit", canvas);digit = canvas.clone();}
}void training()
{vector<Mat> train_images;for (int i = 0; i < 10; i++){for (int j = 0; j < 760; j++){string path = "D:/opencv_c++/Learning-OpenCV/KNN手写数字识别/KNN手写数字识别/train_data/" + to_string(i) + "_" + to_string(j) + ".jpg";Mat train_image = imread(path);cvtColor(train_image, train_image, COLOR_BGR2GRAY);train_images.push_back(train_image);}}for (int n = 0; n < train_images.size(); n++){train_images[n] = train_images[n].reshape(1, 1);}int height = 7600;int width = 784;Mat train_data = Mat::zeros(Size(width, height), CV_8UC1);for (int r = 0; r < height; r++){for (int c = 0; c < width; c++){train_data.at<uchar>(r, c) = train_images[r].at<uchar>(0, c);}}Mat labels = Mat::zeros(Size(1, height), CV_8UC1);for (int k = 0; k < height; k++){int label = k / 760;labels.at<uchar>(k, 0) = label;}train_data.convertTo(train_data, CV_32F);labels.convertTo(labels, CV_32F);auto knn = ml::KNearest::create();int K = 11;knn->setDefaultK(K);knn->setIsClassifier(true);auto data = ml::TrainData::create(train_data, ml::ROW_SAMPLE, labels);knn->train(data);knn->save("knn_digits_model.yml");//使用训练集进行测试,计算正确率Mat results = Mat::zeros(labels.size(), CV_32F);knn->findNearest(train_data, K, results);float acc = 0;for (int n = 0; n < labels.rows; n++){int result = results.at<float>(n, 0);int label = labels.at<float>(n, 0);if (result == label){acc++;}}acc = acc / 7600 * 100;cout << "正确率:  " << acc << "%" << endl;}

注意,第一次运行的时候需要先进行训练,当得到手写体数字识别模型后就可以把train()函数给注释掉,直接进行识别。

下面就来看一下实现的效果是怎样的:






在演示中,我从0到5依次书写数字,除了最后的5被识别成了3,其他均识别正确,至于后面的数字就不再一一演示,有兴趣的朋友可以自己尝试一下。

好了,本次笔记就到此结束啦,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

OpenCV4学习笔记(55)——基于KNN最近邻算法实现鼠标手写数字识别相关推荐

  1. 机器学习框架ML.NET学习笔记【5】多元分类之手写数字识别(续)

    一.概述 上一篇文章我们利用ML.NET的多元分类算法实现了一个手写数字识别的例子,这个例子存在一个问题,就是输入的数据是预处理过的,很不直观,这次我们要直接通过图片来进行学习和判断.思路很简单,就是 ...

  2. kNN算法实现手写数字识别(机器学习)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.实验步骤 二.实验过程 1.收集数据:提供文本文件 2.准备数据:将图像转换为测试向量 3.测试算法:使用k-近邻 ...

  3. 机器学习入门-kNN算法实现手写数字识别

    实验环境 Python:3.7.0 Anconda:3-5.3.1 64位 操作系统:win10 开发工具:sublime text(非必要) 简介 本次实验中的重点为采用kNN算法进行手写数字识别, ...

  4. k-近邻算法实现手写数字识别系统

    k-近邻算法实现手写数字识别系统 一.实验介绍 1.1 实验内容 本实验将会从电影题材分类的例子入手,详细讲述k-近邻算法的原理.在这之后,我们将会使用该算法实现手写数字识别系统. 1.2 课程来源 ...

  5. 深度学习(32)随机梯度下降十: 手写数字识别问题(层)

    深度学习(32)随机梯度下降十: 手写数字识别问题(层) 1. 数据集 2. 网络层 3. 网络模型 4. 网络训练 本节将利用前面介绍的多层全连接网络的梯度推导结果,直接利用Python循环计算每一 ...

  6. 基于随机梯度下降法的手写数字识别、epoch是什么、python实现

    基于随机梯度下降法的手写数字识别.epoch是什么.python实现 一.普通的随机梯度下降法的手写数字识别 1.1 学习流程 1.2 二层神经网络类 1.3 使用MNIST数据集进行学习 注:关于什 ...

  7. 基于matlab BP神经网络的手写数字识别

    摘要 本文实现了基于MATLAB关于神经网络的手写数字识别算法的设计过程,采用神经网络中反向传播神经网络(即BP神经网络)对手写数字的识别,由MATLAB对图片进行读入.灰度化以及二值化等处理,通过神 ...

  8. MATLAB实现数字识别系统,基于人工神经网络的MATLAB手写数字识别系统

    <基于人工神经网络的MATLAB手写数字识别系统>由会员分享,可在线阅读,更多相关<基于人工神经网络的MATLAB手写数字识别系统(8页珍藏版)>请在人人文库网上搜索. 1.基 ...

  9. 深蓝学院第三章:基于卷积神经网络(CNN)的手写数字识别实践

    参看之前篇章的用全连接神经网络去做手写识别:https://blog.csdn.net/m0_37957160/article/details/114105389?spm=1001.2014.3001 ...

最新文章

  1. 实战SSM_O2O商铺_25【商品类别】商品类别列表展示从Dao到View层的开发
  2. java多线程中方法_java中多线程 - 多线程中的基本方法
  3. [BSidesSF2020]haystack
  4. 【AI不惑境】数据压榨有多狠,人工智能就有多成功
  5. 通过互联网启动Linux
  6. python 整数逆位运算_Python 进制转换、位运算
  7. python3 os模块相关方法
  8. Java有哪些常用的转换类,JavaSE——常用类、类型转换
  9. SAP HANA创建类型(SAP HANA CREATE TYPE):
  10. python之pyc
  11. Atitit attilax提出的软件开发发展趋势与概念 1. 长期化 复用化 跨平台 可移植性 1 2. 通用化 通用 化的渠道至少有3种 1 2.1. 模块化 1 2.2. 标准化接口 1 2
  12. 鲸鱼优化算法_鲸鱼优化算法:一种群体智能最优化方法
  13. MySql 常用命令集
  14. LeetCode | 0665. Non-decreasing Array非递减数列【Python】
  15. Android蓝牙通信
  16. 全球某工商云战役自动打卡系统
  17. FFmpeg入门详解之12:Elecard Stream Analyzer码流分析工具
  18. 2017 年前端工具趋势
  19. JS实现右键拖动元素
  20. 天顶围棋 8 zenith 8_世界围棋冠军数排名TOP10:李昌镐第一 古力柯洁并列第4

热门文章

  1. 微型计算机控制系统分几类,微型计算机控制技术 潘新民版 答案
  2. linux系统修改时区
  3. Tekton构建Springboot项目操作手册
  4. 如何合并视频?教你把多个视频合并成一个视频
  5. 网站广告1像素1元,超有创意的百万像素网站
  6. Pascal's Travels 动规小练
  7. 隐藏在《王者荣耀》中程序24种设计模式
  8. PCB设计布局布线,这几点技巧必须要了解!
  9. Armadillo | 复数小记
  10. C/C++——vector的基本操作总结