基于KNN的图像分类

实验要求

对于测试集中的每一幅图像,计算其与训练集的所有图像的距离,并挑选出前K的最近的图像,根据他们的类别在判断测试集中的每一幅图像的类别。最后输出这200幅图像的准确率。

实验过程及结果

我做实验的时候是将car和bird分开读取和分开计算,所以下面的步骤中都是car和bird分开的操作的,我只截取一个类型的(car或者bird)。

因为每个图片的大小具体不太一样,我就取了读取一个图片1000个字节进行计算

1.需要将图片用二进制读入

查看jpg文件的格式,可以看到26FH开始才是图片的信息,所以我们判断的时候也从26FH开始计算。

image.png

Knn的值我们采取的是欧式距离,关键代码:

image.png

将每个训练集的内容以二进制的形式读到temp中。

2.再对训练集中的每张图片读入:

image.png

3.计算欧式距离:

image.png

4.将计算的欧式距离的k最小的值以及判断的类型存储

image.png

5.最后遍历判断的类型与真是图像的类型并统计:

image.png

6.最后计算准确率:

image.png

K=1

image.png

K=2


image.png

K=3

image.png

K=4

image.png

K=5

image.png

K=6

image.png

K=7

image.png

K=8

image.png

K=9

image.png

绘制成曲线图:

image.png

实验总结**

可以看到,随着k的增大,bird的识别率逐渐增高,car的却逐渐减小,整体趋于稳定,我认为造成这样的原因的可能是设置的读取1000个字节可能对car不利。但是通过010edit来看,car图片的大小整体比bird更大,所以1000个字节更接近car的图片,但奇怪的是car的识别率却低。这一部分还需要再考虑考虑。

knn.h代码:

#pragma once
#include <string>
using namespace std;

class knnmodel
{
public:
    knnmodel();
    ~knnmodel();
    int InitTypeK(char _type, int _k);
    int readtestjpg(string filename);//读入一个测试的图片
    char knn();             //计算knn,并且返回判断的类型
private:
    char type;      //图片本身的类型,car 或者 bird
    int k;              //knn中的k,K个最相近的图像
    int size= 1000;         //图片的大小
    char* pbuf;         //存储图片的二进制信息
};

zjx_KNN.cpp代码:

#include "knn.h"
#include <fstream>
#include<iostream>
#include <windows.h>

knnmodel::knnmodel()
{
    /*this->type = _type;
    this->k = _k;*/
}
knnmodel::~knnmodel()
{

}
int knnmodel::InitTypeK(char _type,int _k)
{
    this->type = _type;
    this->k = _k;
    return 0;
}
int knnmodel::readtestjpg(string filename)
{
    ifstream inFile(filename, ios::in | ios::binary);
    if (!inFile.is_open()) {
        cout << "open error" << endl;
        return 1;
    }
    pbuf = new char[size];
    inFile.read(pbuf, size);
    inFile.close();
    return 0;
    
}
char knnmodel::knn() 
{
    char* temp = new char[size];
    char* temp1 = new char[size];
    double* knn_num = new double[k]();//存储前k个最大knn值
    char* KnnType = new char[k];//存储前k个最大knn值的类型
    double KnnSum = 0;      //计算每次knn的值
    string CarPicture = "D:\\dataset\\traincar\\(";
    string BirdPicture = "D:\\dataset\\trainbird\\(";
    for (int i = 1; i <= 1000; i++)
    {
        string CarName = CarPicture + to_string(i) + ").jpg";//每次需要读取的样本集的名字
        ifstream inFile(CarName, ios::in | ios::binary);
        if (!inFile.is_open()) {
            cout << "open error" << endl;
            return 0;
        }
        inFile.read(temp, size);
        inFile.close();
        KnnSum = 0;
        for (int j = 623; j < size; j++)
        {
            KnnSum += (temp[j]-pbuf[j])*(temp[j] - pbuf[j]);
        }
        for (int ki = 0; ki < k; ki++)
        {
            if (knn_num[ki] == 0)
            {
                knn_num[ki] = KnnSum;
                KnnType[ki] = 'c';
                break;
            }
            else if (KnnSum < knn_num[ki])
            {
                knn_num[ki] = KnnSum;
                KnnType[ki] = 'c';
                break;
            }
        }
    }
    for (int i = 1; i <= 1000; i++)
    {
        string BirdName = BirdPicture + to_string(i) + ").jpg";//每次需要读取的样本集的名字
        ifstream inFile(BirdName, ios::in | ios::binary);
        if (!inFile.is_open()) {
            cout << "open error" << endl;
            return 0;
        }
        inFile.read(temp1, size);
        inFile.close();
        KnnSum = 0;
        for (int j = 623; j < size; j++)
        {
            KnnSum += pow(temp1[j] - pbuf[j], 2);
        }
        
        for (int ki = 0; ki < k; ki++)
        {
            if (knn_num[ki] == 0)
            {
                knn_num[ki] = KnnSum;
                KnnType[ki] = 'b';
                break;
            }
            else if (KnnSum < knn_num[ki])
            {
                knn_num[ki] = KnnSum;
                KnnType[ki] = 'b';
                break;
            }
            
        }
    }
    cout << "*********************************************************************************************" << endl;
    int BirdNum = 0,CarNum=0;
    for (int i = 0; i < k; i++)
    {
        cout << "KNN的计算值:"<<knn_num[i] << "\t" <<"判断的类型:"<< KnnType[i] << endl;
    }
    //计算判断为c的数量和判断为b的数量
    for (int i = 0; i < k; i++)
    {
        if (KnnType[i] == 'c')
        {
            CarNum++;
        }
        else if (KnnType[i] == 'b')
        {
            BirdNum++;
        }
    }
    if (CarNum >= BirdNum)
        return 'c';
    else
        return 'b';
}

main.cpp

#include<iostream>
#include "knn.h"
#define TOTALNUM 200
int main()
{
    int CorrectBird = 0;
    int CorrectCar = 0;
    knnmodel* testbird = new knnmodel[101];
    knnmodel* testcar = new knnmodel[101];
    string TestBirdFile = "D:\\dataset\\testbird\\(";
    string TestCarFile = "D:\\dataset\\testcar\\ (";
    char* BirdResult = new char[101];
    char* CarResult = new char[101];
    //计算bird图像的判断结果
    for (int i = 1; i <= 100; i++)
    {
        testbird[i].InitTypeK('b', 9);
        string RealTestName = TestBirdFile + to_string(i) + ").jpg";
        testbird[i].readtestjpg(RealTestName);
        BirdResult[i] = testbird[i].knn();
        cout << "第" << i << "张bird图片判断为" << BirdResult[i] << endl;
    }
    //计算bird图像的判断结果
    for (int i = 1; i <= 100; i++)
    {
        testcar[i].InitTypeK('b', 9);
        string RealTestName = TestCarFile + to_string(i) + ").jpg";
        testcar[i].readtestjpg(RealTestName);
        CarResult[i] = testcar[i].knn();
        cout << "第" << i  << "张car图片判断为" << CarResult[i] << endl;
    }
    
    for (int i = 1; i < 101; i++)
    {
        if (BirdResult[i] == 'b')
            CorrectBird++;
        else if (CarResult[i] == 'c')
            CorrectCar++;
    }
    cout << "************************************" << endl;
    cout << endl;
    cout << "Bird图片判断正确的有" << CorrectBird << "幅,Car判断正确的有" << CorrectCar << "幅"<<endl;
    cout << "200幅图片种有" << CorrectBird + CorrectCar << "幅,正确率为" << double(CorrectBird + CorrectCar) / 200 << endl;
    
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。