KITTI 3D目标检测离线评估工具包说明

本文是KITTI 3D目标检测离线评估工具包的使用说明和相关代码学习文件,从这里可以下载。更新于2018.09.20。

文章目录

  • KITTI 3D目标检测离线评估工具包说明
    • 工具包README文件
    • 代码学习
      • evaluate_object_3d_offline.cpp
        • 主函数
        • eval
        • tBox\tGroundtruth\tDetection
        • eval_class
        • saveAndPlotPlots
        • computeStatistics

工具包README文件

这个工具包是离线运行的,可以在使用者的电脑上评估验证集(从KITTI训练集中选出来的)。评估的指标包括:

  • 重叠率:overlap on image (AP)
  • 旋转重叠率:oriented overlap on image (AOS)
  • 地面重叠率(鸟瞰视角):overlap on ground-plane (AP)
  • 3D重叠率:overlap in 3D (AP)

首先在终端编译evaluate_object_3d_offline.cpp文件,之后运行评估命令:

./evaluate_object_3d_offline groundtruth_dir result_dir

需要注意的是,使用者并不需要评估整个KITTI训练集。Evaluator只评估有结果存在的那些样本。

代码学习

这一部分主要是希望通过学习代码理解所得到的结果和图像,并不深究其中的语法。

总结:

  • 这个函数主要用于评估实验结果,但是评估过程中并未评估所有的结果,而是挑选了置信概率最大的前几个结果(程序中默认取前41个),函数计算了precision和recall并画出二者的关系曲线(关于这两个算法评估概念可以参看这里的说明)。
  • 评估算法是按照类别判断的,对于KITTI库分为3类(人、车、自行车),每个类别中有不同难度(简单、中等、困难),曲线是每个类别对应一个曲线图,图中包括三种难度下算法的评估结果曲线。
  • 算法中还将评估分为2D评估、鸟瞰评估、3D评估三种不同角度,其中2D评估可以有带转角的评估AOS,另外两种则不评估此项。
  • 结果或真值数据的存储格式应当遵从这个顺序:目标类型(人、车、自行车对应的字符串),是否截断(失效值-1),是否遮挡(无遮挡、部分遮挡、全部遮挡)(失效值-1),旋转角度(失效值-10),左上角坐标x1,左上角坐标y1,右下角坐标x2,右下角坐标y2,高,宽,长,box中心坐标t1,box中心坐标t2,box中心坐标t3,box朝向ry,阈值(score)。其中,真值数据不具有最后一项,结果数据是否截断、是否遮挡对应数据无效。
  • t*在函数toPolygon中用到了,但是含义不清楚;ry的含义也不清楚。

evaluate_object_3d_offline.cpp

这一部分记录了evaluate_object_3d_offline.cpp文件的学习笔记,包括其中的主函数、用于评估总过程的eval函数、定义存储信息含义的结构体tBox\tGroundtruth\tDetection、具体实施按类评估的eval_class、和用于存储结果并画出图像的saveAndPlotPlots。

主函数

主导整个评估过程。

int32_t main (int32_t argc,char *argv[]) {// 需要2或4个输入//如果输入个数为3,显示用法并返回if (argc!=3) { cout << "Usage: ./eval_detection_3d_offline gt_dir result_dir" << endl;return 1;  return 1;}//读取输入string gt_dir = argv[1];          //第一个输入是真值路径string result_dir = argv[2];   //第二个输入是结果路径//定义用于提示的邮件地址Mail *mail;mail = new Mail();mail->msg("Thank you for participating in our evaluation!");//运行评估过程//如果评估过程成功,有邮箱地址就将结果链接发送到邮箱,没有就保存在本地plot下//否则,返回错误信息并删除结果目录if (eval(gt_dir, result_dir, mail)) {mail->msg("Your evaluation results are available at:");mail->msg(result_dir.c_str());} else {system(("rm -r " + result_dir + "/plot").c_str());mail->msg("An error occured while processing your results.");} //发送邮件并退出delete mail;return 0;
}

eval

