caffe-ssd中非极大值抑制NMS的原理和实现方法

看论文觉得很好理解,但是一看源码,瞬间懵了,很正常的想法,到代码上就看不懂了,结合了下面博文讲解,终于看懂了caffe-ssd中nms的代码。

https://blog.csdn.net/HappyRocking/article/details/79970627

源码:

void ApplyNMSFast(const vector<NormalizedBBox>& bboxes,
      const vector<float>& scores, const float score_threshold,
      const float nms_threshold, const float eta, const int top_k,
      vector<int>* indices) {
  // Sanity check.  使用的是这个版本的重定义
  CHECK_EQ(bboxes.size(), scores.size())
      << "bboxes and scores have different size.";

  // Get top_k scores (with corresponding indices).
  vector<pair<float, int> > score_index_vec;
  //score_threshold 设置好的conf_threshold,找到得分最高的前top_k个
  GetMaxScoreIndex(scores, score_threshold, top_k, &score_index_vec);

  // Do nms.
  float adaptive_threshold = nms_threshold;
  indices->clear();
  while (score_index_vec.size() != 0) {
  // score_index_vec存放的是map<scores, idx>
  // idx是最大的那个得分的框的序号
    const int idx = score_index_vec.front().second;
    bool keep = true;
    // 刚开始的时候,indices->size()=0,也就是直接把最大的放进去
    for (int k = 0; k < indices->size(); ++k) {
      if (keep) {
        const int kept_idx = (*indices)[k];
        float overlap = JaccardOverlap(bboxes[idx], bboxes[kept_idx]);
        // 对于本次的这个得分最大值序号idx,只要其他候选框的重叠度都小于adaptive_threshold
        // 那就证明不是同一个目标,那就保留这个局部最大值,直到把得分top_k的框都遍历一遍
        keep = overlap <= adaptive_threshold;
      } else {
      // 否则就不保留这个极大值。
        break;
      }
    }
    if (keep) { // 如果把所有的框都遍历一遍,没有跟idx有重叠超过阈值的,说明它是局部极大值,保留。
    // 第一次肯定是把最大的放进去,然后
      indices->push_back(idx);
    }
    // 然后将最大的那个去掉
    score_index_vec.erase(score_index_vec.begin());
    if (keep && eta < 1 && adaptive_threshold > 0.5) {
      adaptive_threshold *= eta;
    }
  }
}

中间标的注释描述不太到位,建议大家先看开头推荐的博文。

个人感觉NMS就是个寻找聚类最中心的值的问题。像推荐博文中举的例子,一张图片中有一类目标:人脸,有两个instance:jack and rose。那他俩的脸肯定不能直接选得分最高的那个,不然jack就没了。这里面的5个框对应两个聚类中心jack and rose,NMS的任务就是把两个聚类里面得分最高的给找出来。我们人一眼就能看出来,但是计算机必须通过计算。

结合代码,函数首先通过GetMaxScoreIndex(scores, score_threshold, top_k, &score_index_vec)这个函数将得分前top_k做成map_pair<score, index>,这里score就是预测某一类目标的第index个框的得分,index的排序是在产生prior box时由那6层feature map对应的priorbox经过flatten和concatenate后的排序,这个不重要,重要的是index跟那8732个框一一对应,就可以了。通过这个函数得到的score_index_vec是经过排序的,也就是其score是从大到小排列的,共top_k=400个(默认参数)。然后进入while(score_index_vec.size() != 0)这个循环,先不去管为啥不等于就得一直进行,先看idx,idx首先得到的是得分最高的那个index,那必然是其中某一个聚类的最中心点,它必须保留(类似于推荐博文中的rose那个得分0.9多的那个框,那个必须保留)。代码也是这么实现的,令keep=1,第一次循环时,indices是个空的vector,因此, 循环等效为:for (int k = 0; k < 0; ++k) {。。。}里面的代码不执行,后面keep依然是1,那顺理成章的把idx推进indices 里面了:indices->push_back(idx);然后把idx从score_index_vec里面清掉。下面的if应该是个自适应阈值的衰减系数,暂且不理会。

当第一个循环搞定后,后面的就是从得分第二的开始,跟已经成为聚类中心的比较,如果重叠率高于既定阈值,那就将其erase。如果得分第二的这个index跟得分最高的重叠率也不超过阈值,那证明他是另外一个聚类中心,也就是第二Instance(类似于博文中的jack,当然jack那个中心的框并不是得分第二高的)。

非极大值抑制抑制的思想较为简单,但是代码实现还是很巧妙的,值得学习。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值