目录
1. 仅输出指定类别的检测结果
coco模型,只输出人、车或其他指定类别的检测结果
这个比较简单,只需要在打印框、输出框信息的时候加个判断即可。
修改src/image.c中的draw_detection函数
int left = (b.x-b.w/2.)*im.w;
int right = (b.x+b.w/2.)*im.w;
int top = (b.y-b.h/2.)*im.h;
int bot = (b.y+b.h/2.)*im.h;
if(left < 0) left = 0;
if(right > im.w-1) right = im.w-1;
if(top < 0) top = 0;
if(bot > im.h-1) bot = im.h-1;
bool is_person=false;
bool is_car=false;
is_person=!strcmp(labelstr,"person");//判断标签是否为person
is_car=!strcmp(labelstr,"car");
if(is_person||is_car){//只输出人和车的检测结果
draw_box_width(im, left, top, right, bot, width, red, green, blue);
if (alphabet) {
image label = get_label(alphabet, labelstr, (im.h*.03));
draw_label(im, top + width, left, label, rgb);
free_image(label);
}
if (dets[i].mask){
image mask = float_to_image(14, 14, 1, dets[i].mask);
image resized_mask = resize_image(mask, b.w*im.w, b.h*im.h);
image tmask = threshold_image(resized_mask, .5);
embed_image(tmask, im, left, top);
free_image(mask);
free_image(resized_mask);
free_image(tmask);
}
}
https://blog.csdn.net/u012244950/article/details/25143019,C++裁剪图片
https://blog.csdn.net/weixin_40003920/article/details/79693221。裁剪yolo
https://blog.csdn.net/LOVE1055259415/article/details/81017075,裁剪yolo
2. 裁剪出目标
只打印指定类别的检测结果+裁剪出目标,修改src/image.c中的draw_detection函数。
void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
char labelstr[4096] = {0};
int class = -1;
for(j = 0; j < classes; ++j){
if (dets[i].prob[j] > thresh){
if (class < 0) {
strcat(labelstr, names[j]);
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
}
//printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
else if (dets[i].prob[j] > 0)
{
printf("Error %s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
}
if(class >= 0){
int width = im.h * .006;
/*
if(0){
width = pow(prob, 1./2.)*10+1;
alphabet = 0;
}
*/
//printf("%d %s: %.0f%%\n", i, names[class], prob*100);
int offset = class*123457 % classes;
float red = get_color(2,offset,classes);
float green = get_color(1,offset,classes);
float blue = get_color(0,offset,classes);
float rgb[3];
//width = prob*20+2;
rgb[0] = red;
rgb[1] = green;
rgb[2] = blue;
box b = dets[i].bbox;
//printf("%f %f %f %f\n", b.x, b.y, b.w, b.h);
int left = (b.x-b.w/2.)*im.w;
int right = (b.x+b.w/2.)*im.w;
int top = (b.y-b.h/2.)*im.h;
int bot = (b.y+b.h/2.)*im.h;
if(left < 0) left = 0;
if(right > im.w-1) right = im.w-1;
if(top < 0) top = 0;
if(bot > im.h-1) bot = im.h-1;
bool Is_person = false;
bool Is_car = false;
Is_person = !strcmp(labelstr, "person");
Is_car = !strcmp(labelstr, "car");
if(Is_person || Is_car){
IplImage* src;
src=image_to_iplImage(im,src);
// cvShowImage("src",src);
int x = left*1.0;
int y = top*1.0;
int w = (right - left)*1.0;
int h = (bot - top)*1.0;
cvSetImageROI(src,cvRect(x,y,w,h)); //这里如果宽或者高越界会导致错误,闪退/
IplImage* crop = cvCreateImage(cvSize(w,h),IPL_DEPTH_8U,src->nChannels);
cvCopy(src,crop,0);
cvResetImageROI(src);
draw_box_width(im, left, top, right, bot, width, red, green, blue);
save_image(im,"test"); //这里的图像已经是有框的
src = image_to_iplImage(im,src);
cvShowImage("src_bbox",src);
cvShowImage("crop",crop);
cvSaveImage("crop.jpg",crop,0);
if (alphabet) {
image label = get_label(alphabet, labelstr, (im.h*.03));
draw_label(im, top + width, left, label, rgb);
free_image(label);
}
if (dets[i].mask){
image mask = float_to_image(14, 14, 1, dets[i].mask);
image resized_mask = resize_image(mask, b.w*im.w, b.h*im.h);
image tmask = threshold_image(resized_mask, .5);
embed_image(tmask, im, left, top);
free_image(mask);
free_image(resized_mask);
free_image(tmask);
}
}
}
}
}
3. 添加目标置信度
在src/image.c中修改函数draw_detections函数。
strcat函数:用来连接字符串,其原型为:char *strcat(char *dest, const char *src);dest 为目的字符串指针,src 为源字符串指针。将参数 src 字符串复制到参数 dest 所指的字符串尾部;dest 最后的结束字符 NULL 会被覆盖掉,并在连接后的字符串的尾部再增加一个 NULL。返回dest 字符串起始地址。
修改image.c文件
大概239行,draw_detector函数,源代码如下:
void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
char labelstr[4096] = {0};
int class = -1;
for(j = 0; j < classes; ++j){
if (dets[i].prob[j] > thresh){
if (class < 0) {
strcat(labelstr, names[j]);
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
}
//printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
else if (dets[i].prob[j] > 0)
{
printf("Error %s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
}
......
}
修改后,代码如下:
void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
char labelstr[4096] = {0};
int class = -1;
char lprob[10];//修改的部分
for(j = 0; j < classes; ++j){
sprintf(lprob, "%.2f",dets[i].prob[j]*100);//修改的部分
if (dets[i].prob[j] > thresh){
if (class < 0) {
strcat(labelstr, names[j]);
strcat(labelstr, ": ");//修改的部分
strcat(labelstr, lprob);//修改的部分
strcat(labelstr, "%");//修改的部分
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
strcat(labelstr, ": ");//修改的部分
strcat(labelstr, lprob);//修改的部分
strcat(labelstr, "%");//修改的部分
}
printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
else if (dets[i].prob[j] > 0)
{
printf("Error %s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
}
......
}
这样make之后,测试图片便显示了置信度。
4. 批量测试图片并保存至指定文件夹
打开darknet文件下examples/detector.c文件,替换test_detector函数如下:
void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen)
{
list *options = read_data_cfg(datacfg);
char *name_list = option_find_str(options, "names", "data/names.list");
char **names = get_labels(name_list);
image **alphabet = load_alphabet();
network *net = load_network(cfgfile, weightfile, 0);
set_batch_network(net, 1);
srand(2222222);
double time;
char buff[256];
char *input = buff;
float nms=.45;
int i=0;
while(1){
if(filename){//如果直接输入单张路径,则进行单张图片测试 和源代码相同
strncpy(input, filename, 256);
image im = load_image_color(input,0,0);
image sized = letterbox_image(im, net->w, net->h);
//image sized = resize_image(im, net->w, net->h);
//image sized2 = resize_max(im, net->w);
//image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
//resize_network(net, sized.w, sized.h);
layer l = net->layers[net->n-1];
float *X = sized.data;
time=what_time_is_it_now();
network_predict(net, X);
printf("%s: Predicted in %f seconds.\n", input, what_time_is_it_now()-time);
int nboxes = 0;
detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
//printf("%d\n", nboxes);
//if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
free_detections(dets, nboxes);
if(outfile)
{
save_image(im, outfile);
}
else{
save_image(im, "predictions");
#ifdef OPENCV
cvNamedWindow("predictions", CV_WINDOW_NORMAL);
if(fullscreen){
cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
}
show_image(im, "predictions");
cvWaitKey(0);
cvDestroyAllWindows();
#endif
}
free_image(im);
free_image(sized);
if (filename) break;
}
else { //如果命令行无图片路径,则提示输入txt路径,txt文件包含图片的路径
printf("Enter Image Path: ");
fflush(stdout);
input = fgets(input, 256, stdin);
if(!input) return;
strtok(input, "\n");
list *plist = get_paths(input);//这里同train_detector时读取图片的操作一样
char **paths = (char **)list_to_array(plist);
printf("Start Testing!\n");
int m = plist->size;
//access()函数,确定文件的访问权限 access(filename,mode)
//mode=0,判断文件是否存在;mode=1,检测文件是否可运行;mode=2,检测文件是否可写访问;mode=4,检查是//否可读访问;mode=6,检查是否可读/写访问
//返回0表示存在,返回-1表示不存在
if(access("/xxx/darknet/data/out",0)==-1)//"/xxx/darknet/data"修改成自己的路径,想把测试结果存哪就写哪
{
//如果没有上述文件,则用mkdir创建,若创建失败,返回-1.
if (mkdir("/xxx/darknet/data/out",0777))//"/xxx/darknet/data"修改成自己的路径
{
printf("create file bag failed!!!");
}
}
for(i = 0; i < m; ++i){//m张图片
char *path = paths[i];
image im = load_image_color(path,0,0);
image sized = letterbox_image(im, net->w, net->h);
//image sized = resize_image(im, net->w, net->h);
//image sized2 = resize_max(im, net->w);
//image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h);
//resize_network(net, sized.w, sized.h);
layer l = net->layers[net->n-1];
float *X = sized.data;
time=what_time_is_it_now();
network_predict(net, X);
printf("Try Very Hard:");
printf("%s: Predicted in %f seconds.\n", path, what_time_is_it_now()-time);
int nboxes = 0;
detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes);
//printf("%d\n", nboxes);
//if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
if (nms) do_nms_sort(dets, nboxes, l.classes, nms);
draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes);
free_detections(dets, nboxes);
if(outfile){
save_image(im, outfile);
}
else{
char b[2048];
sprintf(b,"/xxx/darknet/data/out/%s",GetFilename(path));//"/xxx/darknet/data"修改成自己的路径
save_image(im, b);
printf("save %s successfully!\n",GetFilename(path));
#ifdef OPENCV
cvNamedWindow("predictions", CV_WINDOW_NORMAL);
if(fullscreen){
cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
}
show_image(im, "predictions");
cvWaitKey(0);
cvDestroyAllWindows();
#endif
}
free_image(im);
free_image(sized);
if (filename) break;
}
}
}
}
在前面添加函数:
#include "darknet.h"
#include <sys/stat.h>
#include<stdio.h>
#include<time.h>
#include<sys/types.h>
static int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90};
char *GetFilename(char *p)
{
static char name[20]={""};
char *q = strrchr(p,'/') + 1;
strncpy(name,q,6);
return name;
}
在darknet下重新编译,make.
终端输入测试指令:
./darknet detector test cfg/xx.data cfg/xx.cfg backup/xx.weights
加载完模型后出现Enter Image Path:
这个时候,输入test.txt的路径。test.txt即放测试图片路径的文档。之后测试结果就保存在test_detector函数中写的路径下的out文件夹内。
5. 保存视频检测结果
./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights <video file>,测试视频指令
https://blog.csdn.net/sinat_33718563/article/details/79964758,保存视频测试结果。
6. 修改模型保存频率
在examples文件夹中的detector.c文件中,if(i%10000==0 || (i < 1000 && i%100 == 0)) 表示迭代次数1000以下,每一百次保存一次模型,1000次以上,每1W次保存一次模型,可以根据需要自行修改。
if(i%100==0){//这里改的是每多少次保存一个back模型,这个会覆盖掉之前的back模型
#ifdef GPU
if(ngpus != 1) sync_nets(nets, ngpus, 0);
#endif
char buff[256];
sprintf(buff, "%s/%s.backup", backup_directory, base);
save_weights(net, buff);
}
if(i%1000==0 || (i < 1000 && i%100 == 0)){//这里改的是每多少次保存一个模型,不会覆盖
#ifdef GPU
if(ngpus != 1) sync_nets(nets, ngpus, 0);
#endif
char buff[256];
sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i);
save_weights(net, buff);
}
修改文件后,要重新编译make,才能生效。如果不行,就先make clean,再make。
7. 保存训练日志,参数可视化
训练指令后面加2>1 | tee train_log.txt(后面这个txt名自定义,放在darknet文件下,记录训练日志),tee保存网络加载信息和checkout点保存信息。
./darknet detector train cfg/xx.data cfg/xx.cfg backup/xx.weights 2>1 | tee train_log.txt
日志参数可视化:
https://blog.csdn.net/yudiemiaomiao/article/details/72469135,亲测可用。三个文件在电脑上,先用convert_log.txt文件,分割log.txt成两个txt文档。之后分别运行log.py和iou.py文件(记得修改文件里面的txt路径及名称),可得到loss,avg loss变化图。(按照原来的x%10==9,loss只出现图,没有线。改成和iou一样,if(x%10==0 or x%10==9)就好了,不造咋回事,没细究。)