Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别

1.K-NN原理介绍

K近邻算法(K-NN)算法是一种简单但也很常用的分类算法,它也可以应用于回归计算。K-NN是无参数学习,这意味着它不会对底层数据的分布做出任何假设。它是基于实例,即该算法没有显式地学习模型。相反,它选择的是记忆训练实例,并在一个有监督的学习环境中使用。KNN算法的实现过程主要包括距离计算方式的选择、k值得选取以及分类的决策规则三部分。

1.1.距离计算方式的选择

选择一种距离计算方式,计算测试数据与各个训练数据之间的距离。距离计算方式一般选择欧氏距离或曼哈顿距离。
给定训练集: Xtrain=(x(1),x(2),x(3),…,x(i))Xtrain=(x(1),x(2),x(3),…,x(i)),测试集:Xtest=(x′(1),x′(2),x′(3),…,x′(j))Xtest=(x′(1),x′(2),x′(3),…,x′(j))
这里给出欧式距离公式:
在这里插入图片描述

1.2 k值的选取

在计算测试数据与各个训练数据之间的距离之后,首先按照距离递增次序进行排序,然后选取距离最小的k个点。
一般会先选择较小的k值,然后进行交叉验证选取最优的k值。k值较小时,整体模型会变得复杂,且对近邻的训练数据点较为敏感,容易出现过拟合。k值较大时,模型则会趋于简单,此时较远的训练数据点也会起到预测作用,容易出现欠拟合。

1.3分类的决策规则

常用的分类决策规则是取k个近邻训练数据中类别出现次数最多者作为输入新实例的类别。即首先确定前k个点所在类别的出现频率,对于离散分类,返回前k个点出现频率最多的类别作预测分类;对于回归则返回前k个点的加权值作为预测值。

2.K-NN可视化

2.1代码:

首先创建两个不同的二维点集,用pickle模块保存,一个用于训练,一个用于测试

from numpy.random import randn
import pickle
from pylab import *

# create sample data of 2D points
n = 200
# two normal distributions
class_1 = 0.6 * randn(n,2)
class_2 = 1.2 * randn(n,2) + array([5,1])
labels = hstack((ones(n),-ones(n)))
# save with Pickle
#with open('points_normal.pkl', 'w') as f:
with open('points_normal_test.pkl', 'wb') as f:
    pickle.dump(class_1,f)
    pickle.dump(class_2,f)
    pickle.dump(labels,f)
# normal distribution and ring around it
print ("save OK!")
class_1 = 0.6 * randn(n,2)
r = 0.8 * randn(n,1) + 5
angle = 2*pi * randn(n,1)
class_2 = hstack((r*cos(angle),r*sin(angle)))
labels = hstack((ones(n),-ones(n)))
# save with Pickle
#with open('points_ring.pkl', 'w') as f:
with open('points_ring_test.pkl', 'wb') as f:
    pickle.dump(class_1,f)
    pickle.dump(class_2,f)
    pickle.dump(labels,f)
    
print ("save OK!")

然后用决策函数(分类器)显示边界位置,默认的边界为零等值线。

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()

2.2结果

蓝色为一类,红色为一类,红色圆形为分类不正确的点。
k=3,n=200
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第1张图片
k=10,n=200
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第2张图片
k=3,n=100
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第3张图片
k值越大,分类结果越精确,数据越多,分类结果越不精确。通过k=3和10近乎无差异的对比,可以得知,针对某个数据集,当k的数值达到临界值时,继续增大k将降低计算效率。

3.dense sift特征提取

3.1原理

图像检索总是用SIFT(利用了检测子)
大多数情况下我们并没有训练样本。因此,我们需要利用人的经验过滤区分性低的点(除此之外还引入了IDF进一步加权)。因此,大部分检索问题都利用了检测子,而不是密集采样。
图像识别问题大多用Dense-SIFT
Dense-SIFT在非深度学习的模型中,常常是特征提取的第一步
对于图像识别问题来说,由于有充足的训练样本(正负样本均充足)。通过对训练样本的学习,我们会学习一个分类器。
Dense SIFT是图像特征描述子SIFT的快版变体,即稠密SIFT特征向量。当研究目标是对同样的物体或者场景寻找对应关系(correspondence)时, SIFT更好。而研究目标是图像表示或者场景理解时,Dense SIFT更好,因为即使密集采样的区域不能够被准确匹配,这块区域也包含了表达图像内容的信息。

3.2代码:

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()

3.3结果

原图:
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第4张图片
特征提取:
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第5张图片

4knn手势识别

4.1原理

在这个应用中,我们会用dense sift描述子来表示这些手势图像,并建立一个简单的手势识别系统。用dense sift函数对图像进行处理,可以得到图像的特征向量。用训练数据及其标记作为输入,创建分类器对象,然后在整个测试集上遍历classify()方法对每副图像进行分类。将布尔数组与1相乘并求和可以计算出分类的正确率。该结果会随着K值和dense sift图像描述子参数的选择而变化。
通过混淆矩阵,可以显示每类有多少个样本被分在每一类中的矩阵,它可以显示错误的分布情况,以及哪些类是经常互相“混淆”的。

4.2 代码:

dense sift描述子

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()

混淆矩阵

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)

4.2结果

4.2.1静态手势数据库的结果

Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第6张图片
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第7张图片

4.2.2 博主的手的结果

Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第8张图片
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第9张图片
由于训练集过小(每个手势3组)且变化过小,所以识别结果完全正确,没有任何误差……
Python计算机视觉编程 - 第八章 图像内容分类 -knn分类及可视化、dense sift原理、手势识别_第10张图片
这就是那3组零误差的图像。

你可能感兴趣的:(计算机视觉)