文章已迁移至:http://www.codeci.cn/
- 准备数据
- knn算法
- 简单图片识别(txt格式)
- 简单图片识别
txt格式:
text_digits数据集
training_digits数据集
图片格式:
text_digits数据集
trainning_digits数据集
数据集(训练集和测试集的下载)
d = ( a i − b i ) + ⋯ + ( a n − b n ) d=\sqrt{(a_i-b_i)+\cdots+(a_n-b_n)} d=(ai−bi)+⋯+(an−bn)
选取k个欧拉距离最近的值,判断k个值里面出现的频率最高的特征(即欧拉距离对应训练集图片的数字)。
knn算法简单来说通过欧拉距离比较相似度,从k个结果里面选取频率最高的那个。
代码:
#文本向量化 32x32 -> 1x1024
def img2vector(filename):
returndigits = []
f = open(filename) #打开txt文件
for i in range(32):
linestr = f.readline() #逐行读取数据
for j in range(32):
returndigits.append(int(linestr[j])) #读取数据逐行追加到列表后面
return returndigits
单独测试时,可以将filename改成txt格式图片的文件地址,用print函数查看运行结果。
def classnumCut(fileName):
fileStr = fileName.split('.')[0] #根据文件名中的字符"."进行切片
classNumStr = int(fileStr.split('_')[0]) #根据文件切片结果中的"_"进行再次切片
return classNumStr
图片文件名格式:num1_num2,获取一系列数字num1存入列表。从文件名中分离出数字主要涉及到python的切片操作知识(python的一个重点)。
def trainingDataSet():
hwLabels = []
trainingFileList = listdir('trainingDigits') #获取目录内容
m = len(trainingFileList) #获取文件目录长度
trainingMat = zeros((m,1024)) #m维向量的训练集
for i in range(m):
fileNameStr = trainingFileList[i] #获取第i行文件
hwLabels.append(classnumCut(fileNameStr)) #文件所表示的数字追加到列表(列表内的数字与文件txt显示的数字一一对应)
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) #将向量化的数据追加到trainingMat列表,一行代表一个文件的数据,与hwLabels列表和txt文件对应)
return hwLabels,trainingMat
将训练集的图片向量化,形成一个二元列表。knn算法就是从n个列表中(训练集图片)选取n个距离最近的值。
#knn算法
def sort(inputimage,dataSet,labels,k):
dataSetSize = dataSet.shape[0] #已知分类的数据集(训练集)的行数
#先tile函数将输入点拓展成与训练集相同维数的矩阵,再计算欧氏距离
diffMat = tile(inputimage,(dataSetSize,1))-dataSet #样本与训练集的差值矩阵
sqDiffMat = diffMat ** 2 #差值矩阵平方
sqDistances = sqDiffMat.sum(axis=1) #计算每一行上元素的和
distances = sqDistances ** 0.5 #开方得到欧拉距离矩阵
sortedDistIndicies = distances.argsort() #按distances中元素进行升序排序后得到的对应下标的列表(argsort函数排序的是列表元素的下标)
#选择距离最小的k个点
classCount = {}
for i in range(k):
voteIlabel = labels[ sortedDistIndicies[i] ] #读取labels列表中相同下标的值(即训练组中的值)
classCount[voteIlabel] = classCount.get(voteIlabel,0)+1 #计算classCount列表中voteIlabel值出现的次数
sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #按classCount字典的第2个元素(即类别出现的次数)从大到小排序
return sortedClassCount[0][0]
将测试数据拓展成相同维度矩阵([0,1,……,1,0]转换成[[0,1,……,1,0],[0,1,……,1,0],[0,1,……,1,0],[0,1,……,1,0],……,[0,1,……,1,0],[0,1,……,1,0]]),相当于将测试图片与训练集每个图片进行比较,计算欧拉距离。
排序时比较的是距离大小,排列的是对应列的下标。
拓展后的测试矩阵下标与训练组矩阵下标一一对应,读取下标在训练组中对应的值并形成字典[“key”(出现次数):“value”(测试组数字),……]。最后比较字典第二个元素,即测试组对应的数字(value),第一个元素(key)最大的即是测试结果。
#测试函数
def handwriting():
hwLabels,trainingMat = trainingDataSet() #构建训练集
testFileList = listdir('testDigits') #获取测试集
errorCount = 0.0 #错误数
mTest = len(testFileList) #测试集总样本数
for i in range(mTest):
fileNameStr = testFileList[i]
classNumStr = classnumCut(fileNameStr)
data= img2vector('testDigits/%s' % fileNameStr) #调用knn算法进行测试
classifierResult = sort(data, trainingMat, hwLabels,6)
print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
if (classifierResult != classNumStr):
errorCount += 1.0
#print "error:the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
print "\nthe total number of tests is: %d" % mTest #输出测试总样本数
print "the total number of errors is: %d" % errorCount #输出测试错误样本数
print "the total error rate is: %f" % (errorCount/float(mTest)) #输出错误率
#确定主函数
if __name__ == "__main__":
handwriting()
上述函数可以实现knn算法图片txt文件的识别。相对来说,图片格式的识别对于数据处理的要求更高,但其原理类似,将图片格式转二值化后进行数据比较。
图片识别需要更改上述函数涉及到训练组数据的部分,将调用img2vector函数改成调用imgBinary函数。图片二值化的方法较多,此处采用PIL库进行比较粗糙的二值化处理,总的来说,knn算法下,二值化的准确程度对于测试结果的影响较大。
#图片二值化处理
def imgBinary(inputimage):
img = Image.open(inputimage) #调用PIL库Image函数
img = img.resize((32, 32)) #调整图片大小
width, height = img.size #获取图片的规格(宽、高)
img = img.convert('L') #调用PIL库中的convert函数:图片进行灰度化,模式L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度
data = []
#灰度图片二值化处理
for i in range(height):
for j in range(width):
pixel = img.getpixel((j, i)) #调用PIL库中的getpixel函数获取像素点
if (pixel > 220): #比较灰度值进行二值化
pixel = 0
else:
pixel = 1
data.append(pixel) #数据追加到列表
return data
knn算法中的重点主要是knn算法的理解,文件名中数字的提取,数据比较的处理,图像二值化的处理,注意列表,元组,字典的用法。在实现过程中注意使用函数进行测试,查看数据处理的结果是否合理。遇到调用函数的问题时多看官方文档,查清函数的格式,输入,输出。