基于Gist特征+PCA+KNN的场景分类


什么是场景分类

场景分类是语义分类的一种,例如下图中的图片根据场景分为8种,分别是海滨、森林、高速公路、城市、高山、乡村、街道、高楼。


室外场景图

分类的目的是给定一张图片,给出它属于哪个场景。

整体思路

  1. 把一个图片分成4x4个同样大小的块,每个块用4方向8尺度共32个gabor滤波器滤波,然后对每个块里的像素求平均值,那么一张图片最终能得到16x4x8共512维的一个特征向量。PCA算法能够提取到主要特征,在不是特别影响准确度的情况下,降低计算复杂度,这里从512维降到80维。最后使用KNN算法确定分类结果,这里的k取3。


    基于Gist特征+PCA+KNN的场景分类_第1张图片
    场景分类流程图
  2. 数据集地址
    我们先从数据集地址下载ACB.tar.gz、AnFpark.tar.gz、FDFpark.tar.gz三个压缩包并解压,它们分别属于教学楼、公园A、公园B三个场景类,里面都是视频,暂时只提取每个视频的第一帧图片作为这次项目的数据集。
  3. 每个场景取80张图片作为训练集,2o张图片作为测试集,实际训练时随机取50张图片,测试时随机取10张。

核心代码

  1. 提取视频里的第一帧
#这里只给出AnFpark里视频获取图片的代码,其它两个类似。
rootdir = '/Users/gcf/Desktop/AnFpark'
list = os.listdir(rootdir)
for i in range(0, len(list)):
    path = os.path.join(rootdir, list[i])
    vc = cv2.VideoCapture(path)
    rval, frame = vc.read()
    res = cv2.resize(frame, (352, 240), interpolation=cv2.INTER_CUBIC)
    cv2.imwrite('/Users/gcf/Desktop/image/' + 'AnFpark_' + str(i) + '.jpeg', res)
  1. gabor滤波器获取gist特征
"""
img:图片数据
blocks:划分成blocks*blocks个patch
direction:garbo滤波器的方向
scale:滤波器的尺度
返回gist特征
"""
def getGist(img,blocks,direction,scale):
    #1.获取滤波器
    filters = buildFilters(direction,scale)
    #2.分割图片
    img_arr = cropImage(img,blocks)
    #3.gist向量
    img_array = process(img_arr,filters)

    return  img_array

#分割图片
def cropImage(img,blocks):
    img_array = []
    w,h=img.size
    patch_with = w / blocks
    patch_height = h / blocks
    for i in range(blocks):
        for j in range(blocks):
            crop_image = img.crop((j*patch_with,i*patch_height,patch_with*(j+1),patch_height*(i+1)))
            img_array.append(crop_image)
            # name = str(i)+'行'+str(j)+'列'+'.jpeg'
            # crop_image.save(name)
    return img_array


#构建Gabor滤波器
def buildFilters(direction,scale):
    filters = []
    lamda = np.pi/2.0

    for theta in np.arange(0,np.pi,np.pi/direction):
        for k in xrange(4):
            kern = cv2.getGaborKernel((scale[k],scale[k]),1.0,theta,lamda,0.5,0,ktype=cv2.CV_32F)
            kern /= 1.5*kern.sum()
            filters.append(kern)
    return filters

#gist向量
def process(img_array,filters):
    res = []
    for img in img_array:
        img_ndarray = np.asarray(img)
        for filter in filters:
            accum = np.zeros_like(img_ndarray)
            for kern in filter:
                fimg = cv2.filter2D(img_ndarray, cv2.CV_8UC3, kern)
                np.maximum(accum, fimg, accum)
            average = np.mean(accum)
            res.append(average)
    round_res = np.round(res, 4)
    return round_res
  1. PCA降纬
"""
data_array:特征向量数组
k:降低到k维
返回k个特征向量
"""
def pca(data_array,k):
    #计算每个纬度的平均值
    mean=np.array([np.mean(data_array[:,i]) for i in range(data_array.shape[1])])
    #数据中心化
    norm_X=data_array-mean
    #散度矩阵
    scatter_matrix=np.dot(np.transpose(norm_X),norm_X)
    #获得数组(特征值,特征向量)
    eig_val, eig_vec = np.linalg.eig(scatter_matrix)
    #降序排列特征值索引
    val_index = np.argsort(-eig_val)
    #选择前k个特征向量
    features=np.array([eig_vec[i] for i in val_index[:k]])
    return features
  1. KNN求分类结果
"""
data:测试数据
dataset:训练数据源
labels:训练数据标签
k:k邻近
返回测试数据标签
"""
def knn(data,dataset,labels,k):
  #计算行数
  dataSetSize = dataset.shape[0]
  #矩阵作差(每行一个样本,行里的每个元素都是属性)
  diffMat = np.tile(data, (dataSetSize, 1)) - dataset
  #差平方
  sqDiffMat = diffMat ** 2
  #差平方和(列元素相加)
  sqDistance = sqDiffMat.sum(axis=1)
  #开根号(欧式距离)
  distance = sqDistance ** 0.5
  #从小到大排序(元素下标)
  sortedDistIndicies = distance.argsort()
  #投票
  classCount = {}
  for i in range(k):
      #获取对应的标签
      voteIlabel = labels[sortedDistIndicies[i]]
      #票数+1
      classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
  #投票从大到小排序
  sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
  return sortedClassCount[0][0]

实验

基于Gist特征+PCA+KNN的场景分类_第2张图片
获取150个样本的Gist特征值

基于Gist特征+PCA+KNN的场景分类_第3张图片
测试30个样本的准确率

10次测试的结果

总结

  • Gist特征没有考虑颜色信息,结合颜色信息效果会更好。
  • 下一篇文章将考虑使用简单的卷积神经网络来代替KNN算法做分类器。
  • 关于项目代码,我将在下一篇文章中给出,它里面包含所有用到的代码。
  • 项目运行环境:mac pro笔记本、python 2.7、PyCharm IDE

你可能感兴趣的:(基于Gist特征+PCA+KNN的场景分类)