从主函数中可以看到,起评估作用的是eval函数,下面贴出eval函数和学习说明:

bool eval(string gt_dir, string result_dir, Mail* mail){//设置全局变量CLASS_NAMES,其中包括car, pedestrain, cyclistinitGlobals();// 真值和结果路径:// string gt_dir         = "data/object/label_2";  真值路径// string result_dir     = "results/" + result_sha; 结果路径//保存eval结果图的路径string plot_dir       = result_dir + "/plot";// 按照上面定义的plot路径创建输出目录system(("mkdir " + plot_dir).c_str()); //定义了两个二维数组groundtruth和detections,用于存储真值和检测结果//定义了一个名为groundtruth的二维数组,其中每个位置上的数据类型是tGroundtruth,其中存有box的类型、两组对角坐标(左上、右下)、图像转角等信息。具体见tBox和tGroundtruth说明。vector< vector<tGroundtruth> > groundtruth; //参考上面真值定义vector< vector<tDetection> >   detections;//存储是否计算旋转重叠率AOS(在加载检测结果的时候可能被设成false),并记录本次提交都包含哪些labels//默认计算AOS(仅对于2D评估)bool compute_aos=true; //定义eval_image,存储bool变量,默认值为false,长度为3vector<bool> eval_image(NUM_CLASS, false);vector<bool> eval_ground(NUM_CLASS, false);vector<bool> eval_3d(NUM_CLASS, false);// 读取所有图像的真值和检测结果mail->msg("Loading detections...");//存储所有有结果的图像编号std::vector<int32_t> indices = getEvalIndices(result_dir + "/data/");printf("number of files for evaluation: %d\n", (int)indices.size()); //对于所有图像,读取真值并检查是否都数据读取成功for (int32_t i=0; i<indices.size(); i++) { // 生成文件名 //定义一个长为256的字符串,叫file_namechar file_name[256];sprintf(file_name,"%06d.txt",indices.at(i));//读取真值和结果(result poses)bool gt_success,det_success;       //定义变量用于存储是否读取成功//读取所有图片的真值,每个图片15个值(具体参见tGroundtruth),存入gt(用push_back一组接一组)vector<tGroundtruth> gt   = loadGroundtruth(gt_dir + "/" + file_name,gt_success); //读取检测结果,共16个值(最后一个为score),如果转角的值(第4个)为-10,则不计算AOSvector<tDetection>   det  = loadDetections(result_dir + "/data/" + file_name,   compute_aos, eval_image, eval_ground, eval_3d, det_success); groundtruth.push_back(gt);            //将gt存入groundtruth,也就是直到此时才给之前定义的goundtruth赋值detections.push_back(det);          //将det存入detections//检查是否有读取失败,如果有,输出提示并返回if (!gt_success) { mail->msg("ERROR: Couldn't read: %s of ground truth. Please write me an email!", file_name);return false;}if (!det_success) {mail->msg("ERROR: Couldn't read: %s", file_name);return false;}} mail->msg("  done.");// 定义指向结果文件的指针FILE *fp_det=0, *fp_ori=0;        //FILE是定义在C++标准库中的一个结构体,以指针的方式存储与内存中,其内容描述了一个文件//对于所有类别评估2D窗口for (int c = 0; c < NUM_CLASS; c++) {CLASSES cls = (CLASSES)c;      //找到序号对应的类别(此时cls的值为CAR、PEDESTRAIN或CYCLIST)if (eval_image[c]) {           //如果存在这一类别的图像(在loadDetections里面判断了)才计算fp_det = fopen((result_dir + "/stats_" + CLASS_NAMES[c] + "_detection.txt").c_str(), "w");            //让fp_det指针指向用于存储结果的文件if(compute_aos)               //如果需要计算AOS,就让fp_ori指向用于存储AOS的文件(这里默认不计算)fp_ori = fopen((result_dir + "/stats_" + CLASS_NAMES[c] + "_orientation.txt").c_str(),"w");vector<double> precision[3], aos[3];            //定义两个长度为3的容器(对应简单、中等、困难三个级别),分别用于存储准确率和AOS//如果有任意一个难度计算失败,则返回提示并退出(具体计算过程见eval_class说明)if(   !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, imageBoxOverlap, precision[0], aos[0], EASY, IMAGE)|| !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, imageBoxOverlap, precision[1], aos[1], MODERATE, IMAGE)|| !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, imageBoxOverlap, precision[2], aos[2], HARD, IMAGE)) {mail->msg("%s evaluation failed.", CLASS_NAMES[c].c_str());return false;}fclose(fp_det);           //关闭detection的存储文件saveAndPlotPlots(plot_dir, CLASS_NAMES[c] + "_detection", CLASS_NAMES[c], precision, 0);           //画出曲线图(具体见saveAndPlotPlots说明)if(compute_aos){            //如果需要计算AOSsaveAndPlotPlots(plot_dir, CLASS_NAMES[c] + "_orientation", CLASS_NAMES[c], aos, 1); //画出AOS曲线fclose(fp_ori);}}}printf("Finished 2D bounding box eval.\n");             //结束2D评估//对于鸟瞰图和3D box不要计算AOScompute_aos = false;//对于所有类别评估鸟瞰角度的bounding boxfor (int c = 0; c < NUM_CLASS; c++) {CLASSES cls = (CLASSES)c;if (eval_ground[c]) {             //如果存在该类型的图片才计算fp_det = fopen((result_dir + "/stats_" + CLASS_NAMES[c] + "_detection_ground.txt").c_str(), "w");              //将指针指向用于存储鸟瞰结果的文件vector<double> precision[3], aos[3];            //同2D,分别用于存储简单、中等和困难的情况printf("Going to eval ground for class: %s\n", CLASS_NAMES[c].c_str());//如果任意一个难度评估出错,提示并返回if(   !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, groundBoxOverlap, precision[0], aos[0], EASY, GROUND)|| !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, groundBoxOverlap, precision[1], aos[1], MODERATE, GROUND)|| !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, groundBoxOverlap, precision[2], aos[2], HARD, GROUND)) {mail->msg("%s evaluation failed.", CLASS_NAMES[c].c_str());return false;}fclose(fp_det);saveAndPlotPlots(plot_dir, CLASS_NAMES[c] + "_detection_ground", CLASS_NAMES[c], precision, 0);             //画出评估图像(具体参见saveAndPlotPlots说明)}}printf("Finished Birdeye eval.\n");          //结束鸟瞰评估//对于所有类别评估3D bounding boxesfor (int c = 0; c < NUM_CLASS; c++) { CLASSES cls = (CLASSES)c;if (eval_3d[c]) {           //如果评估3D结果fp_det = fopen((result_dir + "/stats_" + CLASS_NAMES[c] + "_detection_3d.txt").c_str(), "w");          //指针指向保存3D评估结果的文件vector<double> precision[3], aos[3];//如果任意一个难度评估出错,则提示并返回printf("Going to eval 3D box for class: %s\n", CLASS_NAMES[c].c_str());if(   !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, box3DOverlap, precision[0], aos[0], EASY, BOX3D)|| !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, box3DOverlap, precision[1], aos[1], MODERATE, BOX3D)|| !eval_class(fp_det, fp_ori, cls, groundtruth, detections, compute_aos, box3DOverlap, precision[2], aos[2], HARD, BOX3D)) {mail->msg("%s evaluation failed.", CLASS_NAMES[c].c_str());return false;}fclose(fp_det);saveAndPlotPlots(plot_dir, CLASS_NAMES[c] + "_detection_3d", CLASS_NAMES[c], precision, 0); CLASS_NAMES[c], precision, 0);}}printf("Finished 3D bounding box eval.\n");// 成功完成评估,返回truereturn true;
}

