yolov3源码解析——图像数据读取test_detector(02)

test_detector函数中image **alphabet = load_alphabet();实现对data/labels下的图像数据进行读取,本文主要解读这一部分设计的内容。

load_alphabet()函数在include/darknet.h中声明,在src/image.c中实现,代码如下:

image **load_alphabet()
{
    int i, j;
    const int nsize = 8;
    //8个image,为什么是8个
    image **alphabets = calloc(nsize, sizeof(image));
    for(j = 0; j < nsize; ++j){
        //8*128的image
        alphabets[j] = calloc(128, sizeof(image));
        for(i = 32; i < 127; ++i){
            char buff[256];
            //buff以字符串的形式指向文件
            sprintf(buff, "data/labels/%d_%d.png", i, j);
            alphabets[j][i] = load_image_color(buff, 0, 0);
        }
    }
    return alphabets;
}

先来看看image(图像)结构的定义,在include/darknet.h中定义,代码如下:

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

w:weights列数     h:heights行数     c:channels通道数     data:图像数据,是一个线性的列表

load_alphabet()中,使用sprintf函数进行字符串链接,组成文件地址,所以函数中大部分的过程是在为读取数据作准备,创建空的存储空间image,获得要读取的数据文件目录。

而load_image_color函数才是读取图像数据的核心,该函数在include/darknet.h声明,在src/image.c中实现,代码如下:

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

load_image函数在include/darknet.h中声明,在src/image.c中实现,代码如下:

image load_image(char *filename, int w, int h, int c)
{
    //返回图片数据,数据已经标准化(➗255)
    image out = load_image_stb(filename, c);

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

其中load_image_stb函数循环的对data/labels中的图像进行了读取,该函数在darknet.h中声明,在src/image.c中实现,代码如下:

image load_image_stb(char *filename, int channels)
{
    int w, h, c;
    unsigned char *data = stbi_load(filename, &w, &h, &c, channels);

    if (!data) {
        fprintf(stderr, "Cannot load image \"%s\"\nSTB Reason: %s\n", filename, stbi_failure_reason());
        exit(0);
    }
    if(channels) c = channels;
    int i,j,k;
    image im = make_image(w, h, c);
    for(k = 0; k < c; ++k){
        for(j = 0; j < h; ++j){
            for(i = 0; i < w; ++i){
                int dst_index = i + w*j + w*h*k;
                int src_index = k + c*i + c*w*j;
                im.data[dst_index] = (float)data[src_index]/255.;
            }
        }
    }
    free(data);
    return im;
}

其中stbi_loat函数实现数据读取,该函数来自于stb_image库,这是一个用于图像读取的库,库文件为src/stb_image.h和src/stb_image_write.h,相关使用方法请查看相关信息

im作为图像数据的存储空间,im.data存储的是一个线性的列表,并没有使用多维的矩阵,所以需要计算像素点在列表中的存储位置,而循环过程就是在作这件事。

可以看到数据被除了255,数据是进行了标准化的。

回到load_image_stb函数:

其中resize_image是对图像数据的结构调整,和python中的np.resize是类似的

该函数声明在darknet.h中,在src/image.c中实现,代码如下:

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

但是该函数通常不会被调用

这就是读取图像数据的全部过程

你可能感兴趣的:(yolo)