手写数字识别【QT+OpenCV】
手写数字识别【QT+OpenCV】
【说明】
手写数字识别的实现方式很多。
本文尽量将其简化,以让大家能够快速了解怎样实现一个动起来的系统。
【截图】
【思路】
1.特征提取
将图像划分为5*5大小的区域,然后计算该区域内黑色(或白色)的像素点所占比例。
将需要测试的图像、用来分类的图像都进行特征提取。
2.计算当前的测试图像与用来分类的图像之间的欧氏距离。
3.找出欧式距离最小的值即为与当前测试图像最匹配的图像,即将该图像所代表的数字作为当前测试图像的结果。
4.为了处理上的方便,做了简化处理如下:
4.1仅仅选用10幅用来分类的图像。
在实际应用中,10幅图像远远是不够的。但是为了简化程序,这里仅仅选用10幅图像,即数字0~9每个数字仅仅选用了一个特征。
在实际系统内,当选用的特征图像越多时,系统的准确度越高。
4.2采用了最近邻。即选用欧式距离最小的图像作为当前测试图像的结果。
在实际系统中,往往需要采用K近邻,即选择最小的K个欧式距离,判断他们分别属于哪个类从而决定当前数字结果。
因为简化的原因,数字识别的正确率不是很高。但是基本能够满足学习的需求。
【部分代码】
该系统采用QT+OpenCV开发完成,部分代码如下:
【主类内变量及槽函数】
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- cv::Mat testImage,srcImage[10],tempImage;//testImage值要测试的数字图像,srcImage指已经有的用来实现分类的数字图像
- QImage img;
- float testFeature[25];//该数组用来存储待检测数字图像的特征值。
- float srcFeature[10][25];//用来存储原始数字图像的特征值。只有10个数字0~9的图像
- void getFeature(cv::Mat m,float a[25]);//这里定义一个获取图像特征的函数。
- float ouDistance(float a[25],float b[25]);
- float oDistance(float a[25],float b[25]);
- private slots:
- void on_openLenaJpg_triggered();
- void on_exitSystem_triggered();
- void on_openCustomeFile_triggered();
- void on_restoreFile_triggered();
- void on_copyright_triggered();
- void on_about_triggered();
- void on_showImage_triggered();
- void on_showMessage_triggered();
- void on_ImageAndMessage_triggered();
【打开自定义路径待测图像】
- void MainWindow::on_openCustomeFile_triggered()
- {
- QString filename = QFileDialog::getOpenFileName(this,tr("Open Image"),"",tr("Image File(*.bmp *.jpg *.jpeg *.png)"));
- QTextCodec *code = QTextCodec::codecForName("gb18030");
- std::string name = code->fromUnicode(filename).data();
- testImage = cv::imread(name);
- if(!testImage.data)
- {
- QMessageBox msgBox;
- msgBox.setText(tr("未找到数据"));
- msgBox.exec();
- }
- else
- {
- cv::cvtColor(testImage,testImage,CV_BGR2RGB);
- img = QImage((const unsigned char*)(testImage.data),testImage.cols,testImage.rows, testImage.cols*testImage.channels(), QImage::Format_RGB888);
- ui->label1->clear();
- img= img.scaled(ui->label1->width(), ui->label1->height());
- ui->label1->setPixmap(QPixmap::fromImage(img));
- //ui->processPushButton->setEnabled(true);
- // ui->label1->resize(ui->label1->pixmap()->size());//设置当前标签为图像大小
- // ui->label1->resize(img.width(),img.height());
- //this->setWidget(label1);
- }
- }
【图像特征提取:完成图像5*5=25个特征,每个特征表示该子区域内白像素个数】
- void MainWindow::getFeature(cv::Mat m,float a[25])
- {
- int M,N; //用来存储图像m的宽高
- int i,j;
- M=m.cols;
- N=m.rows;
- for(i=0;i<25;i++)
- a[i]=0;
- // QMessageBox::information(NULL, "Title", QString::number(m.at<uchar>(188,88)), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- for(i=0;i<M;i++)
- for(j=0;j<N;j++)
- if(m.at<uchar>(i,j)==255)
- {
- // a[i/5*5+j/5]++; //这里计算错误,不能放入对应的特征值内
- // QMessageBox::information(NULL, "Title", QString::number(5), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- // a[M/i*5+N/j]++;
- //a[M/(i+1)*5+N/(j+1)]++;
- a[i/(M/5)*5+j/(N/5)]++;
- // QMessageBox::information(NULL, "Title","add", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- }
- for(i=0;i<25;i++)
- {
- // QMessageBox::information(NULL, "Title", QString::number(a[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- a[i]=a[i]/((M/5)*(N/5));
- // QMessageBox::information(NULL, "Title", QString::number(a[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- // QMessageBox::information(NULL, "Title", QString::number(5), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- }
- // QMessageBox::information(NULL, "Title", QString::number(a[5]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- }
【欧氏距离计算,第二个函数是测试用的】
- float MainWindow::ouDistance(float a[25],float b[25]) //这个函数居然忘记写MainWindow的类关系,调试好久,瀑布汗!
- {
- int i;
- float distance=0;//开始忘记置零,出错呀!!!
- for(i=0;i<25;i++)
- distance+=(a[i]-b[i])*(a[i]-b[i]);
- distance=sqrt(distance);
- return distance;
- }
- float MainWindow::oDistance(float a[25],float b[25]) //这个函数是ouDistance出问题时测试的,并没有用
- {
- int i;
- float distance=0; //开始忘记置零,出错呀!!!
- //,为了测试ouDistance函数,重写了oDistance发现问题,结果再次出现问题一直却一直在此函数修改。而调用函数用的还是ouDistance
- for(i=0;i<25;i++)
- distance+=(a[i]-b[i])*(a[i]-b[i]);
- distance=sqrt(distance);
- return distance;
- }
【说明】
- void MainWindow::on_copyright_triggered()
- {
- QMessageBox::information(this,"版权",tr("本软件版权所有者为:天津职业技术师范大学。如果使用,请联系:lilizong#gmail"));
- }
- void MainWindow::on_about_triggered()
- {
- QMessageBox::information(this,"关于",tr("本软件当前版本为1.0,由李立宗等人开发。如果有问题,欢迎联系:lilizong#gmail"));
- return;
- }
【显示测试结果:一个图像、一个信息框】
- void MainWindow::on_ImageAndMessage_triggered()
- {
- int i;
- float min; //用来存储最小的欧式距离
- int mini; //用来存储最小的欧氏距离的数字号。
- getFeature(testImage,testFeature); //获取测试图像的特征值,并将其放到testFeature数组内。
- // QMessageBox::information(NULL, "Title", QString::number(testFeature[6]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- //测试当前的testFeature是否正常
- /*
- for(i=0;i<25;i++)
- QMessageBox::information(NULL, "Title", QString::number(testFeature[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- */
- // QMessageBox::information(NULL, "Title", QString::number(testImage.rows), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- for(i=0;i<10;i++)
- {
- QString filePath,fileName,allName;
- filePath="image\\stand\\"; //当前图像目录
- fileName=".bmp"; //当前图像的扩展名
- allName=filePath+"\\"+QString::number(i)+fileName; //i是文件名,使用QString::number(i)完成将其转换为QString类型,当前为数值型
- String s=allName.toStdString(); //转换为标准的字符串型,imread不识别QString类型
- srcImage[i] = cv::imread(s);
- }
- //以下部分用于测试上述代码是否能够将srcImage的值获取到。
- /*
- cv::cvtColor(srcImage[3],srcImage[3],CV_BGR2RGB);
- img = QImage((const unsigned char*)(srcImage[3].data),srcImage[1].cols,srcImage[1].rows, srcImage[1].cols*srcImage[1].channels(), QImage::Format_RGB888);
- ui->label1->clear();
- img= img.scaled(ui->label1->width(), ui->label1->height());
- ui->label1->setPixmap(QPixmap::fromImage(img));
- */
- // 获取原始数字图像的特征值。
- for(i=0;i<10;i++)
- getFeature(srcImage[i],srcFeature[i]);
- /*
- for(i=0;i<25;i++)
- QMessageBox::information(NULL, "Title", QString::number(srcFeature[0][i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- */
- float ouDistanceValue[10]={0}; //存储当前测试图像与已知的十个数字图像之间的欧氏距离
- for(i=0;i<10;i++)
- {
- ouDistanceValue[i]=ouDistance(testFeature,srcFeature[i]);
- // ouDistanceValue[i]=i;
- }
- //总是不能得到结果,测试下ouDistance有没有问题。
- /*
- for(i=0;i<10;i++)
- QMessageBox::information(NULL, "Title", QString::number(ouDistanceValue[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- */
- mini=0;
- min=ouDistanceValue[0]; //给min赋个初始值,假设与数字0的距离最小。
- for(i=0;i<10;i++)
- {
- if(min>ouDistanceValue[i])
- {
- min=ouDistanceValue[i];
- mini=i;
- }
- }
- // QMessageBox::information(NULL, "Title", QString::number(mini), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- //上述语句测试一下mini是否能够取得正确的值
- //将与当前测试图像匹配的图像显示在label2内
- cv::cvtColor(srcImage[mini],srcImage[mini],CV_BGR2RGB);
- img = QImage((const unsigned char*)(srcImage[mini].data),srcImage[mini].cols,srcImage[mini].rows, srcImage[mini].cols*srcImage[mini].channels(), QImage::Format_RGB888);
- ui->label2->clear();
- img= img.scaled(ui->label2->width(), ui->label2->height());
- ui->label2->setPixmap(QPixmap::fromImage(img));
- //将当前图像的匹配结果显示在一个消息框内
- QMessageBox::information(NULL, "测试结果", "当前测试图像的识别结果为数字:"+QString::number(mini), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
- }
转载地址
https://blog.csdn.net/superdont/article/details/46926235
手写数字识别【QT+OpenCV】相关推荐
- python opencv数字识别_基于模板匹配的手写数字识别(python+opencv)
智能计算课第一周的实验是做基于模板匹配的手写数字识别,光听见就很感兴趣,于是决定认真做做这个实验,本实验基于python3+opencv的python版本,所用到的知识都比较简单,基本上边学边做,技术 ...
- 手写数字识别 vs+opencv
手写体数字的识别原理 手写体数字识别综合了图像处理.模式识别.机器学习等多个领域的知识,是一个跨学科的复杂问题,其识别系统通常由图像预处理.特征提取以及分类识别三部分组成,如图1 所示. 图 1 手写 ...
- linux手写数字识别opencv,opencv实现KNN手写数字的识别
人工智能是当下很热门的话题,手写识别是一个典型的应用.为了进一步了解这个领域,我阅读了大量的论文,并借助opencv完成了对28x28的数字图片(预处理后的二值图像)的识别任务. 预处理一张图片: 首 ...
- pyTorch入门(六)——实战Android Minist OpenCV手写数字识别(附源码地址)
学更好的别人, 做更好的自己. --<微卡智享> 本文长度为4239字,预计阅读12分钟 前言 前面几篇文章实现了pyTorch训练模型,然后在Windows平台用C++ OpenCV D ...
- 在OpenCV里使用机器学习库sklearn 实现手写数字识别1
前面学习过KNN的方式来实现手写数字识别,不过效果一般,那么有没有别的方法来试一试,或许可以改进一点呢.在本文里将要介绍使用SVM和HOG的方式来实现手写数字识别,比如最终结果如下图: 在这个例子里与 ...
- 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别(含源码)
文章目录 前言 一.OpenCV DNN模块 1.OpenCV DNN简介 2.LabVIEW中DNN模块函数 二.TensorFlow pb文件的生成和调用 1.TensorFlow2 Keras模 ...
- 深度学习--TensorFlow(项目)Keras手写数字识别
目录 效果展示 基础理论 1.softmax激活函数 2.神经网络 3.隐藏层及神经元最佳数量 一.数据准备 1.载入数据集 2.数据处理 2-1.归一化 2-2.独热编码 二.神经网络拟合 1.搭建 ...
- 使用Caffe进行手写数字识别执行流程解析
之前在 http://blog.csdn.net/fengbingchun/article/details/50987185 中仿照Caffe中的examples实现对手写数字进行识别,这里详细介绍下 ...
- 教程 | 基于LSTM实现手写数字识别
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 基于tensorflow,如何实现一个简单的循环神经网络,完成手写 ...
最新文章
- oracle rpad mysql_Oracle生成不重复票号与LPAD,RPAD与NEXTVAL函数解析
- ASP.NET Core中GetService()和GetRequiredService()之间的区别
- 华为 connect大会2020_英诺森ProcessGo机器人亮相2019华为CONNECT大会
- 万字长文深入理解java中的集合-附PDF下载
- html文本分类输出,构建中文网页分类器对网页进行文本分类
- css宋体代码_html布局中统一设置文字字体样式
- 浅谈多重背包及其优化
- VS2013在Release情况下使用vector有时候会崩溃的一个可能原因
- P2转P3时出现‘utf-8‘ codec can‘t decode byte 0xb3 in position 0: invalid start byte(\x、decode解码)
- java中什么泛型_java中的泛型(一)
- mysql5.6 in走索引吗_MySQL5.6 单列、多列索引以及IN语句的优化(翻译)
- 国内nodejs下载网站
- [BZOJ3993]-[SDOI2015]星际战争-二分答案+最大流
- Python-Django毕业设计老薛男生服装网(程序+LW)
- win7下iTools Android模拟器安装与配置
- 每日统计部门人员考勤打卡情况并汇总通知
- c语言二分法查找数组元素,c语言二分法如何实现查找数组元素
- Leetcode PHP题解--D35 876. Middle of the Linked List
- 图神经网络(十五)DROPEDGE: TOWARDS DEEP GRAPH CONVOLU-TIONAL NETWORKS ON NODE CLASSIFICATION
- 写高性能JavaScript