tBox\tGroundtruth\tDetection

tBox、tGroundtruth和tDetection结构体定义(用于存储真值和检测结果,eval代码中用到):

// holding bounding boxes for ground truth and detections
struct tBox {string  type;     // 存储目标类型double   x1;     double   y1;      //与x1共同定位左上角坐标double   x2;      double   y2;      //与x2共同定位右下角坐标double   alpha;   //图像转角tBox (string type, double x1,double y1,double x2,double y2,double alpha) : //定义与结构体同名的构造函数,在调用时赋值type(type),x1(x1),y1(y1),x2(x2),y2(y2),alpha(alpha) {}
};//存储真值
struct tGroundtruth {tBox    box;        //存储目标类型、box、朝向double  truncation; // truncation 0..1 这个目前还不理解干什么用的int32_t occlusion;  // 是否遮挡,0代表无遮挡,1代表部分遮挡,2代表完全遮挡double ry;  //目前未知含义double  t1, t2, t3; //目前未知含义double h, w, l; //高、宽、长tGroundtruth () : //这是这个结构体内所包含的同名构造函数,在调用时赋值box(tBox("invalild",-1,-1,-1,-1,-10)),truncation(-1),occlusion(-1) {}  tGroundtruth (tBox box,double truncation,int32_t occlusion) :box(box),truncation(truncation),occlusion(occlusion) {}   tGroundtruth (string type,double x1,double y1,double x2,double y2,double alpha,double truncation,int32_t occlusion) :box(tBox(type,x1,y1,x2,y2,alpha)),truncation(truncation),occlusion(occlusion) {}
};//存储检测结果
struct tDetection {tBox    box;    //存储目标类型、box、朝向double  thresh; //检测概率(detection score)double  ry;  //目前未知含义double  t1, t2, t3;  //目前未知含义double  h, w, l;  //高、宽、长tDetection (): //定义与结构体同名的构造函数,在调用时赋值box(tBox("invalid",-1,-1,-1,-1,-10)),thresh(-1000) {}    tDetection (tBox box,double thresh) :box(box),thresh(thresh) {}tDetection (string type,double x1,double y1,double x2,double y2,double alpha,double thresh) :box(tBox(type,x1,y1,x2,y2,alpha)),thresh(thresh) {}
};

