手写数字识别【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);
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 07:21:20

手写数字识别【QT+OpenCV】的相关文章

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

之前在 http://blog.csdn.net/fengbingchun/article/details/50987185 中仿照Caffe中的examples实现对手写数字进行识别,这里详细介绍下其执行流程并精简了实现代码,使用Caffe对MNIST数据集进行train的文章可以参考  http://blog.csdn.net/fengbingchun/article/details/68065338 : 1.   先注册所有层,执行layer_factory.hpp中类LayerRegis

手把手教你搭建caffe及手写数字识别(全程命令提示、纯小白教程)

手把手教你搭建caffe及手写数字识别 作者:七月在线课程助教团队,骁哲.小蔡.李伟.July时间:二零一六年十一月九日交流:深度学习实战交流Q群 472899334,有问题可以加此群共同交流.另探究实验背后原理,请参看此课程:11月深度学习班. 一.前言 在前面的教程中,我们搭建了tensorflow.torch,教程发布后,大家的问题少了非常多.但另一大框架caffe的问题则也不少,加之caffe也是11月深度学习班要讲的三大框架之一,因此,我们再把caffe的搭建完整走一遍,手把手且全程命

使用AI算法进行手写数字识别

人工智能 ??人工智能(Artificial Intelligence,简称AI)一词最初是在1956年Dartmouth学会上提出的,从那以后,研究者们发展了众多理论和原理,人工智能的概念也随之扩展.由于人工智能的研究是高度技术性和专业的,各分支领域都是深入且各不相通的,因而涉及范围极广 . 人工智能的核心问题包括建构能够跟人类似甚至超越人类的推理.知识.学习.交流.感知.使用工具和操控机械的能力等,当前人工智能已经有了初步成果,甚至在一些影像识别.语言分析.棋类游戏等等单方面的能力达到了超越

C#中调用Matlab人工神经网络算法实现手写数字识别

手写数字识别实现 设计技术参数:通过由数字构成的图像,自动实现几个不同数字的识别,设计识别方法,有较高的识别率 关键字:二值化  投影  矩阵  目标定位  Matlab 手写数字图像识别简介: 手写阿拉伯数字识别是图像内容识别中较为简单的一个应用领域,原因有被识别的模式数较少(只有0到9,10个阿拉伯数字).阿拉伯数字笔画少并且简单等.手写阿拉伯数字的识别采用的方法相对于人脸识别.汉字识别等应用领域来说可以采用更为灵活的方法,例如基于规则的方法.基于有限状态自动机的方法.基于统计的方法和基于神

C++使用matlab卷积神经网络库MatConvNet来进行手写数字识别

环境:WIN10(64 bit)+VS2010(64 bit)+Matlab2015b(64 bit) 关于MatConvNet的介绍参考:http://www.vlfeat.org/matconvnet/ Github下载地址为:https://github.com/vlfeat/matconvnet/ 我们的目的是将MatConvNet自带的手写数字识别DEMO移植到一个简单的WIN32 DEMO中使用,主要过程有以下几个步骤: (1)配置MatConvNet,然后将手写数字识别DEMO编译

keras入门实战:手写数字识别

近些年由于理论知识的硬件的快速发展,使得深度学习达到了空前的火热.深度学习已经在很多方面都成功得到了应用,尤其是在图像识别和分类领域,机器识别图像的能力甚至超过了人类. 本文用深度学习Python库Keras实现深度学习入门教程mnist手写数字识别.mnist手写数字识别是机器学习和深度学习领域的"hello world",MNIST数据集是手写数字的数据集合,训练集规模为60000,测试集为10000. 本文的内容包括: 如何用Keras加载MNIST数据集 对于MNIST问题如何

Python 手写数字识别-knn算法应用

在上一篇博文中,我们对KNN算法思想及流程有了初步的了解,KNN是采用测量不同特征值之间的距离方法进行分类,也就是说对于每个样本数据,需要和训练集中的所有数据进行欧氏距离计算.这里简述KNN算法的特点: 优点:精度高,对异常值不敏感,无数据输入假定 缺点:计算复杂度高,空间复杂度高 适用数据范围:数值型和标称型(具有有穷多个不同值,值之间无序)    knn算法代码: #-*- coding: utf-8 -*- from numpy import * import operatorimport

在Kaggle手写数字数据集上使用Spark MLlib的朴素贝叶斯模型进行手写数字识别

昨天我在Kaggle上下载了一份用于手写数字识别的数据集,想通过最近学习到的一些方法来训练一个模型进行手写数字识别.这些数据集是从28×28像素大小的手写数字灰度图像中得来,其中训练数据第一个元素是具体的手写数字,剩下的784个元素是手写数字灰度图像每个像素的灰度值,范围为[0,255],测试数据则没有训练数据中的第一个元素,只包含784个灰度值.现在我打算使用Spark MLlib中提供的朴素贝叶斯算法来训练模型. 首先来设定Spark上下文的一些参数: val conf = new Spar

Tensorflow实践 mnist手写数字识别

minst数据集                                         tensorflow的文档中就自带了mnist手写数字识别的例子,是一个很经典也比较简单的入门tensorflow的例子,非常值得自己动手亲自实践一下.由于我用的不是tensorflow中自带的mnist数据集,而是从kaggle的网站下载下来的,数据集有些不太一样,所以直接按照tensorflow官方文档上的参数训练的话还是踩了一些坑,特此记录. 首先从kaggle网站下载mnist数据集,一份是