实验环境:Ubuntu 18.4.0.1
文本编辑器:Vscode
yolo官网上,调用主函数的命令如下:
./darknet detect cfg/yolov3.cfg cfg/yolov3.weights data/person.jpg
可以看出输入的参数分别是(他们都是以字符串形式输入的):
0:./darknet 1:detect 2:cfg/yolov3.cfg 3:cfg/yolov3.weights 4:data/person.jpg
这个主函数所在的位置为/darknet/examples/darknet.c 源代码如下:
int main(int argc, char **argv)
{
//test_resize("data/bad.jpg");
//test_box();
//test_convolutional_layer();
if(argc < 2){
fprintf(stderr, "usage: %s <function>\n", argv[0]);
return 0;
}
gpu_index = find_int_arg(argc, argv, "-i", 0);
if(find_arg(argc, argv, "-nogpu")) {
gpu_index = -1;
}
#ifndef GPU
gpu_index = -1;
#else
if(gpu_index >= 0){
cuda_set_device(gpu_index);
}
#endif
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], "super")){
run_super(argc, argv);
} else if (0 == strcmp(argv[1], "lsd")){
run_lsd(argc, argv);
} else if (0 == strcmp(argv[1], "detector")){
run_detector(argc, argv);
} else if (0 == strcmp(argv[1], "detect")){
float thresh = find_float_arg(argc, argv, "-thresh", .5);
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, "-out", 0);
int fullscreen = find_arg(argc, argv, "-fullscreen");
test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
} else if (0 == strcmp(argv[1], "cifar")){
run_cifar(argc, argv);
} else if (0 == strcmp(argv[1], "go")){
run_go(argc, argv);
} else if (0 == strcmp(argv[1], "rnn")){
run_char_rnn(argc, argv);
} else if (0 == strcmp(argv[1], "coco")){
run_coco(argc, argv);
} else if (0 == strcmp(argv[1], "classify")){
predict_classifier("cfg/imagenet1k.data", argv[2], argv[3], argv[4], 5);
} else if (0 == strcmp(argv[1], "classifier")){
run_classifier(argc, argv);
} else if (0 == strcmp(argv[1], "regressor")){
run_regressor(argc, argv);
} else if (0 == strcmp(argv[1], "isegmenter")){
run_isegmenter(argc, argv);
} else if (0 == strcmp(argv[1], "segmenter")){
run_segmenter(argc, argv);
} else if (0 == strcmp(argv[1], "art")){
run_art(argc, argv);
} else if (0 == strcmp(argv[1], "tag")){
run_tag(argc, argv);
} else if (0 == strcmp(argv[1], "3d")){
composite_3d(argv[2], argv[3], argv[4], (argc > 5) ? atof(argv[5]) : 0);
} else if (0 == strcmp(argv[1], "test")){
test_resize(argv[2]);
} else if (0 == strcmp(argv[1], "nightmare")){
run_nightmare(argc, argv);
} else if (0 == strcmp(argv[1], "rgbgr")){
rgbgr_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "reset")){
reset_normalize_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "denormalize")){
denormalize_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "statistics")){
statistics_net(argv[2], argv[3]);
} else if (0 == strcmp(argv[1], "normalize")){
normalize_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "rescale")){
rescale_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "ops")){
operations(argv[2]);
} else if (0 == strcmp(argv[1], "speed")){
speed(argv[2], (argc > 3 && argv[3]) ? atoi(argv[3]) : 0);
} else if (0 == strcmp(argv[1], "oneoff")){
oneoff(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "oneoff2")){
oneoff2(argv[2], argv[3], argv[4], atoi(argv[5]));
} else if (0 == strcmp(argv[1], "print")){
print_weights(argv[2], argv[3], atoi(argv[4]));
} else if (0 == strcmp(argv[1], "partial")){
partial(argv[2], argv[3], argv[4], atoi(argv[5]));
} else if (0 == strcmp(argv[1], "average")){
average(argc, argv);
} else if (0 == strcmp(argv[1], "visualize")){
visualize(argv[2], (argc > 3) ? argv[3] : 0);
} else if (0 == strcmp(argv[1], "mkimg")){
mkimg(argv[2], argv[3], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), argv[7]);
} else if (0 == strcmp(argv[1], "imtest")){
test_resize(argv[2]);
} else {
fprintf(stderr, "Not an option: %s\n", argv[1]);
}
return 0;
}
看起来老长一段了,有点不知所措,但是整体上可以分为三个部分来看:
部分一:
if(argc < 2){
fprintf(stderr, "usage: %s <function>\n", argv[0]);
return 0;
}
这里判断输入参数的个数有没有小于2个,如果小于2个,直接输出提示信息,并结束。
如果参数数量大于2再做下一步
这里我说一下主函数的头部:
int main(int argc, char **argv)
argc的值为输入参数的数量,argv可以接收任意个数的参数(都是字符串),所以调用argv时,常常是以数组的形式(argv[0],argv[1]...),所以argc的值也就是argv中的元素个数
所以第一部分代码,如果条件达成,会输出argv[0]中的信息
具体怎么样,我们来试一试,我自己创建了一个新项目,来进行各种尝试,以免把源代码搞乱
源代码,创建了动态的库文件,可以跨文件夹调用,我就简单一点,把所有文件放在统一目录下(其实是不会弄)
所以我们仿照源码创建darknet.c darknet.h两个文件,并在darknet.c文件中写如下代码:
/*
* @Author: BTboay
* @Date: 2019-12-11 20:07:46
* @LastEditTime: 2019-12-12 18:29:48
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /yolov3-copy/examples/darknet.c
*/
//#include "./darknet.h"
//#include "./utils.c"
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
//#include <string.h>
//extern void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen);
/*
void average(int argc, char *argv[])
{
//cfg文件目录
char *cfgfile = argv[2];
//输出文件目录
char *outfile = argv[3];
gpu_index = -1;
network *net = parse_network_cfg(cfgfile)
network *sum = parse_network_cfg(cfgfile)
}
*/
/*主函数调用命令
./darknet detect cfg/yolov3.cfg cfg/yolov3.weights data/dog.jpg
argv[0]->./darknet
argv[1]->detect
argv[2]->cfg/yolov3.cfg
argv[3]->cfg/yolov3.weights
argv[4]->data/dog.jpg
接收5个参数
*/
int main(int argc, char **argv)
{
//根据输入参数选择调用函数和参数设置
//参数数量异常
if (argc < 2)
{
fprintf(stderr, "usage: %s <function>\n", argv[0]);
return 0;
}
/*
else if (0 == strcmp(argv[1], "detect"))
{
//./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg -thresh 0
float thresh = find_float_arg(argc, argv, (char*)"-thresh", .5);
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, (char*)"-out", 0);
int fullscreen = find_arg(argc, argv, (char*)"-fullscreen");
//test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
}
*/
return 0;
}
就是添加了头文件,主函数中写了部分一的代码,有很多注释行,不用管,毕竟我的进度是比博客进度快的
编译过后,在目录下出现darknet.main.out文件
在当前目录下打开终端输入:
./darknet.main.out
只输入了一个参数,所以 按照我们的分析,会输出一串信息,其中带有“./darknet.main.out”,结果如下(注意第二行):
(base) btboay@btboay-Lenovo-ideapad-510-15ISK:~/桌面/yolov3-copy$ ./darknet.main.out
usage: ./darknet.main.out <function>
完全一致,在源代码下测试也是一致的
部分二:
gpu_index = find_int_arg(argc, argv, "-i", 0);
if(find_arg(argc, argv, "-nogpu")) {
gpu_index = -1;
}
#ifndef GPU
gpu_index = -1;
#else
if(gpu_index >= 0){
cuda_set_device(gpu_index);
}
#endif
这一段主要用于GPU训练测试的条件和参数设置,这里先不管,因为我用CPU测试和训练
部分三:这是重点
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], "super")){
run_super(argc, argv);
} else if (0 == strcmp(argv[1], "lsd")){
run_lsd(argc, argv);
} else if (0 == strcmp(argv[1], "detector")){
run_detector(argc, argv);
} else if (0 == strcmp(argv[1], "detect")){
float thresh = find_float_arg(argc, argv, "-thresh", .5);
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, "-out", 0);
int fullscreen = find_arg(argc, argv, "-fullscreen");
test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
} else if (0 == strcmp(argv[1], "cifar")){
run_cifar(argc, argv);
} else if (0 == strcmp(argv[1], "go")){
run_go(argc, argv);
} else if (0 == strcmp(argv[1], "rnn")){
run_char_rnn(argc, argv);
} else if (0 == strcmp(argv[1], "coco")){
run_coco(argc, argv);
} else if (0 == strcmp(argv[1], "classify")){
predict_classifier("cfg/imagenet1k.data", argv[2], argv[3], argv[4], 5);
} else if (0 == strcmp(argv[1], "classifier")){
run_classifier(argc, argv);
} else if (0 == strcmp(argv[1], "regressor")){
run_regressor(argc, argv);
} else if (0 == strcmp(argv[1], "isegmenter")){
run_isegmenter(argc, argv);
} else if (0 == strcmp(argv[1], "segmenter")){
run_segmenter(argc, argv);
} else if (0 == strcmp(argv[1], "art")){
run_art(argc, argv);
} else if (0 == strcmp(argv[1], "tag")){
run_tag(argc, argv);
} else if (0 == strcmp(argv[1], "3d")){
composite_3d(argv[2], argv[3], argv[4], (argc > 5) ? atof(argv[5]) : 0);
} else if (0 == strcmp(argv[1], "test")){
test_resize(argv[2]);
} else if (0 == strcmp(argv[1], "nightmare")){
run_nightmare(argc, argv);
} else if (0 == strcmp(argv[1], "rgbgr")){
rgbgr_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "reset")){
reset_normalize_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "denormalize")){
denormalize_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "statistics")){
statistics_net(argv[2], argv[3]);
} else if (0 == strcmp(argv[1], "normalize")){
normalize_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "rescale")){
rescale_net(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "ops")){
operations(argv[2]);
} else if (0 == strcmp(argv[1], "speed")){
speed(argv[2], (argc > 3 && argv[3]) ? atoi(argv[3]) : 0);
} else if (0 == strcmp(argv[1], "oneoff")){
oneoff(argv[2], argv[3], argv[4]);
} else if (0 == strcmp(argv[1], "oneoff2")){
oneoff2(argv[2], argv[3], argv[4], atoi(argv[5]));
} else if (0 == strcmp(argv[1], "print")){
print_weights(argv[2], argv[3], atoi(argv[4]));
} else if (0 == strcmp(argv[1], "partial")){
partial(argv[2], argv[3], argv[4], atoi(argv[5]));
} else if (0 == strcmp(argv[1], "average")){
average(argc, argv);
} else if (0 == strcmp(argv[1], "visualize")){
visualize(argv[2], (argc > 3) ? argv[3] : 0);
} else if (0 == strcmp(argv[1], "mkimg")){
mkimg(argv[2], argv[3], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), argv[7]);
} else if (0 == strcmp(argv[1], "imtest")){
test_resize(argv[2]);
} else {
fprintf(stderr, "Not an option: %s\n", argv[1]);
}
这是一段昂长的条件判度,通过输入参数来选取要运行的函数,0 == strcmp(argv[1], "average"),这是判断的核心语句,初始值为0代表false,strcmp(str1, str2)判断两个字符串str1,str2是否相同,相同为true,不同为false,所以通过这种方式来选择函数运行。
例如,调用语句里./darknet detect cfg/yolov3.cfg cfg/yolov3.weights data/person.jpg
第二个参数(argv[1])是detect,所以找找判断条件为detect的,找到的代码如下:
else if (0 == strcmp(argv[1], "detect"))
{
//./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg -thresh 0
float thresh = find_float_arg(argc, argv, (char*)"-thresh", .5);
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, (char*)"-out", 0);
int fullscreen = find_arg(argc, argv, (char*)"-fullscreen");
test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
}
我们把他添加到我们测试的文件里
我们一行一行的分析,测试。
现在我们的测试代码中主函数是这样的:
int main(int argc, char **argv)
{
//根据输入参数选择调用函数和参数设置
//参数数量异常
if (argc < 2)
{
fprintf(stderr, "usage: %s <function>\n", argv[0]);
return 0;
}
else if (0 == strcmp(argv[1], "detect"))
{
//./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg -thresh 0
float thresh = find_float_arg(argc, argv, (char*)"-thresh", .5);
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, (char*)"-out", 0);
int fullscreen = find_arg(argc, argv, (char*)"-fullscreen");
test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
}
return 0;
}
第一行:float thresh = find_float_arg(argc, argv, (char*)"-thresh", .5)
这个函数find_float_arg定义在/darknet/src/utils.c,代码如下:
float find_float_arg(int argc, char **argv, char *arg, float def)
{
int i;
for(i = 0; i < argc-1; ++i){
if(!argv[i]) continue;
if(0==strcmp(argv[i], arg)){
def = atof(argv[i+1]);
del_arg(argc, argv, i);
del_arg(argc, argv, i);
break;
}
}
return def;
}
那我们也在我们的测试目录下创建utils.c文件,并把代码加进去,还有头文件,并且在darknet.c中引用utils.c
#include "./utils.c"
find_float_arg函数将参数thresh后面跟的值进行返回,atof函数将字符串转换为浮点数
del_arg函数定义也在utils.c中,代码如下:
void del_arg(int argc, char **argv, int index)
{
int i;
for(i = index; i < argc-1; ++i) argv[i] = argv[i+1];
argv[i] = 0;
}
目的是将参数清空
来测试一下,在调用结束后printf出thresh的值,测试的时候将其他不测试的代码行注释掉,结果:
(base) btboay@btboay-Lenovo-ideapad-510-15ISK:~/桌面/yolov3-copy$ ./darknet.main.out detect -thresh 0.5
0.500000
第二行打印出了值,完全一致,从判断条件可以看出-thresh参数可以放在任何位置,但是值必须紧跟其后:
(base) btboay@btboay-Lenovo-ideapad-510-15ISK:~/桌面/yolov3-copy$ ./darknet.main.out detect 1 2 3 4 5 6 -thresh 0.5
0.500000
我在detect后面加了6个参数,结果依然完全一致
之后的几行代码的作用是一致的:
char *filename = (argc > 4) ? argv[4]: 0;
char *outfile = find_char_arg(argc, argv, (char*)"-out", 0);
int fullscreen = find_arg(argc, argv, (char*)"-fullscreen");
函数代码也在utils.c中,如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <unistd.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <sys/time.h>
#include "utils.h"
void del_arg(int argc, char **argv, int index)
{
int i;
for(i = index; i < argc-1; ++i) argv[i] = argv[i+1];
argv[i] = 0;
}
int find_arg(int argc, char* argv[], char *arg)
{
int i;
for(i = 0; i < argc; ++i) {
if(!argv[i]) continue;
if(0==strcmp(argv[i], arg)) {
del_arg(argc, argv, i);
return 1;
}
}
return 0;
}
float find_float_arg(int argc, char **argv, char *arg, float def)
{
int i;
//循环遍历输入参数
for (i=0; i < argc-1; ++i)
{
//判断是否为空
if (!argv[i]) continue;
//查找参数thresh
if (0 == strcmp(argv[i], arg))
{
//字符串转换为浮点数
def = atof(argv[i+1]);
del_arg(argc, argv, i);
del_arg(argc, argv, i);
break;
}
}
return def;
}
char *find_char_arg(int argc, char **argv, char *arg, char *def)
{
int i;
for(i = 0; i < argc-1; ++i){
if(!argv[i]) continue;
if(0==strcmp(argv[i], arg)){
def = argv[i+1];
del_arg(argc, argv, i);
del_arg(argc, argv, i);
break;
}
}
return def;
}
那么其核心的最后一句代码:
test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen);
是测试的主要过程,将在下一片文章中重点讲解。