KNN即K个最近邻,网上有很多关于KNN的文章。我大概总结下核心:假设有A图片,让A与训练样本依次计算相似度(可用欧式距离),挑选出K个与A图片相似度最大的图片,这K个图片中,哪种类型最多那么定义A图片也属于该类型。
首先,需要有数字的训练样本
https://download.csdn.net/download/weixin_41721222/10784418
KNN的数字识别代码与基于SVM的数字识别大体一致
https://blog.csdn.net/weixin_41721222/article/details/84953788
核心思路:
1:获取一张训练图片后会将图片特征写入到容器中,紧接着会将标签写入另一个容器中,这样就保证了特征和标签是一一对应的关系。
2:特征可用LBP,HOG等提取,但是我们这里主要说KNN训练过程,所以用最简单的方法,即把训练图片的全部像素序列成一行像素作为特征,用reshape(1,1)。
3:图片特征数据得转换成CV_32FC1的数据格式。
4:所有训练样本与测试样本的尺寸都应该一样(这里我都选择20*20)。
下面代码是opencv3和C++
可以根据自己需要修改训练样本类别,数目,尺寸。oss的训练样本路径,src的检测图片路径。
/// 字符识别——基于模版匹配.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
using namespace ml;
ostringstream oss;
int num = -1;
Mat dealimage;
Mat src;
int k = 0;
Mat yangben_gray;
Mat yangben_thresh;
int main()
{
===============================读取训练数据===============================
const int classsum = 10;//图片共有10类
const int imagesSum =500;//每类有500张图片
const int imageRows = 20;//图片尺寸
const int imageCols = 20;
//训练数据,每一行一个训练图片
Mat trainingData;
//训练样本标签
Mat labels;
//最终的训练样本标签
Mat clas;
//最终的训练数据
Mat traindata;
//从指定文件夹下提取图片//
for (int p = 0; p < classsum; p++)
{
oss << "C:/Users/zhang/Desktop/opencv——实例/小案例/车牌检测/基于adaboost机器学习/模版匹配样本/";
num += 1;//num从0到9
int label = num;
oss << num << "/*.jpg";//图片名字后缀,oss可以结合数字与字符串
string pattern = oss.str();//oss.str()输出oss字符串,并且赋给pattern
oss.str("");//每次循环后把oss字符串清空
vector<Mat> input_images;
vector<String> input_images_name;
glob(pattern, input_images_name, false);
//为false时,仅仅遍历指定文件夹内符合模式的文件,当为true时,会同时遍历指定文件夹的子文件夹
//此时input_images_name存放符合条件的图片地址
int all_num = input_images_name.size();//文件下总共有几个图片
//cout << num << ":总共有" << all_num << "个图片待测试" << endl;
for (int i = 0; i < imagesSum; i++)
{
cvtColor(imread(input_images_name[i]), yangben_gray, COLOR_BGR2GRAY);
threshold(yangben_gray, yangben_thresh, 0, 255, THRESH_OTSU);
input_images.push_back(yangben_thresh);
//循环读取每张图片并且依次放在vector<Mat> input_images内
dealimage = input_images[i];
//注意:我们简单粗暴将整个图的所有像素作为了特征,因为我们关注更多的是整个的训练过程
//,所以选择了最简单的方式完成特征提取工作,除此中外,
//特征提取的方式有很多,比如LBP,HOG等等
//我们利用reshape()函数完成特征提取,
//reshape(1, 1)的结果就是原图像对应的矩阵将被拉伸成一个一行的向量,作为特征向量。
dealimage = dealimage.reshape(1, 1);//图片序列化
trainingData.push_back(dealimage);//序列化后的图片依次存入
labels.push_back(label);//把每个图片对应的标签依次存入
}
}
//图片数据和标签转变下
Mat(trainingData).copyTo(traindata);//复制
traindata.convertTo(traindata, CV_32FC1);//更改图片数据的类型,必要,不然会出错
Mat(labels).copyTo(clas);//复制
===============================创建KNN模型===============================
Ptr<KNearest>knn = KNearest::create();
knn->setDefaultK(10);//k个最近领
knn->setIsClassifier(true);//true为分类,false为回归
//训练数据和标签的结合
Ptr<TrainData>trainData = TrainData::create(traindata, ROW_SAMPLE, clas);
//训练
knn->train(trainData);
//model->save("E:/image/KNearestModel.xml");
===============================预测部分===============================
//预测分类
Mat src = imread("C:/Users/zhang/Desktop/opencv——实例/小案例/车牌检测/基于adaboost机器学习/检测到的车牌字符/4.jpg");
cvtColor(src, src, COLOR_BGR2GRAY);
threshold(src, src, 0, 255, CV_THRESH_OTSU);
imshow("原图像", src);
Mat input;
src = src.reshape(1, 1);//输入图片序列化
input.push_back(src);
input.convertTo(input, CV_32FC1);//更改图片数据的类型,必要,不然会出错
float r = knn->predict(input); //对所有行进行预测
cout << r << endl;
waitKey(0);
return 0;
}
结果: