train完数据之后下面把测试的过程记录下吧,train结束后会在backup文件夹中有对应最终的yolov3_final.weights(如果你没改源码的情况下)。
darknet提供了用于评估模型的三个命令,即
./darknet detector test cfg/coco.data cfg/yolov3.cfg backup/yolov3_final.weights data/dogs.jpg
/*不现实评价指标,输入图片路径,只显示框好后的图片和类别、置信率*/
./darknet detector valid cfg/coco.data cfg/yolov3.cfg backup/yolov3_final.weights
在./results/comp4_det_test_[类名].txt里保存测试结果*/
./darknet detector recall cfg/coco.data cfg/yolov3.cfg backup/yolov3_final.weights
/*依次ID:图片序号从0开始,correct:累计检测正确的总框数,total:累计的总ground truth数,RPs/Img: 累计的总proposals/已检测图片数,IOU,Recall: correct / total,proposals:累计的总框数,Precision: correct / proposals*/
其中recall需要修改源码。下面会说。
上图中第一个test最常用啦。这默认是单张图片的detect结果 在这里说一个刚train结束后的bug,就是coco数据的background类,yolov3默认是81分类啦,我在刚开始project.names只按照annotations中的class name写了,漏掉了背景类,于是出现了标签错乱的问题,detect的结果都是跟正确的label错1位,于是修改了project.name,把第一行默认为background,做成了81行的class name的签,就能对应上结果了。
测试单张图片,需要编译时有OpenCV支持:./darknet detector test
测试时还可以用-thresh和-hier选项指定对应参数。
测试多张图片,就需要更改darknet源码啦。
找到exmaple/detetoc.c
1.用下面代码替换detector.c文件(example文件夹下)的void test_detector函数(注意有3处要改成自己的路径)
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 {
printf("Enter Image Path: ");
fflush(stdout);
input = fgets(input, 256, stdin);
if(!input) return;
strtok(input, "\n");
list *plist = get_paths(input);
char **paths = (char **)list_to_array(plist);
printf("Start Testing!\n");
int m = plist->size;
if(access("/media/pengjk/30213d25-fae8-4100-9d8b-9aed2bb5a8df/darknet/data/out",0)==-1)//"/home/FENGsl/darknet/data"修改成自己的路径
{
if (mkdir("/media/pengjk/30213d25-fae8-4100-9d8b-9aed2bb5a8df/darknet/data/out",0777))//"/home/FENGsl/darknet/data"修改成自己的路径
{
printf("creat file bag failed!!!");
}
}
for(i = 0; i < m; ++i){
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,"/media/pengjk/30213d25-fae8-4100-9d8b-9aed2bb5a8df/darknet/data/out/%s",GetFilename(path));//"/home/FENGsl/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;
}
}
}
}
2,在前面添加*GetFilename(char *p)函数(注意后面的注释)
#include "darknet.h"
#include
#include
#include
#include
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);//注意后面的6,如果你的测试集的图片的名字字符(不包括后缀)是其他长度,请改为你需要的长度(官方的默认的长度是6)
return name;
}
3.重新make 记得先make clean。
在这里有个bug啊我遇到的,make失败总说找不到opencv中的window_normal_windows 但是明明已经先编译了opencv! github上有人也遇到了but没有相关的解释啊贼难受, 最后就死马当活马医直接不用opencv,把makefile文件中的opencv重新改为0,编译通过。
4. 执行下面的命令。
./darknet detector test cfg/coco.data cfg/yolov3.cfg backup/yolov3_final.weights
然后会要你输入image的路径了,然后就复制过来即可,在coco.data文件(你自己定义的文件中的valid)的路径,复制过来即可。
5.坐等data/out文件夹下的结果吧 贴上我的result
先通过valid命令生成对应的结果啦,
./darknet detector valid cfg/coco.data cfg/yolov3.cfg backup/yolov3_final.weights
在./results/comp4_det_test_[类名].txt里保存测试结果*/
以上代码是使用的yolo网络自带的valid函数接口来测试大量的图片,把检测结果保存在txt文件里, -out 后面的“”会自动生成 “类名.txt” 在darknet / result 目录下。
因为最终我只需要测试无人机这一类,就以这一类为例了。
result下查看结果,打开对应的txt,其中的每一行代表
图片名字,这一类的框框的置信度,坐标信息,这是voc格式的。
将py-faster-rcnn/lib/datasets/voc_eval.py拷贝到darknet根目录。
voc_eval.py下载:https://github.com/rbgirshick/py-faster-rcnn/blob/master/lib/datasets/voc_eval.py
新建自己的comput_mAP.py文件,如下,针对my无人机类测试
其中第一个参数为自己的results的位置,第二个参数为test图片的annotation的xml所在位置,第三个参数是保存所有test路径的txt文件(注意,这里的txt文件必须不带最后的.jpg后缀,不是.data文件中的那个valid了!重新生成一个把后缀split掉。)第四个参数即你要检测的类别,这里喔也有问题,生成的result带有com4_test的前缀,但这里必须要跟coco.name中的照应上,如果你直接输入带有com4_test的会gg,所以稳妥点把生成的txt rename成对应的 class name。
然后执行py文件,就能生成对应的mAP了。
大规模coco+自己的数据训练了5万次的结果 0.78mAP。后面在优化下应该能达到80+。
重复执行,检测其他类别需要删除 ./darknet/annots.pkl ,或者改变compute_mAP.py中pkl文件保存的路径
需要修改detector.c源码:
替换list *plist = get_paths("data/coco_val_5k.list");为list *plist=get_paths("voc/train.txt");自己的测试集文本
结果:最后一列为recall值。
我把recall的结果重定向到txt文件中了,结果如下:
这对应的是单张图片的recall,如果想算测试集上总的recall,自己写个脚本累加一下就行了。
附:同时输出所有类的mAP代码,我没有用,仅供参考:
from voc_eval import voc_eval
import os
current_path = os.getcwd()
results_path = current_path+"/results"
sub_files = os.listdir(results_path)
mAP = []
for i in range(len(sub_files)):
class_name = sub_files[i].split(".txt")[0] rec, prec, ap = voc_eval('/home/peidashun/projects/darknet/results/{}.txt', '/home/peidashun/projects/darknet/voc/VOCdevkit/VOC2018/Annotations/{}.xml', '/home/peidashun/projects/darknet/voc/2018_test.txt', class_name, '/home/peidashun/projects/darknet/voc/VOCdevkit/VOC2018/mAP')
print("{} :\t {} ".format(class_name, ap))
mAP.append(ap)
mAP = tuple(mAP)
print("***************************")
print("mAP :\t {}".format( float( sum(mAP)/len(mAP)) ))