darknet
内部的image
结构体定义
image结构体定义在image.h
,定义如下:
typedef struct {
int h;
int w;
int c;
float *data;
} image;
从以上定义中可以看出,darknet
内部使用一维数组存放图像像素数据。
与OpenCV
IplImage
结构体的转化
darknet
内部有两种处理图像的方式,一是使用OpenCV
,二是使用stb
。在使用OpenCV
加载图像之后,需要将IplImage
格式转化为image
结构体,而正是这种转化提供了image
内部结构信息。定义在image.c
中,
image ipl_to_image(IplImage* src)
{
unsigned char *data = (unsigned char *)src->imageData;
int h = src->height;
int w = src->width;
int c = src->nChannels;
int step = src->widthStep; // Size of aligned image row in bytes.
image out = make_image(w, h, c);
int i, j, k, count=0;;
for(k= 0; k < c; ++k){
for(i = 0; i < h; ++i){
for(j = 0; j < w; ++j){
out.data[count++] = data[i*step + j*c + k]/255.; // Normalize
}
}
}
return out;
}
从代码中可以看出,image
结构中的像素数据是按通道组合在一起的,且进行了均一化处理。可以使用以下Python代码验证:
for k in range(3):
for i in range(4):
for j in range(5):
print i*15+j*3+k
其他
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);
// set w, h to 0 to supress image resizement.
}
}
return alphabets;
}
此函数加载位于/data/labels
下的图像作为字符集。
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)){ //图像不为空且图像宽高不等于预定义宽高时,进行resize操作
image resized = resize_image(out, w, h);
free_image(out);
out = resized;
}
return out;
}
这是加载图像的函数,从中可以看出,在加载时已经将图像resize
为网络需要的图像尺寸了。
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;
}
float get_pixel(image m, int x, int y, int c)
{
assert(x < m.w && y < m.h && c < m.c);
return m.data[c*m.h*m.w + y*m.w + x];
}
void set_pixel(image m, int x, int y, int c, float val)
{
if (x < 0 || y < 0 || c < 0 || x >= m.w || y >= m.h || c >= m.c) return;
assert(x < m.w && y < m.h && c < m.c);
m.data[c*m.h*m.w + y*m.w + x] = val;
}
从以上源码可以看出,作者自己写了resize
函数,先进行横向的缩放,再进行纵向的缩放。