总结:
基本存储内容和顺序为:类型、左上角x、左上角y、右下角x、右下角y、朝向(角度);如果是真值那么后面会加上:截断信息(truncation)、遮挡情况;如果是检测结果后面会加上:检测概率(score)。


eval_class

eval_class代码部分:

bool eval_class (FILE *fp_det, FILE *fp_ori, CLASSES current_class,const vector< vector<tGroundtruth> > &groundtruth,const vector< vector<tDetection> > &detections, bool compute_aos,double (*boxoverlap)(tDetection, tGroundtruth, int32_t),vector<double> &precision, vector<double> &aos,DIFFICULTY difficulty, METRIC metric) {
assert(groundtruth.size() == detections.size());// 初始化int32_t n_gt=0;                                     // 真值图像总数(recall的分母)vector<double> v, thresholds;                       //用于存储检测得到的概率detection scores,对其评估的结果用于recall的离散化vector< vector<int32_t> > ignored_gt, ignored_det;  //用于存储对于当前类别/难度忽略的图像标号vector< vector<tGroundtruth> > dontcare;            //用于存储在真值中包含的不关心区域的编号//对于所有待测试图像进行:for (int32_t i=0; i<groundtruth.size(); i++){//用于保存当前帧中的忽略的真值、检测结果和不关心区域vector<int32_t> i_gt, i_det;vector<tGroundtruth> dc;//只评估当前类别下的目标(忽略遮挡、截断的目标)cleanData(current_class, groundtruth[i], detections[i], i_gt, dc, i_det, n_gt, difficulty);ignored_gt.push_back(i_gt);ignored_det.push_back(i_det);dontcare.push_back(dc);//计算数据以得到recall的值 tPrData pr_tmp = tPrData();          //用于存储相似度、true positives、false positives和false negativespr_tmp = computeStatistics(current_class, groundtruth[i], detections[i], dc, i_gt, i_det, false, boxoverlap, metric);              //具体分析见ComputeStatistics说明,输出为tPrData类型//将所有图片的detection scores存入向量for(int32_t j=0; j<pr_tmp.v.size(); j++)v.push_back(pr_tmp.v[j]);}//获取对于recall离散化必须要评估的scores(当不再满足(r_recall-current_recall) < (current_recall-l_recall)时就退出,在退出前,每有一个满足的current_recall就加1/40(数值认人为规定)),返回的是排名前40的满足条件的scores。thresholds = getThresholds(v, n_gt);//计算相关scores的TP、FP和FNvector<tPrData> pr;pr.assign(thresholds.size(),tPrData());for (int32_t i=0; i<groundtruth.size(); i++){//对于所有的scores/recall thresholds做如下操作:for(int32_t t=0; t<thresholds.size(); t++){tPrData tmp = tPrData();tmp = computeStatistics(current_class, groundtruth[i], detections[i], dontcare[i],ignored_gt[i], ignored_det[i], true, boxoverlap, metric,compute_aos, thresholds[t], t==38);          //具体分析见ComputeStatistics说明,输出为tPrData类型//将当前帧下TP、FP、FN和AOS的数值加到当前阈值下的总评估中pr[t].tp += tmp.tp;pr[t].fp += tmp.fp;pr[t].fn += tmp.fn;if(tmp.similarity!=-1)          //如果判断AOSpr[t].similarity += tmp.similarity;}}//计算recall、precision和AOSvector<double> recall;precision.assign(N_SAMPLE_PTS, 0);if(compute_aos)aos.assign(N_SAMPLE_PTS, 0);double r=0;for (int32_t i=0; i<thresholds.size(); i++)r = pr[i].tp/(double)(pr[i].tp + pr[i].fn);         //计算recallrecall.push_back(r);precision[i] = pr[i].tp/(double)(pr[i].tp + pr[i].fp);          //计算precisionif(compute_aos)                    //如果需要,计算AOSaos[i] = pr[i].similarity/(double)(pr[i].tp + pr[i].fp);}//用最大值滤取precision和AOS(对i)for (int32_t i=0; i<thresholds.size(); i++){precision[i] = *max_element(precision.begin()+i, precision.end());if(compute_aos)aos[i] = *max_element(aos.begin()+i, aos.end());}//保存数据并返回计算成功saveStats(precision, aos, fp_det, fp_ori);            //在指针fp_det和fp_ori指向的文件中写入数据return true;
}

