手写数字识别【QT+OpenCV】

【说明】

手写数字识别的实现方式很多。

本文尽量将其简化,以让大家能够快速了解怎样实现一个动起来的系统。

【截图】

【思路】

1.特征提取

将图像划分为5*5大小的区域,然后计算该区域内黑色(或白色)的像素点所占比例。

将需要测试的图像、用来分类的图像都进行特征提取。

2.计算当前的测试图像与用来分类的图像之间的欧氏距离。

3.找出欧式距离最小的值即为与当前测试图像最匹配的图像,即将该图像所代表的数字作为当前测试图像的结果。

4.为了处理上的方便,做了简化处理如下:

4.1仅仅选用10幅用来分类的图像。

在实际应用中,10幅图像远远是不够的。但是为了简化程序,这里仅仅选用10幅图像,即数字0~9每个数字仅仅选用了一个特征。

在实际系统内,当选用的特征图像越多时,系统的准确度越高。

4.2采用了最近邻。即选用欧式距离最小的图像作为当前测试图像的结果。

在实际系统中,往往需要采用K近邻,即选择最小的K个欧式距离,判断他们分别属于哪个类从而决定当前数字结果。

因为简化的原因,数字识别的正确率不是很高。但是基本能够满足学习的需求。

【部分代码】

该系统采用QT+OpenCV开发完成,部分代码如下:

【主类内变量及槽函数】

  1. public:
  2. explicit MainWindow(QWidget *parent = 0);
  3. ~MainWindow();
  4. cv::Mat testImage,srcImage[10],tempImage;//testImage值要测试的数字图像,srcImage指已经有的用来实现分类的数字图像
  5. QImage img;
  6. float testFeature[25];//该数组用来存储待检测数字图像的特征值。
  7. float srcFeature[10][25];//用来存储原始数字图像的特征值。只有10个数字0~9的图像
  8. void getFeature(cv::Mat m,float a[25]);//这里定义一个获取图像特征的函数。
  9. float ouDistance(float a[25],float b[25]);
  10. float oDistance(float a[25],float b[25]);
  11. private slots:
  12. void on_openLenaJpg_triggered();
  13. void on_exitSystem_triggered();
  14. void on_openCustomeFile_triggered();
  15. void on_restoreFile_triggered();
  16. void on_copyright_triggered();
  17. void on_about_triggered();
  18. void on_showImage_triggered();
  19. void on_showMessage_triggered();
  20. void on_ImageAndMessage_triggered();

【打开自定义路径待测图像】

  1. void MainWindow::on_openCustomeFile_triggered()
  2. {
  3. QString filename = QFileDialog::getOpenFileName(this,tr("Open Image"),"",tr("Image File(*.bmp *.jpg *.jpeg *.png)"));
  4. QTextCodec *code = QTextCodec::codecForName("gb18030");
  5. std::string name = code->fromUnicode(filename).data();
  6. testImage = cv::imread(name);
  7. if(!testImage.data)
  8. {
  9. QMessageBox msgBox;
  10. msgBox.setText(tr("未找到数据"));
  11. msgBox.exec();
  12. }
  13. else
  14. {
  15. cv::cvtColor(testImage,testImage,CV_BGR2RGB);
  16. img = QImage((const unsigned char*)(testImage.data),testImage.cols,testImage.rows, testImage.cols*testImage.channels(), QImage::Format_RGB888);
  17. ui->label1->clear();
  18. img= img.scaled(ui->label1->width(), ui->label1->height());
  19. ui->label1->setPixmap(QPixmap::fromImage(img));
  20. //ui->processPushButton->setEnabled(true);
  21. // ui->label1->resize(ui->label1->pixmap()->size());//设置当前标签为图像大小
  22. // ui->label1->resize(img.width(),img.height());
  23. //this->setWidget(label1);
  24. }
  25. }

