FDDB evaluate生成ROC数据方法(源码剖析)

测试人脸检测算法性能使用FDDB数据库中途遇到的两个问题,加以记录

问题一. 怎么从detector生成的数据通过评估程序生成ROC曲线

    网上已经太多资源了,基本照着修改编译一下就通过了,有空就再整,没空就先空着。。


问题二. 生成的ROC曲线,两个数据Discrete ROC和Continuous ROC有什么意义,是怎么得到的,有什么区别和联系

首先,我们通过官方评估程序,看一下这两个数据是怎么生成的。

    从evaluate.cpp可以看出,评估方法是分别统计每一张图片在不同阈值下的FP,TP等数据,并将结果整合到最终的结果。

   (a) 对detection score进行排序并去除相同的score,得到一个经过排序的unique的score数组uniqueScores

// find the unique values for detection scores
vector<double> *uniqueScores = det->getUniqueScores();
std::vector<double> * RegionsSingleImage::getUniqueScores(){
  vector<double> *v = new vector<double>;
  v->reserve(list->size());
  for(unsigned int i=0; i<list->size(); i++)
    v->push_back(list->at(i)->detScore);

  sort(v->begin(), v->end()); // 排序
  vector<double>::iterator uniElem = unique(v->begin(), v->end()); // //"删除"相邻的重复元素
  v->erase(uniElem, v->end()); // 删除(真正的删除)重复的元素  
  return v;
}
注:unique函数功能是去除相邻的重复元素,注意是相邻,所以必须先使用sort函数。还有一个容易忽视的特性是它并不真正把重复的元素删除。之所以说比不真正把重复的元素删除,因为unique实际上并没有删除任何元素,而是将无重复的元素复制到序列的前段,从而覆盖相邻的重复元素。unique返回的迭代器指向超出无重复的元素范围末端的下一个位置。

   (b)对于升序排列好的uniqueScores中的每一个值,设置为当前阈值,并按此阈值筛选当前所有的检测结果

   (c)计算检测出的人脸与ground true中的人脸的IOU

   (d)根据(b)、(c)的结果统计当前图片在不同阈值下的结果

            -- N            --即为Gound true中的人脸个数

            -- FP           --False positives -- discrete

            -- TPCont   -- True positives -- continuous

            -- TPDisc    -- True positives -- discrete

for(vector<double>::iterator uit=uniqueScores->begin(); uit != uniqueScores->end(); ++uit)
{
    //  (b) 比较当前的阈值,通过设置一个flags来指定哪些detection的结果会被使用
    double scoreThreshold = *uit;
    for(unsigned di =0; di<det->length(); di++)
    {
        Region *rd = det->get(di);  // 返回一个指向第di个区域的指针
	rd->setValid( rd->detScore >= scoreThreshold ); // 每一次阈值变化都要比对一遍
    }
			  
    //  (c) 计算检测出的人脸区域和Ground True区域的匹配score,这里比较复杂,可以不管,当作两个区域的IOU即可
    vector<MatchPair *> *mps = M->getMatchPairs(); 

    //  (d) 计算当前阈值下的FP,TPCont,TPDisc等,并将结果保存到imageResults中
    Results *r = new Results(imName, scoreThreshold, mps, annot, det);
    imageResults->push_back(r);
}

Results::Results(string s, double scoreThresh, vector<MatchPair *> *mp, RegionsSingleImage *annot, RegionsSingleImage *det){
  imName = s;
  scoreThreshold = scoreThresh;

  N = annot->length();

  FP = 0;
  for(unsigned int i=0; i< det->length(); i++)
    if( (det->get(i))->isValid() )  // 所有detection score大于当前阈值的isValid()都为True
      FP++;

  TPCont = 0;
  TPDisc = 0;
  if(mp)
    for(unsigned int i=0; i< mp->size(); i++){
      double score = mp->at(i)->score;  // score of MatchPair(IOU)
      TPCont += score;  //对于TPCont,只要检测置信度大于阈值,就累加当前的Match Score(IOU),不管是否大于0.5
      if( score>0.5 ) 
      {                 // 对于TPDisc,在满足TPCont的基础上,也要满足Match Score(IOU)> 0.5,它统计的是满足条件的个数
	TPDisc++; 
	FP--;
      }
    }
}

    (e)将(d)中计算的当前图片的结果合并到总的结果中

// merge the list of results for this image (imageResults) with the global list (cumRes)
    vector<Results *> *mergedRes = dummyRes->merge(cumRes, imageResults);

    (f)将(e)中计算的结果输出到文件中

// save the ROC-curve computed from the cumulative statistics 
  dummyRes->saveROC(rocFilePrefix, cumRes);
void Results::saveROC(string outFile, vector<Results *> *rv){
  string s = outFile + "ContROC.txt";
  ofstream osc(s.c_str());

  s = outFile + "DiscROC.txt";
  ofstream osd(s.c_str());

  for(unsigned int i=0; i< rv->size(); i++)
  {
    Results *r = rv->at(i);
    if(r->N)
    {
      osc << (r->TPCont / r->N) << " " << r->FP << endl;
      osd << (r->TPDisc / r->N) << " " << r->FP << " " << r->scoreThreshold<< endl;
    }
    else
    {
      osc << "0 0" << endl;
      osd << "0 0 " <<  r->scoreThreshold<< endl;
    }
  }
  osc.close();
  osd.close();
}
可以看出,生成的DiscROC.txt中第一列为TPR(根据TPDisc算出的),第二列为FP,第三列为当前的阈值;而ConROC.txt中第一列为TPR(根据TPCont 算出的),第二列为FP。

代码看完,问题二自然没有疑问。


参考资料:

    1. unique函数 https://blog.csdn.net/hellokandy/article/details/51317593

猜你喜欢

转载自blog.csdn.net/xcls2010/article/details/79744507