KITTI 3D目标检测离线评估工具包说明
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> >,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目标检测离线评估工具包说明相关推荐
- KITTI 3D目标检测数据集入门
下载地址与描述 数据集官网下载地址: The KITTI Vision Benchmark Suite 3D目标检测数据集由7481个训练图像和7518个测试图像以及相应的点云数据组成,包括总共802 ...
- 3D目标检测之数据集
目录 1. KITTI Dataset 2. Waymo Open Dataset 3. NuScenes DataSet 4. Appllo Scape 5. Lyft L5 6. Argovers ...
- 【CVPR2020】3D目标检测论文汇总
文章目录 1. 3D目标检测--室外 1. Associate-3Ddet: Perceptual-to-Conceptual Association for 3D Point Cloud Objec ...
- 点云 3D 目标检测 - VoxelNet(CVPR 2018)
点云 3D 目标检测 - VoxelNet(CVPR 2018) 摘要 1. 引言 1.1 相关工作 1.2 贡献 2. VoxelNet 2.1 VoxelNet架构 2.1.1 特征学习网络 2. ...
- 一文尽览 | 基于点云、多模态的3D目标检测算法综述!(Point/Voxel/Point-Voxel)
点击下方卡片,关注"自动驾驶之心"公众号 ADAS巨卷干货,即可获取 点击进入→自动驾驶之心技术交流群 后台回复[ECCV2022]获取ECCV2022所有自动驾驶方向论文! 目前 ...
- 点云 3D 目标检测 - SECOND(Sensors 2018)
点云 3D 目标检测 - SECOND: Sparsely Embedded Convolutional Detection - 稀疏嵌入卷积检测(Sensors 2018) 摘要 1. 引言 2. ...
- CaDDN:基于单目的3D目标检测新方法(CVPR2021)
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨元气满满的打工人 来源丨CV研习社 文章导读 导读:在自动驾驶的技术中,3D目标检测能够提更加丰 ...
- Top1的3D目标检测方法(已开源)
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 一.SA-SSD 在bird's eye view任务中,效率与精度并存的SA-SSD 论文:http ...
- DSGN:基于深度立体几何网络的3D目标检测(香港大学提出)
主要思想与创新点 大多数最先进的三维目标检测器严重依赖激光雷达传感器.由于在三维场景中的预测不准确,基于图像的方法与基于激光雷达的方法在性能上仍有很大差距.本文提出了深度立体几何网络(DSGN)的方法 ...
最新文章
- PHP环境下配置WebGrind——让你的网站性能看得见
- JS:两个json数组合并、去重,以及删除某一项元素
- hdfs如何查找指定目录是否文件_hadoop实战教程-HDFS文件系统如何查看文件对应的block...
- 明天 | 2021单细胞组学国际研讨会(线上)【附直播链接】
- python_day24_class_类_继承
- Python处理各种压缩文件(bzip2,gzip,zip)
- 你的核心竞争力真的是技术么?
- php 刷新腾讯云cdn
- c++ 关于指针以及(amp;)使用的一些小问题
- 集合框架(数据结构之栈和队列)
- 移动APP测试,adb工具
- jdk7对list进行排序(按照list中entity的某个属性比如age)
- 用友-凭证辅助项打印不出来的原因
- 共享充电宝的终局:金钱游戏
- 因果故事:偷不走的命运!
- Tensorflow图像识别-2
- 【科普小知识】KMS 是什么?
- React 问题总结
- HTMLCSS学习笔记(二十四)——利用border属性制作太极图与哆啦A梦
- 牧牛商学院,区块链技术在会计领域的应用
热门文章
- android 图片过长,【05-25求助】怎样无损加载一张过长的图片
- tda4vm如何SPL方式加载MCU域的核?
- 供应链安全这件事,早就被朱元璋玩明白了
- 360 mysql无法启动_mysql无法启动
- linux系统上搭建静态网站
- Dubbo配置加载问题
- win10共享打印机怎么设置_win10系统设置共享打印机的方法步骤
- 5G+工业互联网应用场景大集锦!
- ifrog 1130 喵哈哈村的魔法大师╳灬兲笙疯癫°月 缩点+最小路径覆盖+背包
- 曲面积分的投影法_大学数学:第一、二型曲面积分:投影、高斯公式补面挖点怎么用...