본문 바로가기

IT/Deeplearning

[Object Detection / YOLO DARKNET] object detection code review :: option_find_str -[3]

[Object Detection / YOLO DARKNET] object detection code review :: read_data_cfg -[1]

[Object Detection / YOLO DARKNET] object detection code review :: read_data_cfg -[2]


이번 포스팅은 char *name_list = option_find_str에 대한 코드 리뷰를 진행하겠습니다.


1. Concept Overview

이전에 read_data_cfg에 대한 리뷰를 진행할 때, 아래의 그림과 같이 어떻게 *.data 파일이 리스트형태로 저장되는지 확인하였습니다.


이번에 리뷰하는 char *name_list = option_find_str(options, "names", "data/names.list") 함수는read_data_cfg로 만들어진

options라는 리스트 구조체에서 "names"라는 key값을 찾아서 그에 대한 값(val)를 받아내는 함수였습니다.

해당 값을 받으면서, 해당 구조체의 멤버는 이미 사용을 했다라는 의미로 flag used를 1로 변경해주는 것을 볼 수 있습니다.

이를 그림으로 표현하면 아래와 같습니다.


2. test_detector :: examples/detector.c


현재 우리는 YOLO Darknet 코드 상에서 detector.c의 test_detector 파트만 코드 리뷰를 하고 있는 중입니다.

test_detector 파트의 코드는 아래와 같습니다.

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;
    int j;
    float nms=.3;
    while(1){
        if(filename){
            strncpy(input, filename, 256);
        } else {
            printf("Enter Image Path: ");
            fflush(stdout);
            input = fgets(input, 256, stdin);
            if(!input) return;
            strtok(input, "\n");
        }
        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];

        box *boxes = calloc(l.w*l.h*l.n, sizeof(box));
        float **probs = calloc(l.w*l.h*l.n, sizeof(float *));
        for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(l.classes + 1, sizeof(float *));
        float **masks = 0;
        if (l.coords > 4){
            masks = calloc(l.w*l.h*l.n, sizeof(float*));
            for(j = 0; j < l.w*l.h*l.n; ++j) masks[j] = calloc(l.coords-4, sizeof(float *));
        }

        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);
        get_region_boxes(l, im.w, im.h, net->w, net->h, thresh, probs, boxes, masks, 0, 0, hier_thresh, 1);
        //if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
        if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, l.classes, nms);
        draw_detections(im, l.w*l.h*l.n, thresh, boxes, probs, masks, names, alphabet, l.classes);
        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);
        free(boxes);
        free_ptrs((void **)probs, l.w*l.h*l.n);
        if (filename) break;
    }
}


여기서 저번 포스팅까지는 1번째 라인의 list *options = read_data_cfg(datacfg) 에 대한 코드 리뷰를 진행하였습니다.

이번 포스팅은 그 아래 2번째 라인의 char *name_list = option_find_str(options, "names", "data/names.list") 함수에 대해서 리뷰하도록 하겠습니다.


3. option_find_str :: src/option_list.c

option_find_str의 구성은 다음과 같습니다.

char *option_find_str(list *l, char *key, char *def)
{
    char *v = option_find(l, key);
    if(v) return v;
    if(def) fprintf(stderr, "%s: Using default '%s'\n", key, def);
    return def;
}


1.  list 구조체 l과 key값을 인자로 option_find함수를 실행하여, 반환값을 char *타입의 변수 v에 넣습니다.

2. 만약에 v에 값이 있다면 v를 반환하고 함수를 종료합니다.

3. 만약에 v에 값이 없다면 default값을 사용한다는 알람을 출력하고 나서, def값을 반환하고 함수를 종료합니다.


4. option_find : src/option_list.c

option_find의 구성은 아래와 같습니다.

char *option_find(list *l, char *key)
{
    node *n = l->front;
    while(n){
        kvp *p = (kvp *)n->val;
        if(strcmp(p->key, key) == 0){
            p->used = 1;
            return p->val;
        }
        n = n->next;
    }
    return 0;
}


1. node 구조체 포인터 n을 생성한 후에, 파라미터로 받은 리스트 구조체의 멤버 front를 대입합니다.

2. n이 존재한다면 계속 순회를 돕니다.

3. kvp 구조체 포인터 p를 생성하고, node구조체의 val값을 kvp 구조체 포인터로 캐스팅하여 대입합니다.

4. kvp 구조체 포인터 p의 멤버 key와 함수 파라미터로 받은 key값, 즉 문자열이 같다면 kvp 구조체 멤버의 p값의 used를 사용했다는 값인 1로 변경해주고 kvp 구조체의 멤버 val값을 돌려주고 함수를 종료합니다.

5.  kvp 구조체 포인터 p의 멤버 key와 함수 파라미터로 받은 key값, 즉 문자열이 같지 않다면 node 구조체 포인터의 값을 다음 노드의 값으로 변경합니다.

6. 순회를 모두 돌았는데, 해당하는 option을 찾을 수도 없다면 0을 반환하고 함수를 종료합니다.