본문 바로가기

IT/Deeplearning

[Object Detection / YOLO DARKNET] object detection code review :: load_alphabet -[5]

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

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

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

[Object Detection / YOLO DARKNET] object detection code review :: get_labels -[4]


저번 포스팅에서는 get_labels 메소드에 대해서 코드 리뷰를 진행하였습니다.

이번 포스팅은 load_alphabet 메소드에 대해서 코드 리뷰를 진행하도록 하겠습니다.


1. Concept Overview


2. test_detector :: examples/detector.c


현재 리뷰하고 있는 코드 라인은 아래 코드에서 5번 라인에 해당됩니다.

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;
    }
}


3. load_alphabet :: src/image.c

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

image **load_alphabet()
{
    int i, j;
    const int nsize = 8;
    image **alphabets = calloc(nsize, sizeof(image));
    for(j = 0; j < nsize; ++j){
        alphabets[j] = calloc(128, sizeof(image));
        for(i = 32; i < 127; ++i){
            char buff[256];
            sprintf(buff, "data/labels/%d_%d.png", i, j);
            alphabets[j][i] = load_image_color(buff, 0, 0);
        }
    }
    return alphabets;
}

1. int형 데이터 i, j 생성

2. int형 상수 nsize를 8로 설정

3. image 구조체 이중 포인터 alphabets을 선언하고 구조체 image 8개 크기로 메모리 동적할당

4. 0부터 nsize(8)만큼  순회를 돌면서, 각 alphabets 요소에 128개의 image 구조체만큼 메모리 동적할당

5. 32부터 127까지 순회를 돈다

5-1. char 배열 buff를 256크기의 사이즈로 선언한다.

5-2. buff에 순회를 돌면서 각 png의 경로를 넣어준다.

5-3. alphabets의 2차원 배열에 (5-2)에서 데이터 경로를 넣어준 buff를 인자로 갖는 load_image_color를 호출해 반환값을 대입한다.

6. 순회를 모두 돌았으면, alphabets를 반환하고 메소드를 종료한다.


4. image Structure :: include/darknet.h

image 구조체는 다음과 같이 선언되어있습니다.

typedef struct {
    int w;
    int h;
    int c;
    float *data;
} image;

1. w는 image의 width, h는 image의 height, c는 image channels을 의미합니다.

2. float 포인터 타입 데이터 data는 image pixel에 대한 데이터가 담겨있는 포인터를 의미합니다.


5. load_image_color :: src/image.c

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

image load_image_color(char *filename, int w, int h)
{
    return load_image(filename, w, h, 3);
}

1. 받았던 파라미터 filename, w, h를 이용해서 load_image를 호출하고, 그 결과를 반환하고 메소드를 종료합니다.


6. load_image :: src/image.c

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

image load_image(char *filename, int w, int h, int c)
{
#ifdef OPENCV
    image out = load_image_cv(filename, c);
#else
    image out = load_image_stb(filename, c);
#endif

    if((h && w) && (h != out.h || w != out.w)){
        image resized = resize_image(out, w, h);
        free_image(out);
        out = resized;
    }
    return out;
}


1. OPENCV 옵션이 설정되어있는 상태로 make되었다면, 받았던 파라미터 filename, c를 이용하여 load_image_cv 메소드를 호출하고 그 결과값을 image 구조체 out에 대입합니다.

2. OPENCV 옵션이 설정되어있지 않은 상태로 make되었다면, 받았던 파라미터 filename, c를 이용하여 load_image_stb 메소드를 호출하고 그 결과값을 image 구조체 out에 대입합니다.

3. 받은 파라미터의 w,h값이 존재하고, 읽어들인 image구조체의 h와 w가 이와 일치하지 않다면 받은 파라미터 w, h와 (2)에서 생성한 image구조체 out을 파라미터로 하는 resize_image메소드를 호출하여 image 구조체 resize에 대입합니다.

4. image 구조체 out에 할당되었던 메모리를 동적해제합니다.

5. image구조체 포인터 out에 resized를 대입합니다.

6. out을 반환하고, 메소드를 종료합니다.


7. load_image_cv :: src/image.c

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

