什么是场景分类
场景分类是语义分类的一种,例如下图中的图片根据场景分为8种,分别是海滨、森林、高速公路、城市、高山、乡村、街道、高楼。
分类的目的是给定一张图片,给出它属于哪个场景。
整体思路
-
把一个图片分成4x4个同样大小的块,每个块用4方向8尺度共32个gabor滤波器滤波,然后对每个块里的像素求平均值,那么一张图片最终能得到16x4x8共512维的一个特征向量。PCA算法能够提取到主要特征,在不是特别影响准确度的情况下,降低计算复杂度,这里从512维降到80维。最后使用KNN算法确定分类结果,这里的k取3。
- 数据集地址
我们先从数据集地址下载ACB.tar.gz、AnFpark.tar.gz、FDFpark.tar.gz三个压缩包并解压,它们分别属于教学楼、公园A、公园B三个场景类,里面都是视频,暂时只提取每个视频的第一帧图片作为这次项目的数据集。 - 每个场景取80张图片作为训练集,2o张图片作为测试集,实际训练时随机取50张图片,测试时随机取10张。
核心代码
- 提取视频里的第一帧
#这里只给出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)
- 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
- 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
- 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特征没有考虑颜色信息,结合颜色信息效果会更好。
- 下一篇文章将考虑使用简单的卷积神经网络来代替KNN算法做分类器。
- 关于项目代码,我将在下一篇文章中给出,它里面包含所有用到的代码。
- 项目运行环境:mac pro笔记本、python 2.7、PyCharm IDE