视觉进阶: 学习KNN(二)KNN算法手写数字识别的OpenCV实现


在OpenCV的安装文件路径/opencv/sources/samples/data/digits.png下,有这样一张图:

20170913153334658.png

图片大小为1000*2000,有0-9的10个数字,每5行为一个数字,总共50行,共有5000个手写数字,每个数字块大小为20*20。
为了后续方便处理,我们先写一段小程序把这5000个图截取出来:

#include <opencv2/opencv.hpp>
#include <iostream>

 using namespace std;
 using namespace cv;

 int main()
 {
     char ad[128]={0};
     int  filename = 0,filenum=0; 
     Mat img = imread("digits.png");
     Mat gray;
     cvtColor(img, gray, CV_BGR2GRAY);
     int b = 20;
     int m = gray.rows / b;   //原图为1000*2000
     int n = gray.cols / b;   //裁剪为5000个20*20的小图块

     for (int i = 0; i < m; i++)
     {
         int offsetRow = i*b;  //行上的偏移量
         if(i%5==0&&i!=0)
         {
             filename++;
             filenum=0;
         }
         for (int j = 0; j < n; j++)
         {
                int offsetCol = j*b; //列上的偏移量
             sprintf_s(ad, "D:\\data\\%d\\%d.jpg",filename,filenum++);
             //截取20*20的小块
             Mat tmp;
             gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
             imwrite(ad,tmp);
         }
     }
     return 0;
 }

截取之后每个以数字命名的文件夹内就都有500张图片了,需要注意的是OpenCV的imwrite是没有自动创建文件夹功能的,所以路径应该提前存在。

OpenCV提供的KNN算法构造函数:

C++: CvKNearest::CvKNearest()  
C++: CvKNearest::CvKNearest(const Mat& trainData, const Mat& responses, const Mat& sam-  
pleIdx=Mat(), bool isRegression=false, int max_k=32 )

训练函数为:

C++: bool CvKNearest::train(  
    const Mat& trainData, //训练数据  
    const Mat& responses,//对应的响应值  
    const Mat& sampleIdx=Mat(),//样本索引  
    bool isRegression=false,//是否是回归,否则是分类问题  
    int maxK=32, //最大K值  
    bool updateBase=false//是否更新数据,是,则maxK需要小于原数据大小 )

查找函数为:

C++: float CvKNearest::find_nearest(  
const Mat& samples,//按行存储的测试数据  
 int k, //K 值  
Mat* results=0,//预测结果  
const float** neighbors=0, //近邻指针向量  
Mat* neighborResponses=0, //近邻值  
Mat* dist=0 //距离矩阵) const  

C++: float CvKNearest::find_nearest(  
const Mat& samples,  
int k,  
Mat& results,  
Mat& neighborResponses,  
Mat& dists) const


但是由于KNN的特点,其实并没有train的功能,所以train只是相当于存储下来训练样本,而且会在执行构造函数中执行训练过程。

在之前,我们已经把5000张图分别放进了10个文件夹里了,现在我们把其中的每个类别中前400张拿出来做训练数据,其余的测试,代码如下:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>  
using namespace std;
using namespace cv;

char ad[128]={0};

int main()
{
    Mat traindata ,trainlabel;
    int k=5,testnum=0,truenum=0;
    //读取训练数据 4000张
    for (int i = 0; i < 10; i++)
     {
         for (int j =0;j<400;j++)
         {
              sprintf_s(ad, "D:\\data\\%d\\%d.jpg",i,j);
              Mat srcimage = imread(ad);
              srcimage = srcimage.reshape(1,1);
              traindata.push_back(srcimage);
              trainlabel.push_back(i);
         }
     }
       traindata.convertTo(traindata,CV_32F);
      CvKNearest knn( traindata, trainlabel, cv::Mat(), false, k );  
      cv::Mat nearests( 1, k, CV_32F);  
      //读取测试数据  1000张
      for (int i = 0; i < 10; i++)
      {
          for (int j =400;j<500;j++)
          {
              testnum++;
              sprintf_s(ad, "D:\\data\\%d\\%d.jpg",i,j);
              Mat testdata = imread(ad);
              testdata = testdata.reshape(1,1);
              testdata.convertTo(testdata,CV_32F);
              int  response = knn.find_nearest(testdata,k,0,0,&nearests,0); 
              if (response==i)
              {
                  truenum++;
              }
          }
      }
      cout<<"测试总数"<<testnum<<endl;
      cout<<"正确分类数"<<truenum<<endl;
      cout<<"准确率:"<<(float)truenum/testnum*100<<"%"<<endl;
      return 0;
}

在上述代码中,用的特征就是每个位置的像素值,一个样本拉成一维后的列是20*20*3=1200。

image.png


最后是一些个人想法,为什么KNN在手写数字的数据库中表现优异,我觉得主要是因为图像较简单,数字在图像中的位置很规则,都在中间,这两个特点非常利于KNN做距离的计算。



————————————————

版权声明:本文为CSDN博主「chaibubble」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/chaipp0607/article/details/77966888



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864