提问 发文

mmdetection里进行特征图可视化

微微菌

| 2024-03-11 14:17 84 0 0

前言
    在mmdetection里实现简单的特征可视化代码,不是像Grad-CAM算法那样对分类或检测直接通过反向传播值进行可视化的,而是就特征层里的最大像数值,我们认为是重要的,将其可视化出来。一般测试的时候用,能够可视化模型的预测效果。训练阶段可以看看,但没太大效果。

1.特征可视化
1.这个代码结果是将检测的时候特征层,对应像素值大的给可视化出来,附加在原图上。也可以不resize到原图大小,看对应的特征图整个像素的分布。

def draw_feature_map1(features, img_path, save_dir = './work_dirs/feature_map/',name = None):
'''
:param features: 特征层。可以是单层,也可以是一个多层的列表
:param img_path: 测试图像的文件路径
:param save_dir: 保存生成图像的文件夹
:return:
'''
img = cv2.imread(img_path) #读取文件路径
i=0
if isinstance(features,torch.Tensor): # 如果是单层
features = [features] # 转为列表
for featuremap in features: # 循环遍历
heatmap = featuremap_2_heatmap1(featuremap) #主要是这个,就是取特征层整个的求和然后平均,归一化
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) # 将热力图的大小调整为与原始图像相同
heatmap0 = np.uint8(255 * heatmap) # 将热力图转换为RGB格式,0-255,heatmap0显示红色为关注区域,如果用heatmap则蓝色是关注区域
heatmap = cv2.applyColorMap(heatmap0, cv2.COLORMAP_JET) # 将热力图应用于原始图像
superimposed_img = heatmap * 0.4 + img # 这里的0.4是热力图强度因子
plt.imshow(heatmap0) # ,cmap='gray' ,这里展示下可视化的像素值
# plt.imshow(superimposed_img) # ,cmap='gray'
plt.close() #关掉展示的图片
# 下面是用opencv查看图片的
# cv2.imshow("1",superimposed_img)
# cv2.waitKey(0) #这里通过安键盘取消显示继续运行。
# cv2.destroyAllWindows()
if not os.path.exists(save_dir):
os.makedirs(save_dir)
cv2.imwrite(os.path.join(save_dir, name + str(i) + '.png'), superimposed_img) #superimposed_img:保存的是叠加在原图上的图,也可以保存过程中其他的自己看看
print(os.path.join(save_dir, name + str(i) + '.png'))
i = i + 1

def draw_feature_map1(features, img_path, save_dir = './work_dirs/feature_map/',name = None):
'''
:param features: 特征层。可以是单层,也可以是一个多层的列表
:param img_path: 测试图像的文件路径
:param save_dir: 保存生成图像的文件夹
:return:
'''
img = cv2.imread(img_path) #读取文件路径
i=0
if isinstance(features,torch.Tensor): # 如果是单层
features = [features] # 转为列表
for featuremap in features: # 循环遍历
heatmap = featuremap_2_heatmap1(featuremap) #主要是这个,就是取特征层整个的求和然后平均,归一化
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) # 将热力图的大小调整为与原始图像相同
heatmap0 = np.uint8(255 * heatmap) # 将热力图转换为RGB格式,0-255,heatmap0显示红色为关注区域,如果用heatmap则蓝色是关注区域
heatmap = cv2.applyColorMap(heatmap0, cv2.COLORMAP_JET) # 将热力图应用于原始图像
superimposed_img = heatmap * 0.4 + img # 这里的0.4是热力图强度因子
plt.imshow(heatmap0) # ,cmap='gray' ,这里展示下可视化的像素值
# plt.imshow(superimposed_img) # ,cmap='gray'
plt.close() #关掉展示的图片
# 下面是用opencv查看图片的
# cv2.imshow("1",superimposed_img)
# cv2.waitKey(0) #这里通过安键盘取消显示继续运行。
# cv2.destroyAllWindows()
if not os.path.exists(save_dir):
os.makedirs(save_dir)
cv2.imwrite(os.path.join(save_dir, name + str(i) + '.png'), superimposed_img) #superimposed_img:保存的是叠加在原图上的图,也可以保存过程中其他的自己看看
print(os.path.join(save_dir, name + str(i) + '.png'))
i = i + 1

featuremap_2_heatmap1函数:

def featuremap_2_heatmap1(feature_map):
assert isinstance(feature_map, torch.Tensor)
feature_map = feature_map.detach()
# heatmap = feature_map[:,0,:,:]*0 #
heatmap = feature_map[:1, 0, :, :] * 0 #取一张图片,初始化为0
for c in range(feature_map.shape[1]): # 按通道
heatmap+=feature_map[:1,c,:,:] # 像素值相加[1,H,W]
heatmap = heatmap.cpu().numpy() #因为数据原来是在GPU上的
heatmap = np.mean(heatmap, axis=0) #计算像素点的平均值,会下降一维度[H,W]

heatmap = np.maximum(heatmap, 0) #返回大于0的数[H,W]
heatmap /= np.max(heatmap) #/最大值来设置透明度0-1,[H,W]
#heatmaps.append(heatmap)

return heatmap

2.对一层特征图里面包含的通道进行可视化,如256个通道,可以自己选择可视化哪些通道。
输入,可以是一层特征图,也可以是一个列表包含多个特征层,修改输入就行。这里,为了匹配mmdetection里的resnet输出4层特征图,就直接输入输出的特征层列表了:

