近期分布式三维重构系统开发里面有个模块涉及到图片的相似度判断,基于图像之间的相关性来确定是否进行sift特征匹配,依据了解的一些图像检索方面的资料,采用的方案是对基于sift特征描述向量(128维)进行分层聚类,将图像描述成BOW(bag of words)模型,聚类算法是直接使用Opencv的cvKmeans();函数原型为:
CV_IMPL int
cvKMeans2( const CvArr* _samples, int cluster_count, CvArr* _labels,
CvTermCriteria termcrit, int attempts, CvRNG*,
int flags, CvArr* _centers, double* _compactness )
该函数实现在cxmatrix.cpp line 1192,函数的实现主体在kmeans函数中line849,因为没有网上的资料基本上只涉及cvKmeans2这个函数的使用接口,具体参数设置也没有明确的说明(尤其是flags),一开始仿照示例实现得到的结果有些令人费解:聚类结果还算较均匀,但是会出现没有样本数据的空子类,出于好奇就查阅了源码,搜索了cvKmeans引用的地方太多(opencv源代码的组织不是特别有规律,也可以看出此聚类算法在视觉领域相当通用的),后来才发现函数都在cxmatrix.cpp文件中;
cvKmeans2其实只是在kmeans之上进行了一层简单的封装,保持了cvArr*的使用接口(kmeans中使用的mat);
谜底在于kmeans实现细节了,函数原型为:
ouble kmeans( const Mat& data, int K, Mat& best_labels,
TermCriteria criteria, int attempts,
int flags, Mat* _centers )
其中Mat& best_labels可以传递初始值,此时需要将flags设为1(CV_KMEANS_USE_INITIAL_LABELS)
如果不采用此选项,初始中心有两种方式获得:
1. if( flags & KMEANS_PP_CENTERS )
generateCentersPP(data, centers, K, rng, SPP_TRIALS);
KMEANS_PP_CENTERS 值为2,这里使用的是Arthur & Vassilvitskii (2007) k-means++: The Advantages of Careful Seeding里面提出的算法,具体实现细节没有认真去看,总体上对样本数据进行分析提取出合适的初始中心,重要的是使用此选项问题很好的解决了,聚类很均匀,yeah~
2.generateRandomCenter()初始以及后续为空的话都调用该函数产生随机中心(其实也不算完全随机,每次取一样的种子数样本执行结果还是一样的),一开始按照示例flags取0就是默认了此选项,为空的话产生的随机中心固定的?不然迭代那么多次结果还是空的,这种情况在低维度比较少出现,但在128维的高维空间其实也是可以理解的,随机中心会一直偏离样本阈;某些实现版本中直接取一个样本数据作为中心也是可以保证不会出现空集合的情况,但是聚类结果不如人意;
函数整体框架包括两层循环,外层是step,聚类次数,多次从原初状态独立聚类取compactness值最小的,内部就是进行最多100次迭代(收敛的话直接退出),代码层次结构还是很清晰的;
以后调用函数还是先多探究下函数的接口,特别是flags扩充函数功能的灵活接口;
另外想调试是之间进入函数代码实现中,就像mfc atl中的cpp文件一样,在VC++项目设置里添加了cpp文件的搜索路径还是没能跳进实现中进行调试,这样就不用那么麻烦去找内部繁琐的接口关系,如果谁有这方面经验的不吝赐教,万分感激~