【问题标题】:OpenCV C++ Bag Of WordsOpenCV C++ 词袋
【发布时间】:2015-01-21 12:32:33
【问题描述】:

在网上的任何地方,你都可以找到关于弓的不同部分的小教程,但是(无论如何我发现)没有关于你之后做什么:

bowDE.setVocabulary(dictionary);

...

bowDE.compute(image, keypoints, descriptors);

一旦你使用了BOWImgDescriptorExtractorcompute,你会怎么做?

你如何找出什么是好的匹配,什么不是?

然后你可以利用这些信息吗?

如果有,怎么做?

【问题讨论】:

    标签: c++ opencv surf


    【解决方案1】:

    如果您同时拥有描述符和提取器,则可以使用匹配器来查找匹配项。

    这是一个示例函数:

    void drawMatches(const Mat& Img1,const Mat& Img2,const vector<KeyPoint>& Keypoints1,
        const vector<KeyPoint>& Keypoints2,const Mat& Descriptors1,const Mat& Descriptors2)
    {
        Ptr<DescriptorMatcher> descriptorMatcher = DescriptorMatcher::create( "BruteForce" ); //
        vector<DMatch> matches;
        descriptorMatcher->match( Descriptors1, Descriptors2, matches );
        Mat matchImg;
        drawMatches(Img1,Keypoints1,Img2,Keypoints2,matches,matchImg,Scalar::all(-1),CV_RGB(255,255,255),Mat(),4);
        imshow("match",show);
    }
    

    获得这些匹配后,您可以通过检查它们的最大距离、平均距离、总匹配大小等来确定哪些匹配是“好”的。

    还有一个官方教程介绍了如何使用这些描述符和关键点来获取匹配项

    Features2D + Homography to find a known object

    虽然它使用的特征检测器与您的不同,但您仍然可以使用文章的匹配部分。

    更新:

    没有办法对匹配是否为“正确”匹配做出准确的回答。但是你可以得到匹配对的值。

    这是一个使用 SIFT 特征检测器和 BruteForce 匹配器的“错误”匹配和“正确”匹配示例。

    部分代码:

    size_t matches_size = matches.size();
    for( unsigned i = 0; i < matches_size; i++ )
    {
        if( matches[i].distance < MY_GOOD_DISTANCE)//You can get the matching distance like this.
        {
            good_matches.push_back( matches[i]); 
        }
    }
    

    这是一个正确的匹配。

    计算完匹配后,我列出了匹配的距离:

    27.7669 43.715  45.2217 47.4552 53.1601 54.074  57.3672 58.2924 59.0593 63.3009 
    63.6475 64.1093 64.8922 67.0075 70.9718 73.4507 74.0878 76.6225 76.6551 80.075  
    81.2219 82.2192 83.6959 89.2412 90.7855 91.4604 95.3363 95.352  95.6033 98.209  
    98.3362 98.3412 99.4082 101.035 104.024 109.567 110.095 110.345 112.858 118.339 
    119.311 123.976 125.948 126.625 128.02  128.269 130.219 133.015 135.739 138.43  
    144.499 146.055 146.492 147.054 152.925 160.044 161.165 168.899 170.871 179.881 
    183.39  183.573 187.061 192.764 192.961 194.268 194.44  196.489 202.255 204.854 
    230.643 230.92  231.961 233.238 235.253 236.023 244.225 246.337 253.829 260.384 
    261.383 263.934 266.933 269.232 272.586 273.651 283.891 289.261 291.805 297.165 
    297.22  297.627 304.132 307.633 307.695 314.798 325.294 334.74  335.272 344.17  
    352.095 353.456 354.144 357.398 363.762 366.344 367.301 368.977 371.102 371.44  
    371.863 372.459 372.85  373.17  376.082 378.844 382.372 389.01  389.704 397.028 
    398.236 400.53  414.523 417.628 422.61  430.731 461.3   
    
    Min value: 27.76
    Max value: 461.3
    Average: 210.2526882
    

    这是一个错误的匹配:

    336.161 437.132 310.587 376.245 368.683 449.708 334.148 354.79  333.981 399.794 368.889 
    361.653 341.778 266.443 259.365 338.726 352.789 381.097 427.143 350.732 355.522 349.819 
    358.569 373.139 348.201 341.923 383.188 378.233 399.844 294.16  505.107 347.978 314.021 
    332.983 335.364 403.217 385.8   408.859 381.472 372.078 434.167 436.489 279.646 253.271 
    268.522 376.303 418.071 373.3   369.004 272.145 254.448 408.185 326.351 351.886 333.981 
    371.59  440.336 230.558 250.928 337.368 288.579 262.107 409.971 339.391 380.58  374.162 
    361.96  392.59  345.936 328.691 383.586 398.986 336.283 365.768 492.984 392.379 377.042 
    371.652 279.014 370.849 378.213 351.048 311.148 319.168 324.268 319.191 261.555 339.257 
    298.572 241.622 406.977 286.068 438.586 
    
    Min value: 230
    Max value: 505
    Average: 352.6009711
    

    得到所有匹配的距离后,你可以很容易地看出什么是“好”匹配,什么是“坏”匹配。

    这是得分部分。有点棘手,与数据高度相关。 MY_AVG_DISTANCE、MY_LEAST_DISTANCE、MY_MAX_DISTANCE 和 MY_GOOD_DISTANCE 是您应该仔细选择的值。检查您自己的匹配距离,并为它们选择一些值。

    int good_size = good_matches.size() > 30 ? 30 : good_matches.size(); //In case there are too many "good matches"
    //...
    //===========SCORE ============
    double avg = 0;     //Calculates the average of some of the matches. 
    int avgCount = 0;
    int goodCount = 0 ;
    for( unsigned i = 0; i < matches.size(); i++ )
    {
        double dist = matches[i].distance;
        if( dist < MY_AVG_DISTANCE  && dist > MY_LEAST_DISTANCE )
        {
            avg += dist;
            avgCount++;
        }
        if(dist < MY_GOOD_DISTANCE && dist > MY_LEAST_DISTANCE ){
            goodCount++;
        }
    }
    if(avgCount > 6){
        avg /= avgCount;
        if(goodCount < 12){
            avg = avg + (12-goodCount) * 4;
        }
    }else{
        avg = MY_MAX_DISTANCE;
    }
    
    avg = avg > MY_AVG_DISTANCE ? MY_AVG_DISTANCE : avg;
    avg = avg < MY_MIN_DISTANCE ? MY_MIN_DISTANCE : avg;
    
    double score_avg = (MY_AVG_DISTANCE - avg) / ( MY_AVG_DISTANCE - MY_MIN_DISTANCE ) * 100;
    if(formsHomography){ //Some bonus...not related with your matching method, but you can adopt something like this
        score_avg += 40;
        score_avg = score_avg > 100 ? 100 : score_avg;
    }else{
        score_avg -= 5;
        score_avg = score_avg < 0 ? 0 : score_avg;
    }
    return score_avg;
    

    【讨论】:

    • 但是通过检查“最大距离、平均距离、总匹配大小”,您只有一个好的估计。这将需要对结果进行手动检查以确保正确性。此外,您的示例要求每次都加载每个图像,而不是每个图像都已经加载,并且已经提取了它的描述符。虽然,是否可以将每个图像数据保存到 Mat,将其保存到文件中,调用该文件并将其重新应用到新的 Mat,然后针对给定的每个图像运行该文件?
    • 然而,良好匹配的主要决定因素是什么?我想您必须为每个图像创建一个标签数组作为可读输出,以便轻松确定 Good 匹配项。编辑——我知道你说的是 "max distance, average distance, total match size" ,但我要设置这些吗?如果是这样,具体是什么?
    • 对问题1:只有一个“估计”,如果你想要一个准确的“是”或“否”,也许没有这样的功能可以为你提供这样的答案。
    • 对问题2:是的,您可以将它们存储在本地文件中。稍后我将编辑我的答案以向您展示代码。
    • 您如何阅读vector&lt;DMatch&gt; 来查看哪些图片好,哪些不好?
    【解决方案2】:

    您可以找到simple implementation of bag of words in C++ here。所以你不需要依赖OpenCV。

    class Statistics {
      std::unordered_map<std::string, int64_t> _counts;
      int64_t _totWords;
    
      void process(std::string& token);
    public:
      explicit Statistics(const std::string& text);
    
      double Dist(const Statistics& fellow) const;
    
      bool IsEmpty() const { return _totWords == 0; }
    };
    
    namespace {
      const std::string gPunctStr = ".,;:!?";
      const std::unordered_set<char> gPunctSet(gPunctStr.begin(), gPunctStr.end());
    }
    
    Statistics::Statistics(const std::string& text) {
      std::string lastToken;
      for (size_t i = 0; i < text.size(); i++) {
        int ch = static_cast<uint8_t>(text[i]);
        if (!isspace(ch)) {
          lastToken.push_back(tolower(ch));
          continue;
        }
        process(lastToken);
      }
      process(lastToken);
    }
    
    void Statistics::process(std::string& token) {
      do {
        if (token.size() == 0) {
          break;
        }
        if (gPunctSet.find(token.back()) != gPunctSet.end()) {
          token.pop_back();
        }
      } while (false);
      if (token.size() != 0) {
        auto it = _counts.find(token);
        if (it == _counts.end()) {
          _counts.emplace(token, 1);
        }
        else {
          it->second++;
        }
        _totWords++;
        token.clear();
      }
    }
    
    double Statistics::Dist(const Statistics& fellow) const {
      double sum = 0;
      for (const auto& wordInfo : _counts) {
        const std::string wordText = wordInfo.first;
        const double freq = double(wordInfo.second) / _totWords;
        auto it = fellow._counts.find(wordText);
        double fellowFreq;
        if (it == fellow._counts.end()) {
          fellowFreq = 0;
        }
        else {
          fellowFreq = double(it->second) / fellow._totWords;
        }
        const double d = freq - fellowFreq;
        sum += d * d;
      }
      return std::sqrt(sum);
    }
    

    【讨论】:

      猜你喜欢
      • 2014-12-15
      • 2013-11-02
      • 2015-02-15
      • 2012-07-21
      • 1970-01-01
      • 2016-06-16
      • 1970-01-01
      • 1970-01-01
      • 2011-02-20
      相关资源
      最近更新 更多