saveAndPlotPlots

saveAndPlotPlots说明。

void saveAndPlotPlots(string dir_name,string file_name,string obj_type,vector<double> vals[],bool is_aos){char command[1024];//保存结果图像到指定路径FILE *fp = fopen((dir_name + "/" + file_name + ".txt").c_str(),"w");           //保存在plot文件夹下对应类别的文件中printf("save %s\n", (dir_name + "/" + file_name + ".txt").c_str());//对于截取数量的样本,按正确率从高到低输出结果(格式:占40个样本的前百分之多少、简单类别、中等类别、困难类别分别对应的精度)for (int32_t i=0; i<(int)N_SAMPLE_PTS; i++)fprintf(fp,"%f %f %f %f\n",(double)i/(N_SAMPLE_PTS-1.0),vals[0][i],vals[1][i],vals[2][i]); fclose(fp);//求解三种难度下的精度之和,计算AP并显示float sum[3] = {0, 0, 0};for (int v = 0; v < 3; ++v)for (int i = 0; i < vals[v].size(); i = i + 4)sum[v] += vals[v][i];printf("%s AP: %f %f %f\n", file_name.c_str(), sum[0] / 11 * 100, sum[1] / 11 * 100, sum[2] / 11 * 100);//创建png + epsfor (int32_t j=0; j<2; j++) {//打开文件FILE *fp = fopen((dir_name + "/" + file_name + ".gp").c_str(),"w");//保存gnuplot指令if (j==0) {fprintf(fp,"set term png size 450,315 font \"Helvetica\" 11\n");fprintf(fp,"set output \"%s.png\"\n",file_name.c_str());} else {fprintf(fp,"set term postscript eps enhanced color font \"Helvetica\" 20\n");fprintf(fp,"set output \"%s.eps\"\n",file_name.c_str());}//设置labels和范围fprintf(fp,"set size ratio 0.7\n");fprintf(fp,"set xrange [0:1]\n");fprintf(fp,"set yrange [0:1]\n");fprintf(fp,"set xlabel \"Recall\"\n");if (!is_aos) fprintf(fp,"set ylabel \"Precision\"\n");else         fprintf(fp,"set ylabel \"Orientation Similarity\"\n");obj_type[0] = toupper(obj_type[0]);fprintf(fp,"set title \"%s\"\n",obj_type.c_str());//线宽int32_t   lw = 5;if (j==0) lw = 3;//画error曲线fprintf(fp,"plot ");fprintf(fp,"\"%s.txt\" using 1:2 title 'Easy' with lines ls 1 lw %d,",file_name.c_str(),lw); fprintf(fp,"\"%s.txt\" using 1:3 title 'Moderate' with lines ls 2 lw %d,",file_name.c_str(),lw); fprintf(fp,"\"%s.txt\" using 1:4 title 'Hard' with lines ls 3 lw %d",file_name.c_str(),lw);//关闭文件fclose(fp);//运行gnuplot以生成png + epssprintf(command,"cd %s; gnuplot %s",dir_name.c_str(),(file_name + ".gp").c_str());system(command);}//生成pdf并截取sprintf(command,"cd %s; ps2pdf %s.eps %s_large.pdf",dir_name.c_str(),file_name.c_str(),file_name.c_str());system(command);sprintf(command,"cd %s; pdfcrop %s_large.pdf %s.pdf",dir_name.c_str(),file_name.c_str(),file_name.c_str());system(command);sprintf(command,"cd %s; rm %s_large.pdf",dir_name.c_str(),file_name.c_str()); system(command);
}

