本篇文章针对YOLO-v3官方提供的c语言版的darknet进行了修改(CPU模式),添加了一些函数,进行可视化feature-map处理,主要针对Inference过程中,对中间计算结果feature-map数据进行转换,将转换结果用图片是形式直观感受。
主要方法是将卷积计算后的outputs使用sigmoid函数进行归一化,乘以255后将参数写入txt文件中,使用Python将txt文件转为图片,最终得到每一层的feature map,将每个通道都可视化,如下图所示:
在network.c中设计将浮点数转换为像素值的函数,步骤如下:
1、保存文件到特定文件夹(feature_txt)中并根据ai大小命名;
2、创建一个1维的空图片;
3、设计一个数组保存该图片内容;
4、转换feature-map值,保存到文件;
5、关闭文件流。
代码如下所示:
image float_to_cow_image(int w, int h, int c, signed char *data, int ai)
{
char tp[1000];
//保存文件到特定文件夹(feature_txt)中并根据ai大小命名
//创建一个1维的空图片
image out = make_empty_image(w, h, 1);
for (int k = 0; k < c; k++) {
if (ai < 10)
sprintf(tp, "feature_pic/out_000%d", ai);
else
sprintf(tp, "feature_pic/out_00%d", ai);
mkdir(tp);
if (ai < 10)
sprintf(tp, "feature_txt/out_000%d", ai);
else
sprintf(tp, "feature_txt/out_00%d", ai);
mkdir(tp);
if (ai < 10)
sprintf(tp, "feature_txt/out_000%d/out_000%d_%d.txt", ai, ai, k);
else
sprintf(tp, "feature_txt/out_00%d/out_00%d_%d.txt", ai, ai, k);
FILE * stream = fopen(tp, "w+");
int i, j;
//设计一个数组保存该图片内容
float * tempData = calloc(w*h, sizeof(float));
//初始化
for (i = 0; i < w*h; i++)
{
tempData[i] = 0;
}
for (i = 0; i < w*h; i++)
{
tempData[i] = 1.0 / (1 + exp(-1 * data[k*w*h + i]));
}
//保存到文件
for (i = 0; i < w*h; i++)
{
tempData[i] *= 255;
fprintf(stream, " %f", tempData[i]);
if ((i + 1) % w == 0)
fprintf(stream, "\n");
}
//关闭文件流
fclose(stream);
out.data = tempData;
}
return out;
}
将生成feature-map转换为像素值的txt文件,如下目录:
将txt文件中的像素值转为图片,为了更方便对比每一层卷积计算后的feature-map,这里将每一层不同通道的feature-map拼接在一个大的图片中,使用Python来实现,代码如下:
# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a temporary script file.
"""
import os
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import matplotlib.image as mpimg
def process(filepath,outpath):
for fileName in os.listdir(filepath):
a=np.loadtxt(filepath+"/"+fileName)
im = Image.fromarray(np.uint8(a))
plt.title(fileName)
plt.imshow(im),plt.axis('off')
im.save(outpath+"/"+fileName[:-4]+".jpg")
#plt.savefig(outpath+"/"+fileName[:-4]+".jpg",bbox_inches="tight",transparent=True,pad_inches=0)
# 定义图像拼接函数
def image_compose(IMAGE_SIZE,IMAGE_ROW,IMAGE_COLUMN,image_names,IMAGE_SAVE_PATH):
to_image = Image.new('RGB', (IMAGE_COLUMN * IMAGE_SIZE, IMAGE_ROW * IMAGE_SIZE)) #创建一个新图
# 循环遍历,把每张图片按顺序粘贴到对应位置上
for y in range(1, IMAGE_ROW + 1):
for x in range(1, IMAGE_COLUMN + 1):
from_image = Image.open(IMAGES_PATH + image_names[IMAGE_COLUMN * (y - 1) + x - 1]).resize(
(IMAGE_SIZE, IMAGE_SIZE),Image.ANTIALIAS)
to_image.paste(from_image, ((x - 1) * IMAGE_SIZE, (y - 1) * IMAGE_SIZE))
return to_image.save(IMAGE_SAVE_PATH) # 保存新图
if __name__ == "__main__":
outpath = "feature_pic"
filepath="feature_txt"
IMAGES_FORMAT = ['.jpg', '.JPG'] # 图片格式
IMAGE_SIZE_t = [512,256,256,128,128,64,64,32,32,32,32,32,32,32,32] # 每张小图片的大小
IMAGE_ROW_t = [4,4,4,4,8,8,8,8,8,8,8,8,8,5,5] # 图片间隔,也就是合并成一张图后,一共有几行
IMAGE_COLUMN_t = [4,4,4,4,4,4,4,4,8,8,8,8,4,6,6] # 图片间隔,也就是合并成一张图后,一共有几列
count = 0
plt.figure()
for fpathe,fpathe1 in zip(os.walk(filepath),os.walk(outpath)):
#for f in fs:
if(count!=0):
plt.subplot(4,4,count)
IMAGE_SAVE_PATH = 'feature_pic_final/final_00'+str(count-1)+'.jpg' # 图片转换后的地址
process(fpathe[0],fpathe1[0])
IMAGES_PATH = fpathe1[0] + "\\"
# 获取图片集地址下的所有图片名称
image_names = [name for name in os.listdir(IMAGES_PATH) for item in IMAGES_FORMAT if os.path.splitext(name)[1] == item]
# 简单的对于参数的设定和实际图片集的大小进行数量判断
if len(image_names) != IMAGE_ROW_t[count -1] * IMAGE_COLUMN_t[count-1]:
raise ValueError("合成图片的参数和要求的数量不能匹配!")
image_compose(IMAGE_SIZE_t[count-1],IMAGE_ROW_t[count-1],IMAGE_COLUMN_t[count-1],image_names,IMAGE_SAVE_PATH) #调用函数
lena = mpimg.imread(IMAGE_SAVE_PATH)
img = plt.imshow(lena) # 显示图片
count = count + 1
plt.show()