在OpenCV的安装文件路径/opencv/sources/samples/data/digits.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。
最后是一些个人想法,为什么KNN在手写数字的数据库中表现优异,我觉得主要是因为图像较简单,数字在图像中的位置很规则,都在中间,这两个特点非常利于KNN做距离的计算。
————————————————
版权声明:本文为CSDN博主「chaibubble」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chaipp0607/article/details/77966888

