我是靠谱客的博主 忐忑白云,最近开发中收集的这篇文章主要介绍计算机视觉 图像内容分类- K-近邻(KNN)算法和denseSIFT算法原理-手势识别,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Python 计算机视觉 knn算法和denseSIFT算法原理-图像内容分类-图像识别-手势识别

    • 一、原理
          • 1.K邻近分类法(KNN)
          • 2.稠密SIFT(Dense SIFT)
      • 二、代码实现
      • 1.KNN算法分类二维数据集
      • 2.d-sift实现
      • 3.手势识别

一、原理

1.K邻近分类法(KNN)

KNN算法是分类算法中最典型最容易实现的算法。

工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。
一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。

所以我们可以总结出其算法步骤为:

  1. 计算测试对象到训练集中每个对象的距离

  2. 按照距离的远近排序

  3. 选取与当前测试对象最近的k的训练对象,作为该测试对象的邻居

  4. 统计这k个邻居的类别频率

  5. k个邻居里频率最高的类别,即为测试对象的类别

2.稠密SIFT(Dense SIFT)

Dense SIFT算法,是一种对输入图像进行分块处理,再对每一块进行SIFT运算的特征提取过程。Dense SIFT根据可调的参数大小,来适当满足不同分类任务下对图像的特征表征能力;而传统的SIFT算法则是对整幅图像的处理,得到一系列特征点。

Dense-SIFT在非深度学习的模型中,常常是特征提取的第一步。采样的点提取SIFT描述子后,经过码书投影,投影在同一个码字上的采样点都代表了一组描述子相似的点。不同的码字(相当于直方图的每一个bin)之间,采样点的区分能力是不一样的。我们以下图图1为例,bin2代表的是一块很平坦的区域,于是dense采样时,很多点产生的描述子都会投影在bin2上。而bin1,bin3,bin4分别代表一块特有的区域,仅仅在dense采样到自行车,大提琴和眼睛等部位时,才能够形成类似的描述子。换而言之,bin2的重要性最低,而其他码字的重要性都很高。
在这里插入图片描述

通常来讲Dense SIFT更适用于图像分类识别的任务,而传统SIFT更适用于图像检索分割的任务,dense-SIFT在图像检索上的性能不如SIFT检测子的性能好。

二、代码实现

1.KNN算法分类二维数据集

knn分类器
其中K值的选择会影响分类的性能

from numpy import * 

class KnnClassifier(object):
    
    def __init__(self,labels,samples):
        """ Initialize classifier with training data. """
        
        self.labels = labels
        self.samples = samples
    
    def classify(self,point,k=3):
        """ Classify a point against k nearest 
            in the training data, return label. """
        
        # compute distance to all training points
        dist = array([L2dist(point,s) for s in self.samples])
        
        # sort them
        ndx = dist.argsort()
        
        # use dictionary to store the k nearest
        votes = {}
        for i in range(k):
            label = self.labels[ndx[i]]
            votes.setdefault(label,0)
            votes[label] += 1
            
        return max(votes, key=lambda x: votes.get(x))


def L2dist(p1,p2):
    return sqrt( sum( (p1-p2)**2) )

def L1dist(v1,v2):
    return sum(abs(v1-v2))

先随机创建两个不同的二维点集,其中一类使数据点成环状分布。

# -*- coding: utf-8 -*-
from numpy.random import randn
import pickle
from pylab import *

# create sample data of 2D points
n = 200
# two normal distributions
class_1 = 0.3 * randn(n,2) #200个随机点
class_2 = 1.5 * randn(n,2) + array([5,1])#分开两堆随机点
labels = hstack((ones(n),-ones(n)))#用标签分开前200和后200随机点
# 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!")

运行两次该脚本,第二次时修改一下保存的pkl文件名,这样我们就可以获得4个pkl文件,2个用来做训练,2个用来做测试。

接着我们用KNN分类器对数据点进行分类

# -*- coding: utf-8 -*-
import pickle
from pylab import *
from PCV.classifiers import knn
from PCV.tools import imtools

pklist=['points_normal.pkl','points_ring.pkl']

figure()

# 用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)
    # 用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)))
      # 在测试数据集的第一个数据点上进行测试
    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()

实验结果
n=200时:改变K值时不同结果图

在这里插入图片描述
在这里插入图片描述
n=400,k=3时
在这里插入图片描述
实验结论:当k较小时,分类数据点的效果就越准确。而数据样本的大小并不会影响分类效果。

2.d-sift实现

D-Sift文件
用密集采样的sift描述子处理一幅图像。

函数输入(特征大小size,位置之间的步长steps,是否强迫描述子的方位force_orientation(false比奥是所有的方位均朝上),用于调整图像大小的元组)

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

计算dsift描述子

# -*- coding: utf-8 -*-
from PCV.localdescriptors import sift, dsift
from pylab import*
from PIL import Image

dsift.process_image_dsift('gesture/zhongshan.jpg','zhongshan.dsift',90,40,True)
l,d = sift.read_features_from_file('zhongshan.dsift')
im = array(Image.open('gesture/zhongshan.jpg'))
sift.plot_features(im,l,True)
title('dense SIFT')
show()

可视化结果:

在这里插入图片描述

3.手势识别

5种手势
在这里插入图片描述我用了94张图片做训练集,50张图片做测试集,这样可以使结果更加准确
在这里插入图片描述

先对所有图片进行尺寸调整,并生成sift文件

将所有图片的尺寸调整为了100x100,若不这么处理所有图片,可能会使图像特征向量长度不一致,导致比较出错。

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('.JPG')]

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)


filelist_train = get_imagelist('gesture/myself/train3/')
filelist_test = get_imagelist('gesture/myself/test3/')
imlist=filelist_train+filelist_test


for filename in imlist:
    featfile = filename[:-3]+'dsift'
    dsift.process_image_dsift(filename,featfile,20,10,resize=(100,100))


函数read_gesture_features_labels 生成dsift可视化:

在这里插入图片描述
读取训练集,测试集的dsift文件

features,labels = read_gesture_features_labels('gesture/myself/train3/')
test_features,test_labels = read_gesture_features_labels('gesture/myself/test3/')
classnames = unique(labels)

开始用KNN来进行分类

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

生成分类的正确率:
在这里插入图片描述
由于我的图片的背景没有太多冗余的障碍,并且训练集有足够的数量,得到的正确率相比有背景较乱的图片,较少的训练集更高一些。
最后用混淆矩阵来判断哪些手势是分类错误的

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)

得到的混淆矩阵:
在这里插入图片描述
可看出Rock手势在这里插入图片描述
有一个被判断成了Eight手势在这里插入图片描述,一个被判断成了Ye手势在这里插入图片描述,其余手势均正确。


手势识别完整代码:

# -*- coding: utf-8 -*-
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('.JPG')]

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/myself/train3/')
filelist_test = get_imagelist('gesture/myself/test3/')
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=(100,100))



features,labels = read_gesture_features_labels('gesture/myself/train3/')
test_features,test_labels = read_gesture_features_labels('gesture/myself/test3/')
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)


问题解决
在运行手势识别代码时候,之前遇到了无法生成disift文件,却发现没有出现任何报错问题。后发现是文件后缀名要区分大小写。。。在这里插入图片描述

最后

以上就是忐忑白云为你收集整理的计算机视觉 图像内容分类- K-近邻(KNN)算法和denseSIFT算法原理-手势识别的全部内容,希望文章能够帮你解决计算机视觉 图像内容分类- K-近邻(KNN)算法和denseSIFT算法原理-手势识别所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部