OpenCV 3.3中给出了K-最近邻(KNN)算法的实现,即cv::ml::Knearest类,此类的声明在include/opecv2/ml.hpp文件中,实现在modules/ml/src/knearest.cpp文件中。其中:
(1)、cv::ml::Knearest类:继承自cv::ml::StateModel,而cv::ml::StateModel又继承自cv::Algorithm;
(2)、create函数:为static,new一个KNearestImpl用来创建一个KNearest对象;
(3)、setDefaultK/getDefaultK函数:在预测时,设置/获取的K值;
(4)、setIsClassifier/getIsClassifier函数:设置/获取应用KNN是进行分类还是回归;
(5)、setEmax/getEmax函数:在使用KDTree算法时,设置/获取Emax参数值;
(6)、setAlgorithmType/getAlgorithmType函数:设置/获取KNN算法类型,目前支持两种:brute_force和KDTree;
(7)、findNearest函数:根据输入预测分类/回归结果。
关于KNN算法介绍可以参考: http://blog.csdn.net/fengbingchun/article/details/78464169
以下是从数据集MNIST中提取的40幅图像,0,1,2,3四类各20张,每类的前10幅来自于训练样本,用于训练,后10幅来自测试样本,用于测试,如下图:
关于MNIST的介绍可以参考: http://blog.csdn.net/fengbingchun/article/details/49611549
测试代码如下:
#include "opencv.hpp"
#include
#include
#include
#include
#include
#include
#include "common.hpp"
/////////////////////////////////////////// K-Nearest Neighbor(KNN) //////////////////////////////////////
int test_opencv_knn_predict()
{
const int K{ 3 };
cv::Ptr knn = cv::ml::KNearest::create();
knn->setDefaultK(K);
knn->setIsClassifier(true);
knn->setAlgorithmType(cv::ml::KNearest::BRUTE_FORCE);
const std::string image_path{"E:/GitCode/NN_Test/data/images/digit/handwriting_0_and_1/"};
cv::Mat tmp = cv::imread(image_path + "0_1.jpg", 0);
const int train_samples_number{ 40 }, predict_samples_number{ 40 };
const int every_class_number{ 10 };
cv::Mat train_data(train_samples_number, tmp.rows * tmp.cols, CV_32FC1);
cv::Mat train_labels(train_samples_number, 1, CV_32FC1);
float* p = (float*)train_labels.data;
for (int i = 0; i < 4; ++i) {
std::for_each(p + i * every_class_number, p + (i + 1)*every_class_number, [i](float& v){v = (float)i; });
}
// train data
for (int i = 0; i < 4; ++i) {
static const std::vector digit{ "0_", "1_", "2_", "3_" };
static const std::string suffix{ ".jpg" };
for (int j = 1; j <= every_class_number; ++j) {
std::string image_name = image_path + digit[i] + std::to_string(j) + suffix;
cv::Mat image = cv::imread(image_name, 0);
CHECK(!image.empty() && image.isContinuous());
image.convertTo(image, CV_32FC1);
image = image.reshape(0, 1);
tmp = train_data.rowRange(i * every_class_number + j - 1, i * every_class_number + j);
image.copyTo(tmp);
}
}
knn->train(train_data, cv::ml::ROW_SAMPLE, train_labels);
// predict datta
cv::Mat predict_data(predict_samples_number, tmp.rows * tmp.cols, CV_32FC1);
for (int i = 0; i < 4; ++i) {
static const std::vector digit{ "0_", "1_", "2_", "3_" };
static const std::string suffix{ ".jpg" };
for (int j = 11; j <= every_class_number+10; ++j) {
std::string image_name = image_path + digit[i] + std::to_string(j) + suffix;
cv::Mat image = cv::imread(image_name, 0);
CHECK(!image.empty() && image.isContinuous());
image.convertTo(image, CV_32FC1);
image = image.reshape(0, 1);
tmp = predict_data.rowRange(i * every_class_number + j - 10 - 1, i * every_class_number + j - 10);
image.copyTo(tmp);
}
}
cv::Mat result;
knn->findNearest(predict_data, K, result);
CHECK(result.rows == predict_samples_number);
cv::Mat predict_labels(predict_samples_number, 1, CV_32FC1);
p = (float*)predict_labels.data;
for (int i = 0; i < 4; ++i) {
std::for_each(p + i * every_class_number, p + (i + 1)*every_class_number, [i](float& v){v = (float)i; });
}
int count{ 0 };
for (int i = 0; i < predict_samples_number; ++i) {
float value1 = ((float*)predict_labels.data)[i];
float value2 = ((float*)result.data)[i];
fprintf(stdout, "expected value: %f, actual value: %f\n", value1, value2);
if (int(value1) == int(value2)) ++count;
}
fprintf(stdout, "when K = %d, accuracy: %f\n", K, count * 1.f / predict_samples_number);
return 0;
}
测试结果如下:
GitHub: https://github.com/fengbingchun/NN_Test