【图像特征提取:完成图像5*5=25个特征,每个特征表示该子区域内白像素个数】

  1. void MainWindow::getFeature(cv::Mat m,float a[25])
  2. {
  3. int M,N; //用来存储图像m的宽高
  4. int i,j;
  5. M=m.cols;
  6. N=m.rows;
  7. for(i=0;i<25;i++)
  8. a[i]=0;
  9. // QMessageBox::information(NULL, "Title", QString::number(m.at<uchar>(188,88)), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  10. for(i=0;i<M;i++)
  11. for(j=0;j<N;j++)
  12. if(m.at<uchar>(i,j)==255)
  13. {
  14. // a[i/5*5+j/5]++; //这里计算错误,不能放入对应的特征值内
  15. // QMessageBox::information(NULL, "Title", QString::number(5), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  16. // a[M/i*5+N/j]++;
  17. //a[M/(i+1)*5+N/(j+1)]++;
  18. a[i/(M/5)*5+j/(N/5)]++;
  19. // QMessageBox::information(NULL, "Title","add", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  20. }
  21. for(i=0;i<25;i++)
  22. {
  23. // QMessageBox::information(NULL, "Title", QString::number(a[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  24. a[i]=a[i]/((M/5)*(N/5));
  25. // QMessageBox::information(NULL, "Title", QString::number(a[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  26. // QMessageBox::information(NULL, "Title", QString::number(5), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  27. }
  28. // QMessageBox::information(NULL, "Title", QString::number(a[5]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  29. }

【欧氏距离计算,第二个函数是测试用的】

  1. float MainWindow::ouDistance(float a[25],float b[25]) //这个函数居然忘记写MainWindow的类关系,调试好久,瀑布汗!
  2. {
  3. int i;
  4. float distance=0;//开始忘记置零,出错呀!!!
  5. for(i=0;i<25;i++)
  6. distance+=(a[i]-b[i])*(a[i]-b[i]);
  7. distance=sqrt(distance);
  8. return distance;
  9. }
  10. float MainWindow::oDistance(float a[25],float b[25]) //这个函数是ouDistance出问题时测试的,并没有用
  11. {
  12. int i;
  13. float distance=0; //开始忘记置零,出错呀!!!
  14. //,为了测试ouDistance函数,重写了oDistance发现问题,结果再次出现问题一直却一直在此函数修改。而调用函数用的还是ouDistance
  15. for(i=0;i<25;i++)
  16. distance+=(a[i]-b[i])*(a[i]-b[i]);
  17. distance=sqrt(distance);
  18. return distance;
  19. }

【说明】

  1. void MainWindow::on_copyright_triggered()
  2. {
  3. QMessageBox::information(this,"版权",tr("本软件版权所有者为:天津职业技术师范大学。如果使用,请联系:lilizong#gmail"));
  4. }
  5. void MainWindow::on_about_triggered()
  6. {
  7. QMessageBox::information(this,"关于",tr("本软件当前版本为1.0,由李立宗等人开发。如果有问题,欢迎联系:lilizong#gmail"));
  8. return;
  9. }

【显示测试结果:一个图像、一个信息框】

  1. void MainWindow::on_ImageAndMessage_triggered()
  2. {
  3. int i;
  4. float min; //用来存储最小的欧式距离
  5. int mini; //用来存储最小的欧氏距离的数字号。
  6. getFeature(testImage,testFeature); //获取测试图像的特征值,并将其放到testFeature数组内。
  7. // QMessageBox::information(NULL, "Title", QString::number(testFeature[6]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  8. //测试当前的testFeature是否正常
  9. /*
  10. for(i=0;i<25;i++)
  11. QMessageBox::information(NULL, "Title", QString::number(testFeature[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  12. */
  13. // QMessageBox::information(NULL, "Title", QString::number(testImage.rows), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  14. for(i=0;i<10;i++)
  15. {
  16. QString filePath,fileName,allName;
  17. filePath="image\\stand\\"; //当前图像目录
  18. fileName=".bmp"; //当前图像的扩展名
  19. allName=filePath+"\\"+QString::number(i)+fileName; //i是文件名,使用QString::number(i)完成将其转换为QString类型,当前为数值型
  20. String s=allName.toStdString(); //转换为标准的字符串型,imread不识别QString类型
  21. srcImage[i] = cv::imread(s);
  22. }
  23. //以下部分用于测试上述代码是否能够将srcImage的值获取到。
  24. /*
  25. cv::cvtColor(srcImage[3],srcImage[3],CV_BGR2RGB);
  26. img = QImage((const unsigned char*)(srcImage[3].data),srcImage[1].cols,srcImage[1].rows, srcImage[1].cols*srcImage[1].channels(), QImage::Format_RGB888);
  27. ui->label1->clear();
  28. img= img.scaled(ui->label1->width(), ui->label1->height());
  29. ui->label1->setPixmap(QPixmap::fromImage(img));
  30. */
  31. // 获取原始数字图像的特征值。
  32. for(i=0;i<10;i++)
  33. getFeature(srcImage[i],srcFeature[i]);
  34. /*
  35. for(i=0;i<25;i++)
  36. QMessageBox::information(NULL, "Title", QString::number(srcFeature[0][i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  37. */
  38. float ouDistanceValue[10]={0}; //存储当前测试图像与已知的十个数字图像之间的欧氏距离
  39. for(i=0;i<10;i++)
  40. {
  41. ouDistanceValue[i]=ouDistance(testFeature,srcFeature[i]);
  42. // ouDistanceValue[i]=i;
  43. }
  44. //总是不能得到结果,测试下ouDistance有没有问题。
  45. /*
  46. for(i=0;i<10;i++)
  47. QMessageBox::information(NULL, "Title", QString::number(ouDistanceValue[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  48. */
  49. mini=0;
  50. min=ouDistanceValue[0]; //给min赋个初始值,假设与数字0的距离最小。
  51. for(i=0;i<10;i++)
  52. {
  53. if(min>ouDistanceValue[i])
  54. {
  55. min=ouDistanceValue[i];
  56. mini=i;
  57. }
  58. }
  59. // QMessageBox::information(NULL, "Title", QString::number(mini), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  60. //上述语句测试一下mini是否能够取得正确的值
  61. //将与当前测试图像匹配的图像显示在label2内
  62. cv::cvtColor(srcImage[mini],srcImage[mini],CV_BGR2RGB);
  63. img = QImage((const unsigned char*)(srcImage[mini].data),srcImage[mini].cols,srcImage[mini].rows, srcImage[mini].cols*srcImage[mini].channels(), QImage::Format_RGB888);
  64. ui->label2->clear();
  65. img= img.scaled(ui->label2->width(), ui->label2->height());
  66. ui->label2->setPixmap(QPixmap::fromImage(img));
  67. //将当前图像的匹配结果显示在一个消息框内
  68. QMessageBox::information(NULL, "测试结果", "当前测试图像的识别结果为数字:"+QString::number(mini), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
  69. }

转载地址
https://blog.csdn.net/superdont/article/details/46926235

手写数字识别【QT+OpenCV】相关推荐

  1. python opencv数字识别_基于模板匹配的手写数字识别(python+opencv)

    智能计算课第一周的实验是做基于模板匹配的手写数字识别,光听见就很感兴趣,于是决定认真做做这个实验,本实验基于python3+opencv的python版本,所用到的知识都比较简单,基本上边学边做,技术 ...

  2. 手写数字识别 vs+opencv

    手写体数字的识别原理 手写体数字识别综合了图像处理.模式识别.机器学习等多个领域的知识,是一个跨学科的复杂问题,其识别系统通常由图像预处理.特征提取以及分类识别三部分组成,如图1 所示. 图 1 手写 ...

  3. linux手写数字识别opencv,opencv实现KNN手写数字的识别

    人工智能是当下很热门的话题,手写识别是一个典型的应用.为了进一步了解这个领域,我阅读了大量的论文,并借助opencv完成了对28x28的数字图片(预处理后的二值图像)的识别任务. 预处理一张图片: 首 ...

  4. pyTorch入门(六)——实战Android Minist OpenCV手写数字识别(附源码地址)

    学更好的别人, 做更好的自己. --<微卡智享> 本文长度为4239字,预计阅读12分钟 前言 前面几篇文章实现了pyTorch训练模型,然后在Windows平台用C++ OpenCV D ...

  5. 在OpenCV里使用机器学习库sklearn 实现手写数字识别1

    前面学习过KNN的方式来实现手写数字识别,不过效果一般,那么有没有别的方法来试一试,或许可以改进一点呢.在本文里将要介绍使用SVM和HOG的方式来实现手写数字识别,比如最终结果如下图: 在这个例子里与 ...

  6. 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别(含源码)

    文章目录 前言 一.OpenCV DNN模块 1.OpenCV DNN简介 2.LabVIEW中DNN模块函数 二.TensorFlow pb文件的生成和调用 1.TensorFlow2 Keras模 ...

  7. 深度学习--TensorFlow(项目)Keras手写数字识别

    目录 效果展示 基础理论 1.softmax激活函数 2.神经网络 3.隐藏层及神经元最佳数量 一.数据准备 1.载入数据集 2.数据处理 2-1.归一化 2-2.独热编码 二.神经网络拟合 1.搭建 ...

  8. 使用Caffe进行手写数字识别执行流程解析

    之前在 http://blog.csdn.net/fengbingchun/article/details/50987185 中仿照Caffe中的examples实现对手写数字进行识别,这里详细介绍下 ...

  9. 教程 | 基于LSTM实现手写数字识别

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 基于tensorflow,如何实现一个简单的循环神经网络,完成手写 ...

最新文章

  1. oracle rpad mysql_Oracle生成不重复票号与LPAD,RPAD与NEXTVAL函数解析
  2. ASP.NET Core中GetService()和GetRequiredService()之间的区别
  3. 华为 connect大会2020_英诺森ProcessGo机器人亮相2019华为CONNECT大会
  4. 万字长文深入理解java中的集合-附PDF下载
  5. html文本分类输出,构建中文网页分类器对网页进行文本分类
  6. css宋体代码_html布局中统一设置文字字体样式
  7. 浅谈多重背包及其优化
  8. VS2013在Release情况下使用vector有时候会崩溃的一个可能原因
  9. P2转P3时出现‘utf-8‘ codec can‘t decode byte 0xb3 in position 0: invalid start byte(\x、decode解码)
  10. java中什么泛型_java中的泛型(一)
  11. mysql5.6 in走索引吗_MySQL5.6 单列、多列索引以及IN语句的优化(翻译)
  12. 国内nodejs下载网站
  13. [BZOJ3993]-[SDOI2015]星际战争-二分答案+最大流
  14. Python-Django毕业设计老薛男生服装网(程序+LW)
  15. win7下iTools Android模拟器安装与配置
  16. 每日统计部门人员考勤打卡情况并汇总通知
  17. c语言二分法查找数组元素,c语言二分法如何实现查找数组元素
  18. Leetcode PHP题解--D35 876. Middle of the Linked List
  19. 图神经网络(十五)DROPEDGE: TOWARDS DEEP GRAPH CONVOLU-TIONAL NETWORKS ON NODE CLASSIFICATION
  20. 写高性能JavaScript

热门文章

  1. 使用STM32CubeMX把USB配置成虚拟串口(virtual com port)
  2. postgresql使用过程中字段转换
  3. Mol文件和SDF文件介绍
  4. latex公式中插入空格
  5. ku060 schdoc connect record
  6. 寄存器建立时间与保持时间分析
  7. 步进电机使用总结之噪声、振动的抑制
  8. altium designer PCB 导入ADS EM仿真
  9. edge浏览器什么相当于ie的中低_企鹅智酷:2015年微软Edge和IE浏览器对比研究
  10. Android Studio sdk tools文件夹下文件缺失问题以及解决方法