目标检测 - 框架之 darknet- 数据读取
darknet-1之数据读取
之前一直在用darknet的框架训练yolo,但是对于具体的框架内不的机制还是不熟,最近下定决定深入了解这个框架的内部机制,这是参考好了很多博主的博客,感谢他们的帮助。希望可以和各位同仁一起学习,共同进步。
本次的源码阅读参考的https://github.com/AlexeyAB/darknet,俗称A版darknet.
author: dreamer
date: 2020-11-11
首先需要明确的一点是因为darknet是用C, CUDA实现的,因此其中对于复杂层,网络,函数的实现都是经过的结构体,这里先重点关注下面这几个:
//node ,链表中节点的基本单元
typedef struct node{
void *val;
struct node *next;
struct node *prev;
} node;
//双向链接,记录头节点,尾节点,和链表的长度,会存储网络的所有参数
typedef struct list{
int size;
node *front;
node *back;
} list;
首先找到入口函数 darknet.c 中的main()
if (0 == strcmp(argv[1], "average")){
average(argc, argv);
} else if (0 == strcmp(argv[1], "yolo")){
run_yolo(argc, argv);
} else if (0 == strcmp(argv[1], "voxel")){
run_voxel(argc, argv);
} else if (0 == strcmp(argv[1], "super")){
run_super(argc, argv);
} else if (0 == strcmp(argv[1], "detector")){
// 关注主要部分, 检测代码
//======================
//训练的部分基本包含了整个框架
//汇总所有的功能,因此我们跟着一种大佬进来学习学习。
// author: dreamer
//date: 2020-11-11
//=======================
run_detector(argc, argv);
} else if (0 == strcmp(argv[1], "detect")){
float thresh = findfloatarg(argc, argv, "-thresh", .24);
int extoutput = findarg(argc, argv, "-ext_output");
char *filename = (argc > 4) ? argv[4]: 0;
testdetector("cfg/coco.data", argv[2], argv[3], filename, thresh, 0.5, 0, extoutput, 0, NULL, 0, 0);
} else if (0 == strcmp(argv[1], "cifar"))
...
接下来进入run_detector(),前面的部分都是对参数的提取,重点在下面
if (0 == strcmp(argv[2], "test")) testdetector(datacfg, cfg, weights, filename, thresh, hierthresh, dontshow, extoutput, savelabels, outfile, letterbox, benchmark_layers);
//=======================
// train arg,会调用下面这个函数
//=======================
else if (0 == strcmp(argv[2], "train")) traindetector(datacfg, cfg, weights, gpus, ngpus, clear, dontshow, calcmap, mjpegport, showimgs, benchmarklayers, chart_path);
else if (0 == strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights, outfile);
else if (0 == strcmp(argv[2], "recall")) validatedetectorrecall(datacfg, cfg, weights);
else if (0 == strcmp(argv[2], "map")) validatedetectormap(datacfg, cfg, weights, thresh, iouthresh, mappoints, letter_box, NULL);
else if (0 == strcmp(argv[2], "calcanchors")) calcanchors(datacfg, numofclusters, width, height, show);
else if (0 == strcmp(argv[2], "draw")) {
int it_num = 100;
drawobject(datacfg, cfg, weights, filename, thresh, dontshow, itnum, letterbox, benchmark_layers);
}
else if (0 == strcmp(argv[2], "demo")) {
list *options = readdatacfg(datacfg);
int classes = optionfindint(options, "classes", 20);
char *namelist = optionfind_str(options, "names", "data/names.list");
char **names = getlabels(namelist);
if (filename)
if (strlen(filename) > 0)
if (filename[strlen(filename) - 1] == 0x0d) filename[strlen(filename) - 1] = 0;
demo(cfg, weights, thresh, hierthresh, camindex, filename, names, classes, avgframes, frameskip, prefix, outfilename,
mjpegport, dontdrawbbox, jsonport, dontshow, extoutput, letterbox, timelimitsec, httpposthost, benchmark, benchmark_layers);
freelistcontents_kvp(options);
free_list(options);
}
else printf(" There isn't such command: %s", argv[2]);
...
,接着进入train_detector()
//这里进入配置文件的读取
list *options = readdatacfg(datacfg);
//====================================
//readdatacfg的定义, 位于option_list.c
list read_data_cfg(char filename)
{
FILE *file = fopen(filename, "r");
if(file == 0) file_error(filename);
char *line;
int nu = 0;
list *options = make_list(); // 初始化链表,后面每次读一行参数的时候,会把参数挂上这个总的链表上
while((line=fgetl(file)) != 0){
++nu;
strip(line);
switch(line[0]){
case '\0':
case '#':
case ';':
free(line);
break;
default:
// read_option 实现具体如何将读到的哪一行参数解析出来,并且加入链表的操作。
if(!read_option(line, options)){
fprintf(stderr, "Config file error line %d, could parse: %s\n", nu, line);
free(line);
}
break;
}
}
fclose(file);
return options;
}
//readoption() 位于optionlist.c
//分离出参数的key和value,然后进一步调用optioninsert()挂到options,也就是readdata_cfg()中最开始创建的那个总的参数list里面
int read_option(char s, list options)
{
size_t i;
size_t len = strlen(s);
char *val = 0;
for(i = 0; i < len; ++i){
if(s[i] == '='){
s[i] = '\0';
val = s+i+1;
break;
}
}
if(i == len-1) return 0;
char *key = s;
option_insert(options, key, val);
return 1;
}
// option_insert() ,创建一个kvp
/*
typedef struct{
char *key;
char *val;
int used;
} kvp;
void option_insert(list l, char key, char *val)
{
kvp p = (kvp)xmalloc(sizeof(kvp));
p->key = key;
p->val = val;
p->used = 0;
list_insert(l, p);
}
以上就完成了数据配置文件的读取。
总结一下,对于数据文件的配置信息的读取,是通过先创建一个空的list, 然后每次读取一行配置信息出,通过分离出的key 和value,组建一个kvp,顺序的挂上这个创建的list,在需要使用访问文件信息的时候对这个list 读取即可完成。
评论