KNN对手写体数字图片分类并预测

本文采用Python实现KNN算法对手写体数字图片进行分类并预测结果
参考文章:https://blog.csdn.net/asialee_bird/article/details/81051281
https://blog.csdn.net/zhili8866/article/details/53026269
https://2723364262.iteye.com/blog/2311663

一.文件格式

在这里插入图片描述
在这里插入图片描述

二.思路分析

1.首先将所有图片转化为灰度图,因为转为单通道后第一可以减少计算量,第二python中部分图像处理函数只支持单通道图片输入。
2.原始数据集0-9放在了9个文件夹中,并且命名无序,所以第二步我将所有图片按上图格式重命名并放入一个文件夹中。注意:图像批量重命名的时候要保证数字位数相同。
3.将图像分为训练集和测试集,我的一共400张图片,350张训练,50张测试,分别放入不同的文件夹中。
4.将图像转为数组,代码测试文件内容,判断是否预测成功
注:批量转换灰度图,批量重命名文件,批量移动文件详细代码参考我的这篇博客:图像处理(批量重命名,批量转为灰度图,批量移动,批量转为数组)

三.代码实现

"""
批量将图像转为一维数组存储后,运用knn进行手写数字图像分类识别
"""
import os
import numpy as np
from PIL import Image   # 导入Image模块
from os import listdir
from sklearn.neighbors import KNeighborsClassifier as KNN
# from pylab import *     # 导入savetxt模块


# 以下代码可以读取文件夹下所有文件
# def getAllImages(folder):
#     assert os.path.exists(folder)
#     assert os.path.isdir(folder)
#     imageList = os.listdir(folder)
#     imageList = [os.path.abspath(item) for item in imageList if os.path.isfile(os.path.join(folder, item))]
#     return imageList

# print getAllImages(r"D:\\test")

def get_imlist(path):   # 此函数读取特定文件夹下的bmp格式图像

    return [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.bmp')]


c1 = get_imlist("C:\\Users\\Tony.Hsu\\Desktop\\writingTest\\trainingSetGray")
print(c1)     # 这里以list形式输出bmp格式的所有图像(带路径)
d1 = len(c1)    # 这可以以输出图像个数
print(d1)

dataTrain = np.empty((d1, 20*36))  # 建立d*(20*36)的矩阵,因为图片像素20*36
while d1 > 0:
    img1 = Image.open(c1[d1-1])   # 打开图像
    # img_ndarray=numpy.asarray(img)
    img_ndarray = np.asarray(img1, dtype='float64')/256  # 将图像转化为数组并将像素转化到0-1之间
    dataTrain[d1-1] = np.ndarray.flatten(img_ndarray)    # 将图像的矩阵形式转化为一维数组保存到dataTrain中
    d1 = d1-1
print(dataTrain)

# A = numpy.array(data[0]).reshape(28, 28)   # 将一维数组转化为矩28*28矩阵
# print A
# savetxt('num7.txt',A,fmt="%.0f") #将矩阵保存到txt文件中

c2 = get_imlist("C:\\Users\\Tony.Hsu\\Desktop\\writingTest\\testingSetGray")
print(c2)     # 这里以list形式输出bmp格式的所有图像(带路径)
d2 = len(c2)    # 这可以以输出图像个数
print(d2)


dataTest = np.empty((d2, 20*36))  # 建立d*(20*36)的矩阵
while d2 > 0:
    img2 = Image.open(c2[d2-1])   # 打开图像
    # img_ndarray=numpy.asarray(img)
    img_ndarray = np.asarray(img2, dtype='float64')/256  # 将图像转化为数组并将像素转化到0-1之间
    dataTest[d2-1] = np.ndarray.flatten(img_ndarray)    # 将图像的矩阵形式转化为一维数组保存到dataTest中
    d2 = d2-1
print(dataTest)

"""
该函数处理的是txt形式保存的图像文件,故此处不适用
函数说明:将32x32的二进制图像转换为1x1024向量
def img2vector(filename):
    # 创建1x1024零向量

    returnVect = np.zeros((1, 1024))

    # 打开文件

    fr = open(filename)

    # 按行读取

    for i in range(32):

        # 读一行数据

        lineStr = fr.readline()

        # 每一行的前32个元素依次添加到returnVect中

        for j in range(32):
            returnVect[0, 32 * i + j] = int(lineStr[j])

    # 返回转换后的1x1024向量

    return returnVect
"""


"""

函数说明:手写数字分类测试

"""


def handwritingClassTest():
    # 训练集的Labels

    hwLabels = []

    # 返回trainingSetGray目录下的文件名

    trainingFileList = listdir('C:\\Users\\Tony.Hsu\\Desktop\\writingTest\\trainingSetGray')

    # 返回文件夹下文件的个数

    m = len(trainingFileList)

    # 初始化训练的Mat矩阵,训练集

    trainingMat = np.zeros((m, 720))

    # 从文件名中解析出训练集的类

    for i in range(m):
        # 获得文件的名字

        fileNameStr = trainingFileList[i]

        # 获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        # 将获得的类别添加到hwLabels中

        hwLabels.append(classNumber)

        # 将每一个文件的1x720数据存储到trainingMat矩阵中

        trainingMat[i, :] = dataTrain[i-1]

    # 构建kNN分类器

    neigh = KNN(n_neighbors=3, algorithm='auto')

    # 拟合模型, trainingMat为训练矩阵,hwLabels为对应的标签

    neigh.fit(trainingMat, hwLabels)

    # 返回testingSetGray目录下的文件列表

    testFileList = listdir('C:\\Users\\Tony.Hsu\\Desktop\\writingTest\\testingSetGray')

    # 错误检测计数

    errorCount = 0.0

    # 测试数据的数量

    mTest = len(testFileList)
    print(mTest)

    # 从文件中解析出测试集的类别并进行 分类测试

    for i in range(mTest):

        # 获得文件的名字

        fileNameStr = testFileList[i]

        # 获得分类的数字,根据文件命名进行划分

        classNumber = int(fileNameStr.split('_')[0])

        # 获得测试集的1x720向量,用于测试
        print(i)
        
        vectorUnderTest = dataTest[i]        # 获得预测结果

        classifierResult = neigh.predict(vectorUnderTest.reshape(1, -1))

        print("The classification returns a result of:%d\tThe true result is:%d" % (classifierResult, classNumber))

        if classifierResult != classNumber:
            errorCount += 1.0
            
    print("\n")
    
    print("The total number of wrong data is: %d\nThe error rate is %f%%" % (errorCount, errorCount / mTest * 100))


"""

函数说明:main函数

"""

if __name__ == '__main__':
    handwritingClassTest()

四.结果分析

在这里插入图片描述训练结果正确的将0-9分类,并预测成功。

五.注意事项

  1. 图像批量重命名的时候要保证数字位数相同!我第一次弄得乱七八糟,心累。
ValueError: Expected 2D array, got 1D array instead:
Reshape your data either using array.reshape(-1, 1) if your data has 
a single feature or array.reshape(1, -1) if it contains a single sample.

这是由于在新版的sklearn中,所有的数据都应该是二维矩阵,哪怕它只是单独一行或一列(比如前面做预测时,仅仅只用了一个样本数据),所以需要使用.reshape(1,-1)进行转换。

vectorUnderTest = dataTest[i]        # 获得预测结果

classifierResult = neigh.predict(vectorUnderTest.reshape(1, -1))
发布了13 篇原创文章 · 获赞 2 · 访问量 1830

猜你喜欢

转载自blog.csdn.net/SouthWooden/article/details/99538238