BoW(词袋)算法

BoW

BoW模型最初是为解决文档建模问题而提出的,因为文本本身就是由单词组成的。它忽略文本的词序,语法,句法,仅仅将文本当作一个个词的集合,并且假设每个词彼此都是独立的。这样就可以使用文本中词出现的频率来对文档进行描述,将一个文档表示成一个一维的向量。

将BoW引入到计算机视觉中,就是将一幅图像看着文本对象,图像中的不同特征可以看着构成图像的不同词汇。和文本的BoW类似,这样就可以使用图像特征在图像中出现的频率,使用一个一维的向量来描述图像。

要将图像表示为BoW的向量,首先就是要得到图像的“词汇”。通常需要在整个图像库中提取图像的局部特征(例如,sift,orb等),然后使用聚类的方法,合并相近的特征,聚类的中心可以看着一个个的视觉词汇(visual word),视觉词汇的集合构成视觉词典(visual vocabulary) 。 得到视觉词汇集合后,统计图像中各个视觉词汇出现的频率,就得到了图像的BoW表示。

总结起来就是:

  • 提取图像库中所有图像的局部特征,例如sift,得到特征集合FF

  • 对特征集合FF进行聚类,得到kk个聚类中心{Ci|i=1,…,k}{Ci|i=1,…,k},每一个聚类中心CiCi代表着一个视觉词汇。聚类中心的集合就是视觉词典vocabulary={Ci|i=1,…,k}vocabulary={Ci|i=1,…,k}

  • 一幅图像的BoW表示

    • 提取图像的局部特征,得到特征集合f={fi|i=1,…,n}f={fi|i=1,…,n}

    • 计算特征fifi属于那个词汇CiCi(到该中心的距离最近)

    • 统计每个词汇CiCi在图像中出现的频数,得到一个一维的向量,该向量就是图像的BoW表示。

综合起来,取得一幅图像的BoW向量的步骤:

  • 构建图像库的视觉词典Vocabulary

    • 提取图像库中所有图像的局部特征,如SIFT.

    • 对提取到的图像特征进行聚类,如k-means,得到聚类中心就是图像库的视觉词汇词典Vocabulary

  • 计算一幅图像的BoW向量

    • 提取图像的局部特征

    • 统计Vocabulay中的每个视觉词汇visual word ,在图像中出现的频率。

https://img4.mukewang.com/5b8f3f3d0001b34f08960526.jpg

基于OpenCV的实现

基于OpenCV的原生实现

void siftDetecotor::extractFeatures(const std::vector<std::string> &imageFileList,std::vector<cv::Mat> &features)
{    int index = 1;    int count = 0;
    features.reserve(imageFileList.size());    auto size = imageFileList.size();    //size = 20;
    //#pragma omp parallel for
    for(size_t i = 0; i < size; i ++){        auto str = imageFileList[i];
        Mat des;
        siftDetecotor::extractFeatures(str,des);
        features.emplace_back(des); 
        count += des.rows;
        index ++ ;
    }  
    cout << "Extract #" << index << "# images features done!" << "Count of features:#" << count << endl;
}

传入imageFileList是图像的路径列表,vector<Mat> featurues返回提取得到的所有图像的特征。

  • 聚类,得到Vocabulary
    OpenCV中k-means聚类的接口如下:

double cv::kmeans    (    InputArray     data,    int     K,
    InputOutputArray     bestLabels,
    TermCriteria     criteria,    int     attempts,    int     flags,
    OutputArray     centers = noArray() 
)
  • data输入数据,每一行是一条数据。

  • k聚类的个数,这是就是Vocabulary的大小(词汇的个数u)。

  • bestLabels每一个输入的数据所属的聚类中心的index

  • criteria kmenas算法是迭代进行,这里表示迭代的终止条件。 可以是迭代的次数,或者是结果达到的精度,也可以是两者的结合,达到任一条件就结束。

  • attmepts 算法的次数,使用不同的初始化方法

  • flags 算法的初始化方法,可以选择随机初始化KMEANS_RANDOM_CENTERS,或者kmeans++的方法KMEANS_PP_CENTERS

  • centers 聚类的中心组成的矩阵。

得到图像库中图像的所有特征后,可以将这些特征组成一个大的矩阵输入到kmeans算法中,得到聚类中心,也就是Vocabulary

    Mat f;
    vconcat(features,f);
    vector<int> labes;
    kmeans(f,k,labes,TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,100,0.01),3,cv::KMEANS_PP_CENTERS,m_voc);

首先,使用vconcat将提取的特征点沿Y方向叠放在一起。k-means算法的终止条件是ermCriteria::COUNT + TermCriteria::EPS,100,0.01,算法迭代100次或者精度达到0.01就结束。

  • 图像的BoW编码
    得到Vocabulary后,统计视觉词汇在每个图像出现的概率就很容易得到图像的BoW编码

