YOLOv4(darknet版)后处理:显示置信度、保存检测框的内容到本地

之前写了一篇使用YOLOv4训练的博客:https://blog.csdn.net/Creama_/article/details/106209388。然后很多读者反映检测图片的结果中怎么没有置信度,怎么加?怎么改变检测框的粗细?框出来目标怎么保存到本地(包括图片检测和视频检测)?等等。。。今天就先写一下这几部分的代码,希望作者AlexeyAB看到给我打钱。

显示置信度

打开src/image.c
查找"draw_detections_v3",找到这个函数的定义部分,注意是定义部分。拉到函数最后一部分,只要在对应位置加上三句:
char buff[20];
sprintf(buff, "(%2.0f%%)", selected_detections[i].det.prob[selected_detections[i].best_class] * 100);
strcat(labelstr, buff);
即可,然后保存,重新make。

    if (alphabet) {
        char labelstr[4096] = { 0 };
		char buff[20];	//加上这句
        strcat(labelstr, names[selected_detections[i].best_class]);
		sprintf(buff, "(%2.0f%%)", selected_detections[i].det.prob[selected_detections[i].best_class] * 100);  //加上这句
		strcat(labelstr, buff);	//加上这句
        int j;
        for (j = 0; j < classes; ++j) {
            if (selected_detections[i].det.prob[j] > thresh && j != selected_detections[i].best_class) {
                strcat(labelstr, ", ");
                strcat(labelstr, names[j]);
            }
        }
        image label = get_label_v3(alphabet, labelstr, (im.w*.01));
        draw_label(im, top + width, left, label, rgb);
        free_image(label);
    }

改变检测框的粗细

打开src/image.c
查找"draw_detections_v3",找到函数定义部分,在函数体里找到"draw_box_width",其中变量width就是检测框的粗细。作者是这样定义的:

int width = im.h * .002;
if (width < 1)
    width = 1;

变大变小怎么变,你来定。

保存检测框的内容到本地(批量图片检测)

1.修改detector.c
这一部分是在增加了批量检测图片的基础上,将所有检测框中的内容保存到本地。批量检测图片在之前的博客有写。
打开src/detector.c
查找"test_detector",找到这个函数的定义部分,再找到里面的draw_detections_v3, 如下图所示:
在这里插入图片描述

这里有一部分是之前批量保存图片的代码,为了在画框之前保存检测框的内容,把获取文件名的代码放到了draw_detections_v3前面,并且在其后加上保存检测框中目标的代码:

save_dets(im, dets, nboxes, thresh, b);

2.修改image.h和image.c
打开src/image.h
在文件末尾加上函数save_dets的声明:

void save_dets(image im, detection *dets, int num_boxes, float thresh, const char* name);

如下图所示:
在这里插入图片描述
保存后打开src/image.c
在文件开头加入函数save_dets的定义:

void save_dets(image im, detection *dets, int num_boxes, float thresh, const char* name){
    int reg[4];
    int width = im.w;
    int height = im.h;
    
    for(int i=0; i < num_boxes; ++i){
        int best_class = -1;
        float best_class_prob = thresh;
	    for (int j = 0; j < dets[i].classes; ++j){
	        if (dets[i].prob[j] > best_class_prob) {
	            best_class = j;
		        best_class_prob = dets[i].prob[j];
	        }
	    }
	    if (best_class >= 0){
	        box b = dets[i].bbox;
	        reg[0] = round((b.x - b.w / 2) * width);
            reg[1] = round((b.y - b.h / 2) * height);
            reg[2] = round(b.w * width);
            reg[3] = round(b.h * height);
            
            if (reg[0] < 0) reg[0] = 0;
            if (reg[0] > width - 1) reg[0] = width - 1;
            if (reg[1] < 0) reg[1] = 0;
            if (reg[1] > height - 1) reg[1] = height - 1;
            if (reg[2] + reg[0] > width) reg[2] = width - reg[0];
            if (reg[3] + reg[1] > height) reg[3] = height - reg[1];
	        
	        save_dets_cv(im, reg, name, i);
	    }
    }
}