image load_image_cv(char *filename, int channels)
{
    IplImage* src = 0;
    int flag = -1;
    if (channels == 0) flag = -1;
    else if (channels == 1) flag = 0;
    else if (channels == 3) flag = 1;
    else {
        fprintf(stderr, "OpenCV can't force load with %d channels\n", channels);
    }

    if( (src = cvLoadImage(filename, flag)) == 0 )
    {
        fprintf(stderr, "Cannot load image \"%s\"\n", filename);
        char buff[256];
        sprintf(buff, "echo %s >> bad.list", filename);
        system(buff);
        return make_image(10,10,3);
        //exit(0);
    }
    image out = ipl_to_image(src);
    cvReleaseImage(&src);
    rgbgr_image(out);
    return out;
}


1. IplImage 포인터 src를 생성하고 0으로 초기화합니다.

2. int형 타입 flag를 생성하고 -1로 초기화합니다.

3. 받은 파라미터 값이 0인지, 1인지, 3인지를 확인하여 flag의 값을 -1, 0, 1을 주고, 해당사항이 없다면 에러메세지를 출력합니다.

4. cvLoadImage에 파라미터를 filename과 flag를 주고나서 반환값이 0인지 비교합니다.

4-1. (4)이 참이라면, 에러메세지를 출력합니다.

4-2. 256크기의 char 타입 배열을 생성합니다.

4-3. buff에 에러메세지를 기록하고, 해당 내용을 bad.list에 기록하게 합니다.

4-4. 파라미터를 10,10,3을 갖는 make_image 메소드를 호출합니다.

5. image구조체 out을 생성하고, src를 파라미터로 갖는 ipl_to_image메소드를 호출하여 반환값을 대입합니다.

6. src에 할당되었던 메모리를 cvReleaseImage를 통해서 동적해제해줍니다.

7. image구조체 out을 파라미터로 갖는 rgbgr_image 메소드를 호출합니다.

8. image구조체 out을 반환하고 메소드를 종료합니다


8. make _image :: src/image.c

make_image 메소드의 구성은 아래와 같습니다.

image make_image(int w, int h, int c)
{
    image out = make_empty_image(w,h,c);
    out.data = calloc(h*w*c, sizeof(float));
    return out;
}

1. image구조체 out을 생성하고, 파라미터 w, h, c를 갖는 make_empty_image 메소드를 호출합니다.

2. image 구조체 out의 멤버 data에 h*w*c크기의 flaot형 데이터 크기를 갖는 메모리를 동적할당합니다.

3. image 구조체 out을 반환하고 메소드를 종료합니다.


9. make_empty_image :: src/image.c

make_empty_image 메소드의 구성은 아래와 같습니다.

image make_empty_image(int w, int h, int c)
{
    image out;
    out.data = 0;
    out.h = h;
    out.w = w;
    out.c = c;
    return out;
}

1. image구조체 out을 생성합니다.

2. image구조체 out의 멤버 data를 0으로 초기화합니다.

3. image구조체 out의 멤버 h에 파라미터 h 값을 대입합니다.

4. image구조체 out의 멤버 w에 파라미터 w 값을 대입합니다.

5. image구조체 out의 멤버 c에 파라미터 c 값을 대입합니다.

6. image구조체 out을 반환하고 메소드를 종료합니다.


10. ipl_to_image :: src/image.c

ipl_to_image 메소드의 구성은 아래와 같습니다.

image ipl_to_image(IplImage* src)
{
    int h = src->height;
    int w = src->width;
    int c = src->nChannels;
    image out = make_image(w, h, c);
    ipl_into_image(src, out);
    return out;
}

1. int형 데이터 h를 생성하고, src의 멤버 height값을 대입한다.

2. int형 데이터 w를 생성하고, src의 멤버 width값을 대입한다.

3. int형 데이터 c를 생성하고, src의 nChannels값을 대입한다.

4. image 구조체 out을 생성하고, 파라미터 w,h,c를 갖는 make_image 메소드를 호출한다.

5. 파라미터 src, out을 갖는 ipl_into_image를 호출한다.

6. image 구조체 out을 반환하고 메소드를 종료한다.


11. ipl_into_image :: src/image.c

ipl_into_image 메소드의 구성은 아래와 같다.

void ipl_into_image(IplImage* src, image im)
{
    unsigned char *data = (unsigned char *)src->imageData;
    int h = src->height;
    int w = src->width;
    int c = src->nChannels;
    int step = src->widthStep;
    int i, j, k;

    for(i = 0; i < h; ++i){
        for(k= 0; k < c; ++k){
            for(j = 0; j < w; ++j){
                im.data[k*w*h + i*w + j] = data[i*step + j*c + k]/255.;
            }
        }
    }
}