void Vocabulary::transform_bow(const cv::Mat &img,std::vector<int> bow)
{    auto fdetector = xfeatures2d:: SIFT ::create(0,3,0.2,10);    vector<KeyPoint> kpts;
    Mat des;
    fdetector->detectAndCompute(img,noArray(),kpts,des);

    Mat f;
    rootSift(des,f);    // Find the nearest center
    Ptr<FlannBasedMatcher> matcher = FlannBasedMatcher::create();    vector<DMatch> matches;
    matcher->match(f,m_voc,matches);

    bow = vector<int>(m_k,0);    // Frequency
    /*for( size_t i = 0; i < matches.size(); i++ ){
        int queryIdx = matches[i].queryIdx;
        int trainIdx = matches[i].trainIdx; // cluster index
        CV_Assert( queryIdx == (int)i );

        bow[trainIdx] ++; // Compute word frequency
    }*/

    // trainIdx => center index
    for_each(matches.begin(),matches.end(),[&bow](const DMatch &match){
        bow[match.trainIdx] ++; // Compute word frequency
    });
}

在查找图像的某个特征属于的聚类中心时,本质上就是查找最近的向量,可以使用flann建立索引树来查找;也可以使用一些特征匹配的方法,这里使用flannMatcher。统计每个词汇在图像中出现的频率,即可得到图像的BoW向量。

BoWTrainer

在OpenCV中封装了3个关于BoW的类。

抽象基类BOWTrainer,从图像库中的特征集中构建视觉词汇表Vobulary

class CV_EXPORTS_W BOWTrainer
{public:
    BOWTrainer();    virtual ~BOWTrainer();    CV_WRAP void add( const Mat& descriptors );
    CV_WRAP const std::vector<Mat>& getDescriptors() const;    CV_WRAP int descriptorsCount() const;    CV_WRAP virtual void clear();    CV_WRAP virtual Mat cluster() const = 0;    CV_WRAP virtual Mat cluster( const Mat& descriptors ) const = 0;protected:    std::vector<Mat> descriptors;    int size;
};

BOWKMeansTrainer基于k-means聚类,实现了BOWTrainer的方法。使用kmeans方法,从特征集中聚类得到视觉词汇表Vocabulary。其声明如下:

class CV_EXPORTS_W BOWKMeansTrainer : public BOWTrainer{public:    CV_WRAP BOWKMeansTrainer( int clusterCount, const TermCriteria& termcrit=TermCriteria(),                      int attempts=3, int flags=KMEANS_PP_CENTERS );    virtual ~BOWKMeansTrainer();    CV_WRAP virtual Mat cluster() const;    CV_WRAP virtual Mat cluster( const Mat& descriptors ) const;protected:    int clusterCount;
    TermCriteria termcrit;    int attempts;    int flags;
};

该类的使用也是很简单的,首先构建一个BOWKMeansTrainer的实例,其第一个参数clusterCount是聚类中心的个数,也就是Vocabulary的大小,余下的几个参数就是使用kmeans函数的参数,具体可参考上面的介绍。
然后,调用add方法,添加提取到的特征集。 添加特征集的时候,有两种方法:

for(int i=0; i<numOfPictures; i++)
    bowTraining.add( descriptors( i ) );

也可以提取好所有图像的特征,然后将特征合并为一个矩阵添加

    Mat feature_list;
    vconcat(features,feature_list);    BOWKMeansTrainer bow_trainer(k);
    bow_trainer.add(feature_list);

添加图像特征后,调用

vocabulary =  bow_trainer.cluster();

对特征集进行聚类,得到的聚类中心就是所要求的视觉词汇表Vocabulary

在得到Vocabulary后,就可以对一副图像进行编码,使用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:
    Mat vocabulary;
    Ptr<DescriptorExtractor> dextractor;
    Ptr<DescriptorMatcher> dmatcher;

该类实现了一下三个功能:

  • 根据相应的Extractor提取图像的特征

  • 找到距离每个特征最近的visual word

  • 计算图像的BoW表示,并且将其进归一化。

要实例化一个BOWImgDescriptorExtractor,需要提供三个参数

  • 视觉词汇表Vocabulalry

  • 图像特征提取器 DescriptorExtractor

  • 特征匹配的方法 descriptorMatcher ,用来查找和某个特征最近的visual word。

其使用也很便利,使用ExtractorMatcher实例化一个BOWImgDescriptorExtractor,然后设置Vocabulary

BOWImgDescriptorExtractor bowDE(extractor, matcher);
bowDE.setVocabulary(dictionary); //dictionary是通过前面聚类得到的词典;

要求某图像的BoW,可以调用compute方法

bowDE.compute(img, keypoints, bow);

Summary

BOWKMeansTrainer 对提取到的图像特征集进行聚类,得到视觉词汇表Vocabulary
BOWImgDescriptorExtractor 在得到视觉词汇表后,使用该类,可以很方便的对图像进行BoW编码。


作者:慕婉清6462132
链接:http://www.imooc.com/article/74483
来源:慕课网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值