computeStatistics

用于计算必要的数据,为recall的计算做准备:

tPrData computeStatistics(CLASSES current_class, const vector<tGroundtruth> &gt,const vector<tDetection> &det, const vector<tGroundtruth> &dc,const vector<int32_t> &ignored_gt, const vector<int32_t>  &ignored_det,bool compute_fp, double (*boxoverlap)(tDetection, tGroundtruth, int32_t),METRIC metric, bool compute_aos=false, double thresh=0, bool debug=false){tPrData stat = tPrData();const double NO_DETECTION = -10000000;vector<double> delta;            //用于存储TP需要的角度的不同(AOS计算需要)vector<bool> assigned_detection; //用于存储一个检测结果是被标注有效还是忽略assigned_detection.assign(det.size(), false);vector<bool> ignored_threshold;ignored_threshold.assign(det.size(), false); //如果计算FP,用于存储低于阈值的检测结果//在计算precision时,忽略低score的检测结果(需要FP)if(compute_fp)for(int32_t i=0; i<det.size(); i++)if(det[i].thresh<thresh)ignored_threshold[i] = true;//评估所有真值boxesfor(int32_t i=0; i<gt.size(); i++){//如果这个真值不属于当前或相似类别,则忽略if(ignored_gt[i]==-1)continue;/*=======================================================================find candidates (overlap with ground truth > 0.5) (logical len(det)) =======================================================================*/int32_t det_idx          = -1;double valid_detection = NO_DETECTION;double max_overlap     = 0;//寻找可能的检测结果bool assigned_ignored_det = false; for(int32_t j=0; j<det.size(); j++){//如果这个检测结果不属于当前类别或已经存在或低于阈值,都被忽略 if(ignored_det[j]==-1) continue;if(assigned_detection[j])continue;if(ignored_threshold[j])continue;//找到候选目标对应的score最大值并获取响应的检测结果编号double overlap = boxoverlap(det[j], gt[i], -1); //为计算recall阈值,需要考虑拥有最高score的候选if(!compute_fp && overlap>MIN_OVERLAP[metric][current_class] && det[j].thresh>valid_detection){det_idx         = j;valid_detection = det[j].thresh;}//为计算precision曲线值,需要考虑拥有最大重叠率的候选  //如果该候选是一个被忽略的检测(min_height),启用重叠率检测else if(compute_fp && overlap>MIN_OVERLAP[metric][current_class] && (overlap>max_overlap || assigned_ignored_det) && ignored_det[j]==0){ max_overlap     = overlap;det_idx         = j;valid_detection = 1;assigned_ignored_det = false;;}else if(compute_fp && overlap>MIN_OVERLAP[metric][current_class] && valid_detection==NO_DETECTION && ignored_det[j]==1){det_idx              = j; valid_detection      = 1;assigned_ignored_det = true;}}/*=======================================================================compute TP, FP and FN  compute TP, FP and FN=======================================================================//如果没有给当前有效的真值分配任何东西if(valid_detection==NO_DETECTION && ignored_gt[i]==0) {  stat.fn++;}//只评估有效真值等同于 detection assignments (considering difficulty level)else if(valid_detection!=NO_DETECTION && (ignored_gt[i]==1 || ignored_det[det_idx]==1))assigned_detection[det_idx] = true;//找到一个有效的true positiveelse if(valid_detection!=NO_DETECTION){//向阈值向量写入最高的stat.tp++;stat.v.push_back(det[det_idx].thresh);//真值和检测结果之间的计算角度差异(如果提供了有效的角度检测)if(compute_aos) delta.push_back(gt[i].box.alpha - det[det_idx].box.alpha);  //清空assigned_detection[det_idx] = true;}}//如果需要计算FP are requested,则考虑stuff area if(compute_fp){//计数fpfor(int32_t i=0; i<det.size(); i++){//如果需要,对所有的false positives计数(高度小于规定的被忽略(ignored_det==1))if(!(assigned_detection[i] || ignored_det[i]==-1 || ignored_det[i]==1 || ignored_threshold[i]))stat.fp++;}//不考虑与 stuff area重叠的检测结果int32_t nstuff = 0; for(int32_t i=0; i<dc.size(); i++){for(int32_t j=0; j<det.size(); j++){//忽略不属于当前类别的检测结果、已经处理过的检测结果、阈值或最小高度很低的检测结果if(assigned_detection[j])continue;if(ignored_det[j]==-1 || ignored_det[j]==1)continue;if(ignored_threshold[j]) continue;//计算重叠率,如果重叠率超过给定数值就分配给stuff area double overlap = boxoverlap(det[j], dc[i], 0);if(overlap>MIN_OVERLAP[metric][current_class]){assigned_detection[j] = true;nstuff++;}}}// FP = 所有未分配真值的点的个数(no. of all not to ground truth assigned detections) - 分配到stuff area的点的个数(detections assigned to stuff areas)stat.fp -= nstuff;//如果所有角度值有效则计算AOSif(compute_aos){vector<double> tmp;// FP have a similarity of 0, for all TP compute AOStmp.assign(stat.fp, 0);for(int32_t i=0; i<delta.size(); i++)tmp.push_back((1.0+cos(delta[i]))/2.0);// be sure, that all orientation deltas are computedassert(tmp.size()==stat.fp+stat.tp);assert(delta.size()==stat.tp);// get the mean orientation similarity for this imageif(stat.tp>0 || stat.fp>0)stat.similarity = accumulate(tmp.begin(), tmp.end(), 0.0);// there was neither a FP nor a TP, so the similarity is ignored in the evaluationelsestat.similarity = -1;}}return stat;
}

