10.4 局部特征表示
在计算机视觉领域里有一个较新的进展,那就是基于局部特征的方法。局部特征 (local feature)跟之前我们介绍的从整个图像里计算出的特征不同,它是在图像的一小块区域内计算出来的特征。mahotas支持这类特征中的一种;加速稳健特征 (Speeded Up Robust Feature)又叫做SURF ;还有一些其他特征,其中最有名的要算是尺度不变特征变换 (Scale-Invariant Feature Transform,SIFT)。这些局部特征对于旋转和光照变化十分稳健(也就是说,在光照变化的时候,它们的值只有很小改变)。
在使用这些特征的时候,我们需要确定在哪里计算它们。这里有3个经常采用的选项:
随机计算;
在一个格子里计算;
检测图像中的兴趣区域(这种技术叫做关键点检测 ,即keypoint detection,或兴趣点检测 ,即interest point detection)。
所有这些方式都是可行的,在正确的场景下,它们都可以给出较好的结果。mahotas对这三种方式都提供了支持。如果你确定的兴趣点可以和图像里的重要区域相对应,那么兴趣点检测的效果将会是最好的。当然,这取决于你的图像集合里面包含了什么。通常,它们对人造图像的效果比自然风景图像要好。人造景观有较强烈的角度、边界,以及高对比度的区域。这些通常会被自动检测器识别为兴趣区域。
由于我们所用的图像几乎都是自然景观,所以我们将使用兴趣点方法。用mahotas很容易计算这些特征;引入正确的子模块,并调用surf.surf 函数:
from mahotas.features import surf descriptors = surf.surf(image, descriptors_only=True)
descriptors_only=True 标志是说,我们只对它们的描述符感兴趣,对它们的像素位置、大小和其他信息并不感兴趣。或者,我们可以采用密集抽样方法,使用surf.dense 函数:
from mahotas.features import surf descriptors = surf.dense(image, spacing=16)
它返回了描述符的值。这个值是从相互之间相距16像素的点集中计算出来的。由于这些点的位置是固定的,所以我们对兴趣点的元信息并不是很感兴趣,它也不会默认返回。不管在哪种情况下,它的结果(这些描述符)都是一个n ×64的数组,其中n 是抽样点的个数。抽样点的个数取决于图像的大小、图像的内容,以及传到函数里的参数。我们之前使用的都是默认参数,用这种方式我们每个图像都可以得到几百个描述符。
我们不能直接把这些描述符传进支持向量机、逻辑回归或者类似的分类系统中。要使用图像中的描述符,这里有几种方案。比如我们可以对它们取平均值,但这种做法的效果并不是很好,因为这样会丢失位置相关的信息。在这种情况下,我们应该用另外一组基于边缘检测的全局特征集合。
在这里我们要采用的方案是词袋 模型。这是一个非常新的想法。它第一次发表是在2004年。这是一个事后看起来非常明显的想法:它非常简单,效果非常好。
在处理图像的时候提到“词语”,这好像有些奇怪,如果你不把它想象成写出来的词语,而把它当做说出来的声音,那就很容易理解它们之间的区别。如果一次说出一个词语,那么声音就会略有不同,所以它的波形也不会跟其他时候完全相同。然而,通过对波形的聚类,我们希望可以从中恢复出大部分结构信息,使得包含该词语的所有样本都聚集在一个簇中。即使这个过程并不完美(也不会是完美的),我们仍然可以把波形的分组当做词语。
这就是我们所说的视觉词语:把图像中看起来相似的区域聚成一组,把它们叫做视觉词语。这里的分组是我们在第3章中所碰到的聚类的一种形式。
注意 词语个数一般对算法的最终效果并没有很大影响。不过,如果这个数字特别小(10或20,当你有几千图片的时候),那么整个系统的效果就不会很好。类似的,如果你有太多的词语(例如比图片数目多得多),那么这个系统也不会有好效果。然而,在两个极端之间,通常有一个很大的区间,你可以在不影响效果的情况下选择词语的数目。作为一个经验法则,如果你有很多很多图片,采用诸如256、512或1024这样的数值应该可以得到不错的结果。
我们从计算特征开始:
alldescriptors = [] for im in images: im = mh.imread(im, as_grey=True) im = im.astype(np.uint8) alldescriptors.append(surf.surf(im, descriptors_only))
这样可以得到超过100 000个局部描述符。现在,我们使用k均值聚类 (k-means clustering)得到聚类中心。我们可以使用所有的描述符,但是为了提速,我们只使用其中一个较小的抽样集合:
concatenated = np.concatenate(alldescriptors) # 把所有描述符放进一个数组中 concatenated = concatenated[::32] # 只使用第32个元素所组成的向量 from sklearn.cluster import Kmeans k = 256 km = KMeans(k) km.fit(concatenated)
在所有这些都做完之后(会花费一点工夫),km 里会包含所有关于聚类中心的信息。我们现在回到描述符,来构建特征向量:
features = [] for d in alldescriptors: c = km.predict(d) features.append( np.array([np.sum(c == ci) for ci in range(k)]) ) features = np.array(features)
这个循环得到的最后结果是:features[fi] 是一个跟图像位置fi 相对应的直方图(用np.histogram 函数可以使计算更快,但要把参数弄正确却需要一点技巧,而且剩下的代码会比现在这个简单步骤缓慢许多)。
结果是:现在每个图像可以用一列数目相同的特征来表示(在我们这里类簇个数是256)。然后我们就可以使用标准分类方法进行分类了。再次使用逻辑回归,我们现在得到了62%的正确率,有7%的提升。我们把所有特征组合在一起,可以得到67%的正确率,比基于纹理的方法提高12%。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论