在将darknet训练好的yolov3、v4转换成mxnet中遇到的一些问题在这里做个总结
对齐主要是三部分
在前处理中darknet主要是将图片读取后放入内存中,放入的顺序为RRR…GGG…BBB…
在darkent项目中的detector.c中将图片保存下来,代码如下:
FILE *fptr;
char output_file_name[1000];
sprintf(output_file_name, "./img_after_preprocess.txt");
fptr = fopen(output_file_name,"w");
for (int index = 0; index < sized.c*sized.w*sized.h; index++) {
fprintf(fptr,"%f\n",sized.data[index]);
}
fclose(fptr);
在python中将保存好的txt文件转换成图片,代码如下:
import cv2
import os
import numpy as np
import pdb
img = np.loadtxt("./img_after_preprocess.txt")
img *= 255
img_r = img[:608*608].reshape([608, 608, 1])
img_g = img[608*608:608*608*2].reshape([608, 608, 1])
img_b = img[608*608*2:608*608*3].reshape([608, 608, 1])
img_out = np.concatenate([img_b, img_g, img_r], axis=-1) #这里需要注意opencv存图片的格式为bgr
cv2.imwrite("test.jpg", img_out)
~
为了方便做对齐、消除darknet、python的读图、resize的区别,通常我们会使用所有像素为255的矩阵当作网络输入,经过前处理后输入网络的矩阵元素都为1,具体的操作代码在darknet框架下如下:
for(int index = 0; index < sized.c*sized.w*sized.h; index++){
sized.data[index] = 1;
}
修改darknet下network.c文件的detection *make_network_boxes(network *net, float thresh, int *num)函数,使其将网络的每一层结果保存到文件中,并在python中对每一层的结果求sum对齐,python代码在此处不做展示。
FILE *fptr;
int i;
layer l = net->layers[net->n - 1];
for (i = 0; i < net->n; ++i) {
layer l_tmp = net->layers[i];
char output_file_name[1000];
sprintf(output_file_name, "./darknet_output/test_layer%d.txt", i);
fptr = fopen(output_file_name,"w");
for(int index_i=0; index_i < l_tmp.out_c*l_tmp.out_w*l_tmp.out_h; index_i++) {
fprintf(fptr,"%f\n",l_tmp.output[index_i]);
}
fclose(fptr);
}
}
需要注意的是yolov4.cfg中yolo层有个scale_x_y的参数,此参数为了解决yolo层中预测grid sensitivity的问题,主要的是在每层的yolo的x、y上乘以一个系数,此步骤能带来大约2%的map提升。需要强调的是darknet中每一层yolo层的顺序排序为xxxyyywwwhhhCCCc1c1c1c2c2c2c2###xxxyyywwwhhhCCCc1c1c1c2c2c2c2###xxxyyywwwhhhCCCc1c1c1c2c2c2c2…
假设每一层yolo对应有三个anchor boxes,###分隔的是不同anchor
关于scale_x_y参数的解释,参考:alexeyAB对于scale_x_y参数的解释
以下为python解析yolo层的步骤,假设yolo层的输出为[1,18,76,76]
# x.shape = (1, 18, 76, 76) assume feature map size is 76 * 76
x = x.reshape([-1, self.num_boxes * self.bbox_attrs, self.feature_size * self.feature_size]) # x.shape = (1, 18, 5776)
x = F.transpose(x, [0, 2, 1]) # x.shape = (1, 5776, 18)
x = x.reshape([-1, self.feature_size * self.feature_size * self.num_boxes, self.bbox_attrs]) # x.shape = (1, 17328, 6)
xy_pred = F.sigmoid(x.slice_axis(begin=0,end=2,axis=-1)) #xy为相对于左上角的偏移,先经过sigmoid变成0-1
xy_pred = xy_pred * self.scale - 0.5*(self.scale - 1.0) #在这里对xy乘以sccale
xy_pred = (xy_pred + x_y_offset) / 76 # 在这里将xy还原成对应特征图的上的相对坐标 0-1
wh_pred = x.slice_axis(begin=2,end=4,axis=-1)
wh_pred = nd.exp(wh_pred) * anchors / input_dim # 在这里将wh还原成相对坐标0-1
score_pred = F.sigmoid(x.slice_axis(begin=4, end=5, axis=-1))
cls_pred = F.sigmoid(x.slice_axis(begin=5,end=None, axis=-1))
output = F.concat(xy_pred, wh_pred, score_pred, cls_pred, dim=-1)
此时矩阵的数据排列为:
左上角第一个bbox
x,y,w,h,C,c1 # anchor1
x,y,w,h,C,c1 # anchor2
x,y,w,h,C,c1 # anchor3
左上角第二个bbox
x,y,w,h,C,c1 # anchor1
x,y,w,h,C,c1 # anchor2
x,y,w,h,C,c1 # anchor3
…
17328