KITTI 3D目标检测离线评估工具包说明相关推荐

  1. KITTI 3D目标检测数据集入门

    下载地址与描述 数据集官网下载地址: The KITTI Vision Benchmark Suite 3D目标检测数据集由7481个训练图像和7518个测试图像以及相应的点云数据组成,包括总共802 ...

  2. 3D目标检测之数据集

    目录 1. KITTI Dataset 2. Waymo Open Dataset 3. NuScenes DataSet 4. Appllo Scape 5. Lyft L5 6. Argovers ...

  3. 【CVPR2020】3D目标检测论文汇总

    文章目录 1. 3D目标检测--室外 1. Associate-3Ddet: Perceptual-to-Conceptual Association for 3D Point Cloud Objec ...

  4. 点云 3D 目标检测 - VoxelNet(CVPR 2018)

    点云 3D 目标检测 - VoxelNet(CVPR 2018) 摘要 1. 引言 1.1 相关工作 1.2 贡献 2. VoxelNet 2.1 VoxelNet架构 2.1.1 特征学习网络 2. ...

  5. 一文尽览 | 基于点云、多模态的3D目标检测算法综述!(Point/Voxel/Point-Voxel)

    点击下方卡片,关注"自动驾驶之心"公众号 ADAS巨卷干货,即可获取 点击进入→自动驾驶之心技术交流群 后台回复[ECCV2022]获取ECCV2022所有自动驾驶方向论文! 目前 ...

  6. 点云 3D 目标检测 - SECOND(Sensors 2018)

    点云 3D 目标检测 - SECOND: Sparsely Embedded Convolutional Detection - 稀疏嵌入卷积检测(Sensors 2018) 摘要 1. 引言 2. ...

  7. CaDDN:基于单目的3D目标检测新方法(CVPR2021)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨元气满满的打工人 来源丨CV研习社 文章导读 导读:在自动驾驶的技术中,3D目标检测能够提更加丰 ...

  8. Top1的3D目标检测方法(已开源)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 一.SA-SSD 在bird's eye view任务中,效率与精度并存的SA-SSD 论文:http ...

  9. DSGN:基于深度立体几何网络的3D目标检测(香港大学提出)

    主要思想与创新点 大多数最先进的三维目标检测器严重依赖激光雷达传感器.由于在三维场景中的预测不准确,基于图像的方法与基于激光雷达的方法在性能上仍有很大差距.本文提出了深度立体几何网络(DSGN)的方法 ...

