实验环境: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);

是测试的主要过程,将在下一片文章中重点讲解。