编程艺术剖析 darknet read_data_cfg 接口
Posted 极智视界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编程艺术剖析 darknet read_data_cfg 接口相关的知识,希望对你有一定的参考价值。
欢迎关注我的公众号 [极智视界],获取我的更多笔记分享
O_o
>_<
o_O
O_o
~_~
o_O
本文介绍一下 darknet 中 read_data_cfg 接口实现。
这个接口在 darknet 中的作用主要是加载 .data 中的数据,功能比较简单。
1、darknet 数据加载例程
要说这个接口首先需要介绍一下 darknet 目标检测的数据加载流程:
以 voc 格式数据来说,对于模型推理一般首先需要加载 voc.data 中的 classes 和 names,而其中的 train 和 valid 主要是训练的时候需要的数据集,来看一下 vod.data 的样子:
通过 voc.data 中的 names 来调 .names,.names 中即为目标检测的类别名称,上面的 classes 为类别数,来看一下 voc.names:
接着就是 cfg 和 weights,这个没什么好说的,就是模型结构文件和权重文件。
2、read_data_cfg 接口
来看一下 read_data_cfg 的调用:
list *options = read_data_cfg(datacfg);
可以看到 read_data_cfg 的返回是指向 list 的指针,而 list 就是之前写的这篇文章《【编程艺术】剖析 darknet C 链表实现》中的链表了。
/// list.h
typedef struct list{
int size;
node *front;
node *back;
} list;
来看一下 read_data_cfg 的实现:
/// 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){ // fgetl(file)逐行读入
++nu;
strip(line); // 去除line中的空格、制表和换行符,并在最后加'\\0'结束
switch(line[0]){ // 判断line的首字符是否为'\\0'、'#'、';',若是则释放line并跳出
case '\\0':
case '#':
case ';':
free(line);
break;
default:
if(!read_option(line, options)){ // 将line读入到链表options
fprintf(stderr, "Config file error line %d, could parse: %s\\n", nu, line);
free(line);
}
break;
}
}
fclose(file);
return options;
}
上面实现中的三个函数拿出来说一说:fgetl、strip、read_option。
fgetl 的功能是逐行读入,来看一下 fgetl 的实现:
/// utils.c
// 逐行读取
char *fgetl(FILE *fp)
{
if(feof(fp)) return 0; // 判断是否读到文件尾
size_t size = 512;
char* line = (char*)xmalloc(size * sizeof(char)); // 限定一次最多读512 char*数据
if(!fgets(line, size, fp)){ // fgets是从fp流中读取size个字符到line,当读取到size-1个字符或换行符或到达文件尾时,则停止。即逐行读
free(line);
return 0;
}
size_t curr = strlen(line); // 获取行的大小
while((line[curr-1] != '\\n') && !feof(fp)){ // 防止实际文件行大于size的处理逻辑
if(curr == size-1){ // 将size扩容后再存入
size *= 2;
line = (char*)xrealloc(line, size * sizeof(char)); // xrelloc调整已开辟空间大小
}
size_t readsize = size-curr;
if(readsize > INT_MAX) readsize = INT_MAX-1;
fgets(&line[curr], readsize, fp);
curr = strlen(line);
}
if(curr >= 2)
if(line[curr-2] == 0x0d) line[curr-2] = 0x00;
if(curr >= 1)
if(line[curr-1] == 0x0a) line[curr-1] = 0x00;
return line;
}
再来看一下 strip 的实现:
/// utils.c
// 去除空格、制表和换行符,并在最后加'\\0'结束
void strip(char *s)
{
size_t i;
size_t len = strlen(s);
size_t offset = 0; // 偏移
for(i = 0; i < len; ++i){
char c = s[i];
if(c==' '||c=='\\t'||c=='\\n'||c =='\\r'||c==0x0d||c==0x0a) ++offset; // 去除' '、'\\t'、'\\n'、'\\r'、0x0d即回车符、0x0a即换行符
else s[i-offset] = c;
}
s[len-offset] = '\\0'; // 尾巴加上'\\0'
}
最后来看一下 read_option 的实现:
/// option_list.c
// 将字符串读入链表
int read_option(char *s, list *options)
{
size_t i;
size_t len = strlen(s);
char *val = 0;
for(i = 0; i < len; ++i){ // voc.data中每行字符都有'=', 取'='后数据
if(s[i] == '='){
s[i] = '\\0';
val = s+i+1;
break;
}
}
if(i == len-1) return 0; // 判断'='后是否有数据,若没有则跳出
char *key = s; // 将'='前的键值赋给key
option_insert(options, key, val); // 将数据插入链表操作
return 1;
}
来看一下将数据插入链表方法 option_insert:
/// option_list.c
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_insert 可查阅我之前写的《【编程艺术】剖析 darknet C 链表实现》中的 list_insert 实现注释,说的比较清楚。
这里介绍分析了一下 darknet 中 read_data_cfg 接口,也可以帮助温固 C 语言基础。
a4f9e65a5eb08f22307707339c02b5839240d8ab7a0977f&token=277768597&lang=zh_CN#rd)》中的 list_insert 实现注释,说的比较清楚。
这里介绍分析了一下 darknet 中 read_data_cfg 接口,也可以帮助温固 C 语言基础。
【公众号传送】
扫描下方二维码即可关注我的微信公众号【极智视界】,获取更多AI经验分享,让我们用极致+极客的心态来迎接AI !
以上是关于编程艺术剖析 darknet read_data_cfg 接口的主要内容,如果未能解决你的问题,请参考以下文章
编程艺术剖析 darknet 链表查找 option_find_xx 接口
编程艺术剖析 darknet parse_network_cfg 接口