如下图所示:
在这里插入图片描述
3.修改image_opencv.h和image_opencv.cpp
打开src/image_opencv.h
在文件末尾加上函数save_dets_cv的声明:

void save_dets_cv(image im, int reg[], const char* name, int i);

如下图所示:
在这里插入图片描述
保存后打开src/image_opencv.cpp
在文件末尾加上函数save_dets_cv的定义:

void save_dets_cv(image im, int reg[], const char* name, int i){
    cv::Mat mat = image_to_mat(im);
    cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
    cv::Mat dets = mat(cv::Rect(reg[0], reg[1], reg[2], reg[3]));
    //string name_dets = name;
    char name_dets[256];
    sprintf(name_dets, "%s_dets%d.jpg", name, i);
    cv::imwrite(name_dets, dets);
}

如下图所示:
在这里插入图片描述
到这里代码就写完了,保存所有文件,重新make。输入检测的命令行,结果会保存在output/,如下图所示,带dets的是每个检测框中的内容,如果不想保存大图可以注释掉detector.c中的save_image。
在这里插入图片描述
因为C++学的不是很好,所以只能借助opencv的方法切片并保存,有更好的方法欢迎留言。


2021.3.10附:如果希望保存的名字像 “15_color_000158_person_0.jpg” 这样,其中person是该检测框的类别信息。
1.修改detector.c

save_dets(im, dets, nboxes, thresh, names, b); // 增加names

2.修改image.h和image.c
打开src/image.h
在文件末尾加上函数save_dets的声明:

void save_dets(image im, detection *dets, int num_boxes, float thresh, char **names, const char* name);

保存后打开src/image.c
在文件开头加入函数save_dets的定义:

void save_dets(image im, detection *dets, int num_boxes, float thresh, char **names, const char* name){
    int reg[4];
    int width = im.w;
    int height = im.h;
    
    for(int i=0; i < num_boxes; ++i){
        int best_class = -1;
        float best_class_prob = thresh;
	    for (int j = 0; j < dets[i].classes; ++j){
	        if (dets[i].prob[j] > best_class_prob) {
	            best_class = j;
		        best_class_prob = dets[i].prob[j];
	        }
	    }
	    if (best_class >= 0){
	        box b = dets[i].bbox;
	        reg[0] = round((b.x - b.w / 2) * width);
            reg[1] = round((b.y - b.h / 2) * height);
            reg[2] = round(b.w * width);
            reg[3] = round(b.h * height);
            
            if (reg[0] < 0) reg[0] = 0;
            if (reg[0] > width - 1) reg[0] = width - 1;
            if (reg[1] < 0) reg[1] = 0;
            if (reg[1] > height - 1) reg[1] = height - 1;
            if (reg[2] + reg[0] > width) reg[2] = width - reg[0];
            if (reg[3] + reg[1] > height) reg[3] = height - reg[1];
	        
	        save_dets_cv(im, reg, names[best_class], name, i);  // 参数列表增加names,这里增加names[best_class]
	    }
    }
}

3.修改image_opencv.h和image_opencv.cpp
打开src/image_opencv.h
在文件末尾加上函数save_dets_cv的声明:

void save_dets_cv(image im, int reg[], char* class_name, const char* name, int i); // 增加class_name

保存后打开src/image_opencv.cpp
在文件末尾加上函数save_dets_cv的定义:

void save_dets_cv(image im, int reg[], char* class_name, const char* name, int i){
    cv::Mat mat = image_to_mat(im);
    cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
    cv::Mat dets = mat(cv::Rect(reg[0], reg[1], reg[2], reg[3]));
    //string name_dets = name;
    char name_dets[256];
    sprintf(name_dets, "%s_%s_%d.jpg", name, class_name, i); // 修改
    cv::imwrite(name_dets, dets);
}

period


2020.10.20更新

保存检测框的内容到本地(视频检测)

