图像辨认之KNN算法的了解与运用
作者头像
  • 白雪
  • 2021-03-08 09:12:09 2

KNN(K近邻算法)是一种经典的机器学习算法,既可用于分类也可用于回归预测。该算法的核心思路是在训练样本中寻找与待分类样本距离最近的K个样本。如果目的是分类,则统计这K个样本中各类别的数量,数量最多的类别即视为待分类样本的类别;如果是回归预测,则计算这K个样本的平均值作为预测值。

KNN算法可以应用于图像识别,因为图像识别本质上也是一种分类任务。本文将首先介绍KNN算法的基本原理,然后讲解如何在OpenCV中使用KNN算法模块,并通过实例展示如何利用KNN算法对手写数字图像进行识别。

KNN算法的主要组成部分

  1. 训练样本:这是预先准备好的数据集,包含所有可能的类别,每个数据都有一个唯一的标签来标识其类别。例如,待分类的图像可能属于鸟、狗、马、飞机或班车等5种类别,每个类别都有相应的图像,并附有标签。

  2. 待分类样本:这是需要分类的新数据,例如一张图像。将这张图像输入KNN算法中,算法会对其进行分类并给出类别标签。

  3. 样本距离:通常,每个样本都被表示为一个向量。衡量向量之间距离的方法有多种,如欧氏距离、余弦距离、汉明距离等。其中,欧氏距离是最常用的一种距离度量方法。

  4. K值:K值决定了在训练样本中寻找与待分类样本距离最近的样本数量。例如,如果K值设为5,那么对于每一个待分类样本,都会找到5个与其距离最近的样本,然后统计这些样本中各类别的数量,数量最多的类别即为待分类样本的类别。K值的选择通常在5到10之间,具体数值需要根据实际应用效果进行调整。

实例说明

假设有一个待分类样本,其可能的类别为矩形、圆形和菱形。设K值为6,从所有训练样本中找到与待分类样本最接近的6个样本,分别是样本1、样本2、样本3、样本4、样本5和样本6。对这6个样本进行分类统计,发现其中有3个矩形、2个菱形和1个圆形。由于矩形数量最多,因此可以判断待分类样本为矩形。

OpenCV中的KNN算法模块

OpenCV 3.4.1已经实现了KNN算法,并将其封装为类。使用时只需调用相关接口并传入参数即可获得分类结果。具体步骤如下:

  1. 创建KNN类并设置参数: cpp const int K = 3; // 设置K值为3 cv::Ptr<cv::ml::KNearest> knn = cv::ml::KNearest::create(); knn->setDefaultK(K); // 设置KNN类的K值 knn->setIsClassifier(true); // 设置KNN用于分类 knn->setAlgorithmType(cv::ml::KNearest::BRUTE_FORCE); // 设置寻找距离最近的K个样本的方式为暴力破解

  2. 输入训练数据及其标签: cpp Mat traindata; Mat trainlabel; traindata.push_back(srcimage); // 将训练图像保存到训练矩阵中 trainlabel.push_back(i); // 将标签保存到标签矩阵中 traindata.convertTo(traindata, CV_32F); // 训练矩阵必须是浮点型数据

  3. 将训练数据和标签输入KNN模块进行训练: cpp knn->train(traindata, cv::ml::ROW_SAMPLE, trainlabel);

  4. 对待分类图像进行分类: cpp Mat result; int response = knn->findNearest(testdata, K, result);

手写数字识别

在OpenCV 3.4.1的samples/data目录中有一张1000x2000的手写数字图像,该图像包含5000个20x20的小图像块,每个小图像块代表一个手写数字。首先编写程序将这张图像分割成5000个20x20的图像块,并将相同的数字保存到同一文件夹中。

cpp void read_digital_img() { 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; // 原图为1000x2000 int n = gray.cols / b; // 裁剪为5000个20x20的小图块 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/%d.jpg", filename, filenum++); Mat tmp; gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp); imwrite(ad, tmp); } } }

运行上述代码后,从0到9的文件夹中分别保存了对应数字的小图像块。接下来,使用每个文件夹下的前400张图像作为训练数据,对KNN模型进行训练,然后使用该模型对每个文件夹下的后100张图像进行分类,并统计分类结果的准确率。

```cpp void KNNtest() { char ad[128] = {0}; int testnum = 0, truenum = 0; const int K = 3; cv::Ptr knn = cv::ml::KNearest::create(); knn->setDefaultK(K); knn->setIsClassifier(true); knn->setAlgorithmType(cv::ml::KNearest::BRUTEFORCE);

Mat traindata, trainlabel;
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 400; j++) {
        sprintf_s(ad, "%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);

knn->train(traindata, cv::ml::ROW_SAMPLE, trainlabel);

for (int i = 0; i < 10; i++) {
    for (int j = 400; j < 500; j++) {
        testnum++;
        sprintf_s(ad, "%d/%d.jpg", i, j);
        Mat testdata = imread(ad);
        testdata = testdata.reshape(1, 1);
        testdata.convertTo(testdata, CV_32F);
        Mat result;
        int response = knn->findNearest(testdata, K, result);
        if (response == i) {
            truenum++;
        }
    }
}
cout << "测试总数: " << testnum << endl;
cout << "正确分类数: " << truenum << endl;
cout << "准确率: " << (float)truenum / testnum * 100 << "%" << endl;

} ```

运行上述代码后,可以看到KNN算法对手写数字图像的识别(分类)准确率较高。这是因为手写数字图像包含的特征相对简单,而KNN算法在处理简单特征时表现良好。然而,对于复杂图像,KNN算法的识别准确率可能会较低。在后续文章中,我们将探讨如何使用KNN算法识别更复杂的图像,并尝试提高识别准确率。

    本文来源:图灵汇
责任编辑: : 白雪
声明:本文系图灵汇原创稿件,版权属图灵汇所有,未经授权不得转载,已经协议授权的媒体下载使用时须注明"稿件来源:图灵汇",违者将依法追究责任。
    分享
辨认算法图像运用了解KNN
    下一篇