一叶舟轻
双桨鸿惊
概念
前言
计算机视觉作业,利用词袋模型进行图像检索,并计算检索的正确率和召回率,评估效果
BoW(词袋模型)
词袋模型(Bag of Words)最初被用到文本分类中,将文档表示成特征向量。其对于文本来说,忽略词法、句法,将文本仅看作单词的集合。那么每篇文档就都相当于一个袋子,里面装着各个单词。例如:
A: My name is OzyMandias, King of kings!
B: My name is Airkuit, princess of Vampire!
基于这两个文档,可以构造一个词典:
Dictionary = {1:"My", 2:"name", 3:"is", 4:"OzyMandias", 5:"King", 6:"of", 7:"kings", 8:"Airkuit", 9:"princess", 10:"Vampire"}
那么A、B可以以如下特征向量形式表示:
A: [1,1,1,1,1,1,1,0,0,0]
B: [1,1,1,0,0,1,0,1,1,1]
词典一共10维,特征向量则也是10维,每一维特征表示次特征出现次数,这便对文本完成了降维。
在图像处理中,也可以应用这种方法。文本中,以单词作为特征,图像中则以SIFT、SURF等经典算子提取的矢量进行后续处理。但是图像中提取出的矢量数目是非常大的,很难进行处理,因此对其进行聚类,得到指定数目的特征簇,这些特征簇则相当于文本中的单词。那么一幅图像就可以用特征向量来描述了,完成图像降维。
图像检索或分类
以特征向量描述图像之后,就可以进行图像检索或分类了,我们已有一堆已知标签的图像的特征向量,对新进入的图像进行上述操作得到其特征向量之后,可以使用最简单的KNN方法即可检索出与这张图最相似的图,或者预测这张图的类别。当然如果数据量比较大,这里还是会很耗时,也可以采用其他机器学习方法。不过我们这次不是为此而来,我们要进行正统的图像检索。这里就用到了另一个文本处理的方法——倒排索引(Inverted File Index)
如之前所述,A、B目前被表示为10维特征向量,那么我们可以通过某一个特征找到A或B吗?相当于我们使用百度、谷歌等搜索引擎一样,通过关键字,能检索到相应文本。在这里就是将特征作为索引,该特征出现在图像中的次数作为向量值,如下所示
1: [1,1]
2: [1,1]
3: [1,1]
4: [1,0]
5: [1,1]
6: [1,0]
7: [0,1]
8: [0,1]
9: [0,1]
10: [0,1]
接下来,检索一张图片可以使用简单的投票法,将图片表示成特征向量之后,逐一检索其特征,根据倒排索引表统计该图片最相思的那些图片。当然这里是存在问题的。比方说,文本中最常见的单词是诸如”我“、”是“这些无意义词,图像中也一样。所以需要对特征向量加一个权重,降低这些无意义特征权重,增加决定特征权重。这里使用tf-idf权重,tf为词频,idf为逆文档词频,tf-idf值越大,说明这个词对这篇文章越重要
\[tf = 某词文章中出现次数 / 文章总词数\] \[idf = log(文档总数/(包含该词文档数+1))\] \[tf-idf = tf*idf\]在图像检索中,特征出现次数可以直接作为词频使用,那么再乘一个idf值,得到tf-idf向量,然后\(l_2\)归一化,再与倒排索引矩阵求内积进行相似性度量即可。
实验
数据
vgg的衍射变换数据集,下载地址:http://www.robots.ox.ac.uk/~vgg/data/affine/
步骤
-
提取SIFT特征
def chooseFeatures(imgPath,resPath=None): sift = cv2.xfeatures2d.SIFT_create() img = cv2.imread(imgPath) #gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) keypoints, descriptors = sift.detectAndCompute(img,None) return descriptors
-
对特征进行K-Means聚类
def createClusters(descriptors, numClusters): clusters = KMeans(numClusters) clusters.fit(np.vstack(descriptors)) # returns a trained KMeans cluster return clusters
-
将图像用词向量表示
def createBoW(descriptors, clusters): imageBoW = np.zeros(clusters.k) for i in range(0, len(descriptors)): cluster = clusters.predict(descriptors[i]) imageBoW[cluster] = imageBoW[cluster] + 1 # returns the bag of words representation of a single image return imageBoW
array([[ 16., 12., 15., ..., 9., 99., 19.], [ 10., 9., 7., ..., 11., 13., 13.], [ 7., 6., 5., ..., 5., 10., 6.], ..., [ 54., 52., 50., ..., 101., 89., 42.], [ 57., 58., 56., ..., 111., 76., 52.], [ 56., 61., 58., ..., 92., 75., 49.]])
-
构建倒排索引表
def createInvertedTable(imageBows): imageBows = imageBows * idf imageBows = normalize(imageBows, norm='l2') return imageBows.T
array([[0. , 0. , 0. , ..., 0. , 0. , 0. ], [0. , 0. , 0. , ..., 0. , 0. , 0. ], [0. , 0. , 0. , ..., 0. , 0. , 0. ], ..., [0. , 0. , 0. , ..., 0. , 0. , 0. ], [0.28282037, 0.08420714, 0.09850629, ..., 0.07760871, 0.06366686, 0.06512577], [0.05427866, 0.08420714, 0.05910378, ..., 0.03662434, 0.04356154, 0.04254884]])
-
选取图片计算准确率和召回率
def getPrecisionAndRecall(imagePath,imageLabels,imageType): descriptors = chooseFeatures(imagePath) imageBow = createBoW(descriptors, clusters) #imageBow = scaler.transform(imageBow.reshape(1,-1)) imageBow = imageBow * idf imageBow = normalize([imageBow], norm='l2') temp = np.dot(imageBow,invertedTable) rank = np.argsort(-temp)[0] results = imageLabels[rank[0:7]] #claculate precision and recall tp = np.sum(results == imageType) precision = tp / 7 recall = tp / 7 return precision, recall
(0.7714285714285714, 0.7714285714285714)
总结
在CNN未出现之前,BoW是非常热门的图像检索和分类方法,具有很强的可解释性。