KNN分类法与手势识别
K邻近分类法(KNN)
1. 在分类算法中,最简单且最常用的一种方法之一就是KNN算法。这种算法把要分类的对象与训练集中已知类标定的所有对象进行对比,并由k近邻对指派到哪个类进行投票。但knn属于懒惰算法,这种算法需要预设k的值,k值会影响分类的性能,而且算法对大训练集搜索效果非常慢。
2. knn算法步骤如下:
(1) 算距离:给定测试对象,计算它与训练集中的每个对象的距离
(2)找邻居:圈定距离最近的k个训练对象,作为测试对象的近邻
(3)做分类:根据这k个近邻归属的主要类别,来对测试对象分类
3. k值设定为多大? k太小,分类结果易受噪声点影响;k太大,近邻中又可能包含太多的其它类别的点。(对距离加权,可以降低k值设定的影响)k值通常是采用交叉检验来确定(以k=1为基准)经验规则:k一般低于训练样本数的平方根
KNN简单二维示例
代码:首先创建两个不同的二维点集,用pickle模块保存,一个用于训练,一个用于测试。然后用决策函数(分类器)显示边界位置,默认的边界为零等值线。
import pickle
from pylab import *
from PCV.classifiers import knn
from PCV.tools import imtools
pklist=['points_normal.pkl','points_ring.pkl']
figure()
# load 2D points using Pickle
for i, pklfile in enumerate(pklist):
with open(pklfile, 'rb') as f:
class_1 = pickle.load(f)
class_2 = pickle.load(f)
labels = pickle.load(f)
# load test data using Pickle
with open(pklfile[:-4]+'_test.pkl', 'rb') as f:
class_1 = pickle.load(f)
class_2 = pickle.load(f)
labels = pickle.load(f)
model = knn.KnnClassifier(labels,vstack((class_1,class_2)))
# test on the first point
print (model.classify(class_1[0]))
#define function for plotting
def classify(x,y,model=model):
return array([model.classify([xx,yy]) for (xx,yy) in zip(x,y)])
# lot the classification boundary
subplot(1,2,i+1)
imtools.plot_2D_boundary([-6,6,-6,6],[class_1,class_2],classify,[1,-1])
titlename=pklfile[:-4]
title(titlename)
show()
运行结果:
(1)默认k=3的结果:
(2)k=2的结果:
可以看出分类结果更加精确了,也没出现错误。
(3)k=10的结果:
可以看出分界线上还有一些点集,错误也稍微变多,但整体效果上差别不是很大,可能是数据集较小的原因。
稠密SIFT描述子
对于图像识别问题,我们大都采用dense sift 来提取特征。图像识别问题之所以采用密集采样,是因为密集采样后的点,会通过训练后的分类器进行了进一步的筛选。所以,无需人工干预特征点的选取。即使密集采样的区域不能够被准确匹配,这块区域也包含了表达图像内容的信息。
dense sift 示例
demo可视化代码:
from PCV.localdescriptors import sift, dsift
from pylab import *
from PIL import Image
dsift.process_image_dsift('gesture/empire.jpg','empire.dsift',90,40,True)
l,d = sift.read_features_from_file('empire.dsift')
im = array(Image.open('gesture/empire.jpg'))
sift.plot_features(im,l,True)
title('dense SIFT')
show()
dsift 代码:如果force_orientation为真,则提取出的描述子会基于局部主梯度方向进行归一化,否则 只是简单地朝上。
from PIL import Image
from numpy import *
import os
from PCV.localdescriptors import sift
def process_image_dsift(imagename,resultname,size=20,steps=10,force_orientation=False,resize=None):
""" Process an image with densely sampled SIFT descriptors
and save the results in a file. Optional input: size of features,
steps between locations, forcing computation of descriptor orientation
(False means all are oriented upwards), tuple for resizing the image."""
im = Image.open(imagename).convert('L')
if resize!=None:
im = im.resize(resize)
m,n = im.size
if imagename[-3:] != 'pgm':
#create a pgm file
im.save('tmp.pgm')
imagename = 'tmp.pgm'
# create frames and save to temporary file
scale = size/3.0
x,y = meshgrid(range(steps,m,steps),range(steps,n,steps))
xx,yy = x.flatten(),y.flatten()
frame = array([xx,yy,scale*ones(xx.shape[0]),zeros(xx.shape[0])])
savetxt('tmp.frame',frame.T,fmt='%03.3f')
path = os.path.abspath(os.path.join(os.path.dirname("__file__"),os.path.pardir))
path = path + "\\python3-ch08\\win32vlfeat\\sift.exe "
if force_orientation:
cmmd = str(path+imagename+" --output="+resultname+
" --read-frames=tmp.frame --orientations")
else:
cmmd = str(path+imagename+" --output="+resultname+
" --read-frames=tmp.frame")
os.system(cmmd)
print ('processed', imagename, 'to', resultname)
原图一:
运行结果:
原图二:
运行结果:
KNN手势识别
在这边,我们用dense sift描述子来表示这些手势图像,得到所有图像的特征向量。用训练数据及其标记作为输入,创建分类器对象,然后在整个测试集上遍历classify()方法对每副图像进行分类。将布尔数组与1相乘并求和可以计算出分类的正确率。而输出的混淆矩阵可以显示每类有多少个样本被分在每一类的矩阵,显示错误的分布情况。
1. 手势图片的dense描述子
代码:
import os
from PCV.localdescriptors import sift, dsift
from pylab import *
from PIL import Image
imlist=['gesture/train/C-uniform02.ppm','gesture/train/B-uniform01.ppm',
'gesture/train/A-uniform01.ppm','gesture/train/Five-uniform01.ppm',
'gesture/train/Point-uniform01.ppm','gesture/train/V-uniform01.ppm']
figure()
for i, im in enumerate(imlist):
print (im)
dsift.process_image_dsift(im,im[:-3]+'dsift',90,40,True)
l,d = sift.read_features_from_file(im[:-3]+'dsift')
dirpath, filename=os.path.split(im)
im = array(Image.open(im))
#显示手势含义title
titlename=filename[:-14]
subplot(2,3,i+1)
sift.plot_features(im,l,True)
title(titlename)
show()
运行结果:
自己的数据集:
但手势含义的title却没有显示,不知为何…
2.KNN测试手势识别
代码:
from PCV.localdescriptors import dsift
import os
from PCV.localdescriptors import sift
from pylab import *
from PCV.classifiers import knn
def get_imagelist(path):
""" Returns a list of filenames for
all jpg images in a directory. """
return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.ppm')]
def read_gesture_features_labels(path):
# create list of all files ending in .dsift
featlist = [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.dsift')]
# read the features
features = []
for featfile in featlist:
l,d = sift.read_features_from_file(featfile)
features.append(d.flatten())
features = array(features)
# create labels
labels = [featfile.split('/')[-1][0] for featfile in featlist]
return features,array(labels)
def print_confusion(res,labels,classnames):
n = len(classnames)
# confusion matrix
class_ind = dict([(classnames[i],i) for i in range(n)])
confuse = zeros((n,n))
for i in range(len(test_labels)):
confuse[class_ind[res[i]],class_ind[test_labels[i]]] += 1
print ('Confusion matrix for')
print (classnames)
print (confuse)
filelist_train = get_imagelist('gesture/train')
filelist_test = get_imagelist('gesture/test')
imlist=filelist_train+filelist_test
# process images at fixed size (50,50)
for filename in imlist:
featfile = filename[:-3]+'dsift'
dsift.process_image_dsift(filename,featfile,10,5,resize=(50,50))
features,labels = read_gesture_features_labels('gesture/train/')
test_features,test_labels = read_gesture_features_labels('gesture/test/')
classnames = unique(labels)
# test kNN
k = 1
knn_classifier = knn.KnnClassifier(labels,features)
res = array([knn_classifier.classify(test_features[i],k) for i in
range(len(test_labels))])
# accuracy
acc = sum(1.0*(res==test_labels)) / len(test_labels)
print ('Accuracy:', acc)
print_confusion(res,test_labels,classnames)
课本uniform数据集运行结果:
对于uniform比较简单一致的数据集,准确率约为0.81。但P手势经常被误分类为V。
课本complex数据集运行结果:
对于complex相对复杂的数据集,准确率只有0.28,错配很多。
自己拍数据集运行结果:
这边不知道是自己拍的数据集不符标准还是什么原因,运行没有生成dsift文件,(应该是图片的问题)导致标签无法生成,也就无法分类识别。但一时也不知道如何解决,还需研究研究。(未完待续)