最新文章

  1. PHP环境下配置WebGrind——让你的网站性能看得见
  2. JS:两个json数组合并、去重,以及删除某一项元素
  3. hdfs如何查找指定目录是否文件_hadoop实战教程-HDFS文件系统如何查看文件对应的block...
  4. 明天 | 2021单细胞组学国际研讨会(线上)【附直播链接】
  5. python_day24_class_类_继承
  6. Python处理各种压缩文件(bzip2,gzip,zip)
  7. 你的核心竞争力真的是技术么?
  8. php 刷新腾讯云cdn
  9. c++ 关于指针以及(amp;)使用的一些小问题
  10. 集合框架(数据结构之栈和队列)
  11. 移动APP测试,adb工具
  12. jdk7对list进行排序(按照list中entity的某个属性比如age)
  13. 用友-凭证辅助项打印不出来的原因
  14. 共享充电宝的终局:金钱游戏
  15. 因果故事:偷不走的命运!
  16. Tensorflow图像识别-2
  17. 【科普小知识】KMS 是什么?
  18. React 问题总结
  19. HTMLCSS学习笔记(二十四)——利用border属性制作太极图与哆啦A梦
  20. 牧牛商学院,区块链技术在会计领域的应用

热门文章

  1. android 图片过长,【05-25求助】怎样无损加载一张过长的图片
  2. tda4vm如何SPL方式加载MCU域的核?
  3. 供应链安全这件事,早就被朱元璋玩明白了
  4. 360 mysql无法启动_mysql无法启动
  5. linux系统上搭建静态网站
  6. Dubbo配置加载问题
  7. win10共享打印机怎么设置_win10系统设置共享打印机的方法步骤
  8. 5G+工业互联网应用场景大集锦!
  9. ifrog 1130 喵哈哈村的魔法大师╳灬兲笙疯癫°月 缩点+最小路径覆盖+背包
  10. 曲面积分的投影法_大学数学:第一、二型曲面积分:投影、高斯公式补面挖点怎么用...