前几天有人留言希望出个保存检测框到本地视频版的,我感觉挺有意思,虽然代码很快就写完了,但是运行的时候遇到一个bug,结果花了两天时间,虽然解决了这个bug,但是还是不太清楚为什么会出现这个问题(憔悴),估计是show_img为NULL的时候无法保存吧。下面开始讲步骤:
1.修改demo.c
打开src/demo.c,找到"draw_detections_cv_v3",在前面添加代码:

// save video detections
char b[256];
sprintf(b, "video_dets/video_dets_frame%d_", count-1);
if(show_img) save_detections_cv(show_img, local_dets, local_nboxes, demo_thresh, b);	// 这里if(show_img)很关键

如下图所示:
在这里插入图片描述
2.修改image_opencv.h和image_opencv.cpp
打开src/image_opencv.h,在"Draw Detection"后面增加"Save Detection",写上save_detections_cv函数的声明:

// Save Detection
void save_detections_cv(mat_cv* mat, detection *dets, int num_boxes, float thresh, const char* name);

如下图所示:
在这里插入图片描述

打开src/image_opencv.cpp,找到"Draw Loss",在前面添加save_detections_cv函数的定义:

// ====================================================================
// Save Detections
// ====================================================================
extern "C" void save_detections_cv(mat_cv* mat, detection *dets, int num_boxes, float thresh, const char* name){
    cv::Mat *show_img = (cv::Mat*)mat;
    image im = mat_to_image(*show_img);
    int reg[4];
    int width = im.w;
    int height = im.h;
    
    for(int i=0; i < num_boxes; ++i){
        int best_class = -1;
        float best_class_prob = thresh;
	    for (int j = 0; j < dets[i].classes; ++j){
	        if (dets[i].prob[j] > best_class_prob) {
	        	best_class = j;
		    	best_class_prob = dets[i].prob[j];
	        }
	    }
	if (best_class >= 0){
	    box b = dets[i].bbox;
	    reg[0] = round((b.x - b.w / 2) * width);
        reg[1] = round((b.y - b.h / 2) * height);
        reg[2] = round(b.w * width);
        reg[3] = round(b.h * height);
            
        if (reg[0] < 0) reg[0] = 0;
        if (reg[0] > width - 1) reg[0] = width - 1;
        if (reg[1] < 0) reg[1] = 0;
        if (reg[1] > height - 1) reg[1] = height - 1;
        if (reg[2] + reg[0] > width) reg[2] = width - reg[0];
        if (reg[3] + reg[1] > height) reg[3] = height - reg[1];
	        
	    cv::Mat img = *show_img;
	    cv::Mat rect = img(cv::Rect(reg[0], reg[1], reg[2], reg[3]));
	    char name_dets[256];
    	sprintf(name_dets, "%s%d.jpg", name, i);
	    cv::imwrite(name_dets, rect);
	    }
    }
}
// ----------------------------------------

如下图所示:
在这里插入图片描述
到这里代码就写完了,最后在darknet目录下新建一个文件夹video_dets,然后重新make,运行类似下面的命令检测视频:

./darknet detector demo cfg/tfclight.data cfg/yolov4-tfclight.cfg backup/yolov4-tfclight/yolov4-tfclight_8000.weights rotate01.mp4 -dont_show -out_filename tfclight01.mp4

最后视频输出在darknet下,检测框保存在video_dets下(这里每帧图片只有一个检测框):
在这里插入图片描述
代码应该还可以优化,有时间再说了。最最后关于那个bug长这样:
在这里插入图片描述
这是没有加if(show_img)报的错,加上if(show_img)后就可以正常运行了,中间试了很多解决方法没搞定,没想到最后问题在这。

版权声明:本文为CSDN博主「Creama_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Creama_/article/details/107960513

Creama_

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

YOLOv3详解

1. 什么是YOLO ​  YOLO是“You Only Look Once”的简称,它虽然不是最精确的算法,但在精确度和速度之间选择的折中,效果也是相当不错。YOLOv3借鉴了YOLOv1和YOLO

Yolox 训练自己的数据集

目录 Yolo系列因为其灵活性,一直是目标检测热门算法,近期旷视的研究者提出了Yolox高性能目标检测器,将Anchor free引入了Yolo算法,是除YOLOV1之后,