def feature_map_channel(features,img_path,save_dir = 'work_dirs/feature_map',name = 'noresbnsie2ltft_'):
# 随便定义a,b,c,d去取对应的特征层,把通道数变换到最后一个维度,将计算的环境剥离由GPU变成CPU,tensor变为numpy
a = torch.squeeze(features[0][:1, :, :, :], dim=0).permute(1, 2, 0).detach().cpu().numpy()
b = torch.squeeze(features[1][:1, :, :, :], dim=0).permute(1, 2, 0).detach().cpu().numpy()
c = torch.squeeze(features[2][:1, :, :, :], dim=0).permute(1, 2, 0).detach().cpu().numpy()
d = torch.squeeze(features[3][:1, :, :, :], dim=0).permute(1, 2, 0).detach().cpu().numpy()
img = cv2.imread(img_path)
for j,x in enumerate([d]):
# x.shape[-1]:表示所有通道数,不想可视化这么多,可以自己写对应的数量
for i in range(x.shape[-1]):
heatmap = x[:, :, i]
# heatmap = np.maximum(heatmap, 0) #一个通道应该不用归一化了
# heatmap /= np.max(heatmap)
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) # 将热力图的大小调整为与原始图像相同
heatmap0 = np.uint8(255 * heatmap) # 将热力图转换为RGB格式,0-255,heatmap0显示红色为关注区域,如果用heatmap则蓝色是关注区域
heatmap = cv2.applyColorMap(heatmap0, cv2.COLORMAP_JET)
superimposed_img = heatmap * 0.4 + img # 将热力图应用于原始图像
# plt.figure() # 展示
# plt.title(str(j))
# plt.imshow(heatmap0) #, cmap='gray'
# # plt.savefig(os.path.join(save_dir, name+str(j)+str(i) + '.png'))
# plt.close()
cv2.imwrite(os.path.join(save_dir, name + str(j)+str(i) + '.png'), superimposed_img)

2.为了在mmdetection使用可视化
1.创建自己的一个检测框架方便专门用来进行可视化singlestage_heatmap.py
这里,只需要将原来的SingleStageDetector(mmdet/models/detectors/singlestage.py)里的代码复制,再对里面的训练和测试的整个流程里进行修改,添加个获取到的图片的全部信息的字典(img_metas)就行。如果熟悉的话,自己根据可视化的代码可以自己修改相应的代码

def forward_train(self,
img,
img_metas,
gt_bboxes,
gt_labels,
gt_bboxes_ignore=None):
"""
Args:
img (Tensor): Input images of shape (N, C, H, W).
Typically these should be mean centered and std scaled.
img_metas (list[dict]): A List of image info dict where each dict
has: 'img_shape', 'scale_factor', 'flip', and may also contain
'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.
For details on the values of these keys see
:class:`mmdet.datasets.pipelines.Collect`.
gt_bboxes (list[Tensor]): Each item are the truth boxes for each
image in [tl_x, tl_y, br_x, br_y] format.
gt_labels (list[Tensor]): Class indices corresponding to each box
gt_bboxes_ignore (None | list[Tensor]): Specify which bounding
boxes can be ignored when computing the loss.

Returns:
dict[str, Tensor]: A dictionary of loss components.
"""
super(SingleStageDetectorHp, self).forward_train(img, img_metas)
x = self.extract_feat(img,img_metas) #修改
losses = self.bbox_head.forward_train(x, img_metas, gt_bboxes,
gt_labels, gt_bboxes_ignore)
return losses

def simple_test(self, img, img_metas, rescale=False):
"""Test function without test-time augmentation.

Args:
img (torch.Tensor): Images with shape (N, C, H, W).
img_metas (list[dict]): List of image information.
rescale (bool, optional): Whether to rescale the results.
Defaults to False.

Returns:
list[list[np.ndarray]]: BBox results of each image and classes.
The outer list corresponds to each image. The inner list
corresponds to each class.
"""
feat = self.extract_feat(img,img_metas) #FPN的输出
results_list = self.bbox_head.simple_test(
feat, img_metas, rescale=rescale)
bbox_results = [
bbox2result(det_bboxes, det_labels, self.bbox_head.num_classes)
for det_bboxes, det_labels in results_list #就是将LABEL值与预测的det_bboxes进行匹配
]
return bbox_results

2.经过backbone后,可视化输出的特征层

def extract_feat(self, img,img_metas):
"""Directly extract features from the backbone+neck."""
imgpath = img_metas[0]['filename'] # 主要是要图片的原始路径
x = self.backbone(img)
from tools.feature_visualization import draw_feature_map1,feature_map_channel
draw_feature_map1(x,imgpath,name='inputs_') #特征层,图片路径,保存的文件名
feature_map_channel(x,imgpath,name='chanel_')
if self.with_neck:
x = self.neck(x,imgpath)
return x

3.在相应的检测器整体框架流程里,导入自己的检测框架
比如在ATSS里


效果图
测试时的可视化resnet50,最后两层

原图:

倒数第二层,C4层


最后一层,C5层

使用自己模块经过FPN后生成的,可以发现,更有利于分层检测。
生成的可视化图:



————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_41311686/article/details/125421382

收藏 0
分享
分享方式
微信

评论

游客

全部 0条评论

9228

文章

4.77W+

人气

12

粉丝

1

关注

官方媒体

轻松设计高效搭建,减少3倍设计改稿与开发运维工作量

开始免费试用 预约演示

扫一扫关注公众号 扫一扫联系客服

©Copyrights 2016-2022 杭州易知微科技有限公司 浙ICP备2021017017号-3 浙公网安备33011002011932号

互联网信息服务业务 合字B2-20220090

400-8505-905 复制
免费试用
微信社区
易知微-数据可视化
微信扫一扫入群