1. unsigned char 포인터 data를 생성하고, IplImage 구조체 src의 멤버 imageData를 unsigned char형태의 데이터 타입으로 캐스팅하여 대입합니다.

2. int형 데이터 h를 생성하고, src의 멤버 height를 대입합니다.

3. int형 데이터 w를 생성하고, src의 멤버 width를 대입합니다.

4. int형 데이터 c를 생성하고, src의 멤버 nChannels를 대입합니다.

5. int형 데이터 step을 생성하고, src의 멤버 widthStep을 대입합니다.

6. int형 데이터 i,j,k를 생성합니다.

7. i는 h만큼, k는 c만큼, j는 w만큼 차례차례 inner loop를 순회합니다.

8. image 구조체 im의 멤버 data의 요소 순서 [k*w*h + i*w + j]로 접근하여 data의 [i*step + j*c + k]순으로 접근하여 이를 255로 나눈다음에 대입해준다


12. rgbgr_image :: src/image.c

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

void rgbgr_image(image im)
{
    int i;
    for(i = 0; i < im.w*im.h; ++i){
        float swap = im.data[i];
        im.data[i] = im.data[i+im.w*im.h*2];
        im.data[i+im.w*im.h*2] = swap;
    }
}

1. int형 데이터 i를 생성한다.

2. image구조체 멤버 w * h만큼 순회를 돈다.

3. float타입 swap을 생성하고, i번째 image구조체 im의 멤버 data를 대입한다.

4. i번째 image구조체 im의 멤버 data에 [i+im.w*im.h*2]번째 image구조체 im의 멤버 data의 값을 대입해준다.

5. [i+im.w*im.h*2]번째 image구조체 im의 멤버 data의 값을 swap으로 대입한다.


13. resize_image :: src/image.c

resize_image의 구성은 아래와 같습니다.
image resize_image(image im, int w, int h)
{
    image resized = make_image(w, h, im.c);   
    image part = make_image(w, im.h, im.c);
    int r, c, k;
    float w_scale = (float)(im.w - 1) / (w - 1);
    float h_scale = (float)(im.h - 1) / (h - 1);
    for(k = 0; k < im.c; ++k){
        for(r = 0; r < im.h; ++r){
            for(c = 0; c < w; ++c){
                float val = 0;
                if(c == w-1 || im.w == 1){
                    val = get_pixel(im, im.w-1, r, k);
                } else {
                    float sx = c*w_scale;
                    int ix = (int) sx;
                    float dx = sx - ix;
                    val = (1 - dx) * get_pixel(im, ix, r, k) + dx * get_pixel(im, ix+1, r, k);
                }
                set_pixel(part, c, r, k, val);
            }
        }
    }
    for(k = 0; k < im.c; ++k){
        for(r = 0; r < h; ++r){
            float sy = r*h_scale;
            int iy = (int) sy;
            float dy = sy - iy;
            for(c = 0; c < w; ++c){
                float val = (1-dy) * get_pixel(part, c, iy, k);
                set_pixel(resized, c, r, k, val);
            }
            if(r == h-1 || im.h == 1) continue;
            for(c = 0; c < w; ++c){
                float val = dy * get_pixel(part, c, iy+1, k);
                add_pixel(resized, c, r, k, val);
            }
        }
    }

    free_image(part);
    return resized;
}

1. image 구조체 resized를 선언하고, 받은 파라미터 w,h, im.c를 갖는 make_image 메소드를 호출하여 반환값을 대입한다.

2. image 구조체 part를 선언하고, 받은 파라미터  w,h,im.c를 갖는 make_image 메소드를 호출하여 반환값을 대입한다.

3. int형 데이터 r, c, k를 선언한다.

4. float형 데이터 w_scale을 선언하고, (im.w-1)/(w-1)의 결과값을 float형으로 캐스팅하여 대입한다.

5. float형 데이터 h_scale을 선언하고, (im.h-1)/(h-1)의 결과값을 flaot형으로 캐스팅하여 대입한다.

6. 






해야할 것 -> load_image_stb, free_image, get_pixel, set_pixel