实验要求
对于测试集中的每一幅图像,计算其与训练集的所有图像的距离,并挑选出前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;
}