BOW其实就是bag of word的缩写,在OpenCV中关于此框架的有3个类。
第一个是一个基类,算是BOW训练的类型,
class BOWTrainer { public: BOWTrainer(){} virtual ~BOWTrainer(){} void add( const Mat &descriptors); const vector<Mat> &getDescriptors() const; int descriptorsCount() const; virtual void clear(); virtual Mat cluster() const = 0; virtual Mat cluster( const Mat &descriptors ); protected: … }
第二个类别是我们在应用的时候真正的BOW训练的接口类别叫做BOWKmeansTrainer, 此类继承来自BOWTrainer类别(不知道这么说对不对,C++还在学习中)
class BOWKmeansTrainer : public BOWTrainer { public: BOWKmeansTrainer( int clusterCount, const TermCriteria &termcrit = TermCriteria(), int attempts = 3, int flags = KMEANS_PP_CENTERS); virtual ~BOWKmeansTrainer(){}; virtual Mat cluster() const; virtual Mat cluster( const Mat &descriptors ) const; protected: … }
利用此类先定义一个 bowTraining;
BOWKmeansTrainer bowTraining(1000); //定义聚类中心1000个,其余的默认参数;
然后,将得到的特征,例如SIFT特征,将每一副图的SIFT特征利用add函数加入到bowTraining中去。
for(int i=0; i<numOfPictures; i++) bowTraining.add( descriptors( i ) );
将所有的特征加进去后,就可以进行聚类训练了:
Mat dictionary = bowTraining.cluster(); //这一步的时间根据特征的维度以及定义的词典中心的个数相关。
或者,将得到的特征合并成一个矩阵,这里贴出OpenCV的BOW内部合成矩阵的代码
int descCount = 0; for( size_t i = 0; i < descriptors.size(); i++ ) descCount += descriptors[i].rows; Mat mergedDescriptors( descCount, descriptors[0].cols, descriptors[0].type() ); for( size_t i = 0, start = 0; i < descriptors.size(); i++ ) { Mat submut = mergedDescriptors.rowRange((int)start, (int)(start + descriptors[i].rows)); descriptors[i].copyTo(submut); start += descriptors[i].rows; }
同样:
Mat dictionary = bowTraining.cluster( mergedDescriptors );
得到词典后,就要利用另一个类来进行图像BOW特征的提取----BOWImgDescriptorExtractor
class BOWImgDescriptorExtractor { public: BOWImgDescriptorExtractor( const Ptr<DescriptorExtractor> &dextractor, const Ptr<DescriptorMatcher> & dmatcher ); virtual ~BOWImgDescriptorExtractor(){} void setVocabulary( const Mat& vocabulary ); const Mat& getVocabulary() const; void compute( const Mat& image, vector<KeyPoint> & keypoints, Mat& imgDescriptor, vector<vector<int> >* pointIdxOfClusters = 0, Mat* descriptors = 0 ); int descriptorSize() const; int descriptorType() const; protected: … }
利用上面这BOW的第三个类别定义一个变量;
Ptr<DescriptorExtractor> extractor = DescriptorMatcher::create("SIFT"); //引号里面修改特征种类。 Ptr<DescriptorMatcher> matcher = DescriptorExtractor::create("BruteForce"); //引号里面修改匹配类型; BOWImgDescriptorExtractor bowDE(extractor, matcher);
前面两个定义是为了方便初始化类的定义,在BOW图像特征定义完成后,便可以对每一副图片提取BOW的特征。
bowDE.setVocabulary(dictionary); //dictionary是通过前面聚类得到的词典; for(int i=0; i<numOfPictures; i++) { vector<KeyPoint> keypoints; SiftFeatureDetector detector; detector.detect(pictures[i], keypoints); bowDE.compute(pictures[i], keypoints, descriptors); }
这样,整个BOW特征提取过程就结束了。