1. 程式人生 > >darknet原始碼剖析(六)

darknet原始碼剖析(六)

繼續分析load_data_detection,進入fill_truth_detection函式。fill_truth_detection的作用是讀取圖片對應的標註資訊。

首先進入find_replace函式。

void find_replace(char *str, char *orig, char *rep, char *output)
{
    char buffer[4096] = {0};
    char *p;

    sprintf(buffer, "%s", str);
    if(!(p = strstr(buffer, orig))){  // Is 'orig' even in 'str'?
        sprintf(output, "%s", str);
        return;
    }

    *p = '\0';

    sprintf(output, "%s%s%s", buffer, rep, p+strlen(orig));
}

該函式的作用是首先查詢orig是否存在,若不存在則將str的值直接賦給output並返回;若存在則將orig替換為rep。

    find_replace(path, "images", "labels", labelpath);
    find_replace(labelpath, "JPEGImages", "labels", labelpath);

    find_replace(labelpath, "raw", "labels", labelpath);

從以上程式碼可以知道,圖片可以存放在images、JPEGImages、raw路徑下,上述路徑會被替換為labels。上述程式碼同時說明標籤檔案需要放置在labels資料夾下。

    find_replace(labelpath, ".jpg", ".txt", labelpath);
    find_replace(labelpath, ".png", ".txt", labelpath);
    find_replace(labelpath, ".JPG", ".txt", labelpath);
    find_replace(labelpath, ".JPEG", ".txt", labelpath);

上述程式碼的功能是將*.jpg/png/JPG/JPEG替換為*.txt,同時表明darknet僅支援jpg與png兩種格式。

獲取標籤路徑後,讀取標註。

    box_label *boxes = read_boxes(labelpath, &count);
    randomize_boxes(boxes, count);
    correct_boxes(boxes, count, dx, dy, sx, sy, flip);

呼叫read_boxes讀取標註資料,randomize_boxes隨機交換標註資料順序,correct_boxes根據圖片調整比例,調整標註框的大小。

read_boxes函式如下:

box_label *read_boxes(char *filename, int *n)
{
    FILE *file = fopen(filename, "r");
    if(!file) file_error(filename);
    float x, y, h, w;
    int id;
    int count = 0;
    int size = 64;
    box_label *boxes = calloc(size, sizeof(box_label));
    while(fscanf(file, "%d %f %f %f %f", &id, &x, &y, &w, &h) == 5){
        if(count == size) {
            size = size * 2;
            boxes = realloc(boxes, size*sizeof(box_label));
        }
        boxes[count].id = id;
        boxes[count].x = x;
        boxes[count].y = y;
        boxes[count].h = h;
        boxes[count].w = w;
        boxes[count].left   = x - w/2;
        boxes[count].right  = x + w/2;
        boxes[count].top    = y - h/2;
        boxes[count].bottom = y + h/2;
        ++count;
    }
    fclose(file);
    *n = count;
    return boxes;
}

通過left、right、top、bottom的計算方法,可以知曉x、y是物體的中心位置。同時x、y、w、h需要注意是歸一化後的結果。

這一點可以從產生的voc標註的程式碼驗證,在yolo官網中可以下載該工具,在generate_annotation/voc_label.py中。

def convert(size, box):
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[1])/2.0
    y = (box[2] + box[3])/2.0
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

回到darknet程式碼中。

    if(count > num_boxes) count = num_boxes;
    float x,y,w,h;
    int id;
    int i;
    int sub = 0;

若標註數量大於num_boxes則將count設為num_boxes,表明會隨機的丟棄一些標註框。僅取前num_boxes個框,因此需要呼叫randomize_boxes函式,調整標註框的順序。

    for (i = 0; i < count; ++i) {
        x =  boxes[i].x;
        y =  boxes[i].y;
        w =  boxes[i].w;
        h =  boxes[i].h;
        id = boxes[i].id;

        if ((w < .001 || h < .001)) {
            ++sub;
            continue;
        }

        truth[(i-sub)*5+0] = x;
        truth[(i-sub)*5+1] = y;
        truth[(i-sub)*5+2] = w;
        truth[(i-sub)*5+3] = h;
        truth[(i-sub)*5+4] = id;
    }
    free(boxes);

最後將boxes的資訊放入truth中,w或h小於0.001,則捨棄該標註框。