测试人脸检测算法性能使用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