单目标跟踪通过CAM绘制heatmap图像(以SiamCAR为例)
论文链接:
SiamCAR: Siamese Fully Convolutional Classification and Regression for Visual Tracking
Group-CAM: Group Score-Weighted Visual Explanations for Deep Convolutional Networks
代码链接:
SiamCAR+Group-CAM:SiamCAR-CAMv1.0
SiamCAR+CAM(支持包括GradCAM,GroupCAM在内的六种CAM):SiamCAR-CAMv2.0
环境配置:
pip install -r requirements.txt
cd toolkit/utils/
python setup.py build_ext --inplace
注:与SiamCAR环境配置相同,不同的是这里增加了一个python包:kornia
v2.0则增加了kornia,optuna(用于测试阶段搜参,与CAM无关),sklearn(Group-CAM),numba
运行:
对于SiamCAR-CAM代码的运行,在终端上,参见下面的代码。
cd /path/to/SiamCAR-CAM
conda activate SiamCAR
export PYTHONPATH=./:$PYTHONPATH
python tools/CAM-demo.py \--dataset_dir /path/to/dataset/root \ # dataset path--dataset UAV123 \ # dataset name(OTB100, GOT-10k, LaSOT, UAV123)--snapshot snapshot/general_model.pth \ # tracker_name--format bmp \ # save fomat (pdf,png,jpg,bmp) --save_dir /path/to/save \ # save dir--config ./experiments/siamcar_r50/config.yaml \ # config file--register_layer softmax \ # module register name
值得注意的是,通过CAM进行可视化,只有最后一层分类分支或者SiamCAR中,经过归一化的Centerness分支的输出进行可视化才是有意义的。其余分支经过CAM进行可视化,会出现杂乱无章的状态,这对于我们做数据分析来说,是无用的。但是为了方便用户在后续实践中,对所有层都能进行可视化,因此在代码中,我们将SiamCAR其余的神经网络结构也进行了注册。
以上面代码为例,在配置好环境后,我们在终端下进行如下的指令运行:
cd /home/db306/桌面/SiamCAR-CAM
conda activate SiamCAR
export PYTHONPATH=./:$PYTHONPATH
python tools/CAM-demo.py \--dataset_dir /media/db306/HUSHUO/Benchmark/ \--dataset OTB100 \ --snapshot snapshot/general_model.pth \ --format bmp \ --save_dir ./test \ --config ./experiments/siamcar_r50/config.yaml \--register_layer softmax
最后绘制出SiamCAR的分类分支heatmap可视化的结果:
在OTB100基准下,不同视频不同帧的分类分支的heatmap可视化
CAM部分代码:
参见:
/path/to/SiamCAR-CAM/CAM/GroupCAM.py
/path/to/SiamCAR-CAM/pysot/tracker/siamcar_tracker_cam.py
/path/to/SiamCAR-CAM/tools/CAM-demo.py
注:由于未知原因,实际上经运行发现,不是所有的视频帧都会出现heatmap图像,这与Group-CAM有关,以及单目标跟踪中(pysot系列),通常不会将输入图像进行归一化有关,这篇代码中,对于CAM部分,我们进行了归一化操作,对于涉及到跟踪器的部分,我们进行了反归一化操作。故猜测可能是这一个问题造成的。
代码中的一些要点详解
GroupCAM.py
对于各个结构的查看,我们参见上图所示,假设查看上面标志的downsample这一模块从分类输出映射到downsample的heatmap图像,则target_layer应被赋值为backbone.layer1.0.downsample。
class GroupCAM(object):def __init__(self, model, target_layer="layer4.2", groups=32):super(GroupCAM, self).__init__()self.model = modelself.groups = groupsself.gradients = dict()self.activations = dict()# 归一化self.transform_norm = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])# 反归一化self.Nutransform = UnNormalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])self.target_layer = target_layer# 模块注册,对于各模块名称,参见CAM-demo.py中,对模型的打印# 对于各个模块,为了方便引用,因此,我们使用层级引用方式if "backbone" in target_layer:for module in self.model.model.model.backbone.named_modules():if module[0] == '.'.join(target_layer.split('.')[1:]):module[1].register_forward_hook(self.forward_hook)module[1].register_backward_hook(self.backward_hook)if 'down' in target_layer:for module in self.model.model.model.down.named_modules():module[1].register_forward_hook(self.forward_hook)module[1].register_backward_hook(self.backward_hook)if "neck" in target_layer:for module in self.model.model.model.down.named_modules():module[1].register_forward_hook(self.forward_hook)module[1].register_backward_hook(self.backward_hook)if 'car_head' in target_layer:for module in self.model.model.model.car_head.named_modules():if module[0] == '.'.join(target_layer.split('.')[1:]):module[1].register_forward_hook(self.forward_hook)module[1].register_backward_hook(self.backward_hook)if "softmax" in target_layer:for module in self.model.model.softmax.named_modules():module[1].register_forward_hook(self.forward_hook)module[1].register_backward_hook(self.backward_hook)
CAM的计算
def forward(self, x, hp, retain_graph=False):output = self.model.track_cam(x, hp)cls = output["cls"]# idx = output["idx"]x_crop = output["x_crop"]bbox = output["bbox"]b, c, h, w = x_crop.size()self.model.model.zero_grad()idx = torch.argmax(cls)score = cls.reshape(-1)[idx]score.backward(retain_graph=retain_graph)gradients = self.gradients['value'].dataactivations = self.activations['value'].datab, k, u, v = activations.size()alpha = gradients.view(b, k, -1).mean(2)weights = alpha.view(b, k, 1, 1)activations = weights * activationsscore_saliency_map = torch.zeros((1, 1, h, w))if torch.cuda.is_available():activations = activations.cuda()score_saliency_map = score_saliency_map.cuda()masks = activations.chunk(self.groups, 1)with torch.no_grad():x_crop = torch.cat([x_crop[:, 2, :, :][:, None, :, :],x_crop[:, 1, :, :][:, None, :, :],x_crop[:, 0, :, :][:, None, :, :]], dim=1)/ 255.0norm_img = self.transform_norm(x_crop)blur_img = blur(norm_img)img = self.Nutransform(blur_img)base_line = self.model.model.track(torch.cat([img[:, 2, :, :][:, None, :, :],img[:, 1, :, :][:, None, :, :],img[:, 0, :, :][:, None, :, :]], dim=1) * 255.0)["cls"][:, 1, :, :][:, None, :, :].reshape(-1)[idx]for saliency_map in masks:saliency_map = saliency_map.sum(1, keepdims=True)saliency_map = F.relu(saliency_map)threshold = np.percentile(saliency_map.cpu().numpy(), 70)saliency_map = torch.where(saliency_map > threshold, saliency_map, torch.full_like(saliency_map, 0))saliency_map = F.interpolate(saliency_map, size=(h, w), mode='bilinear', align_corners=False)if saliency_map.max() == saliency_map.min():continue# normalize to 0-1norm_saliency_map = (saliency_map - saliency_map.min()) / (saliency_map.max() - saliency_map.min())# how much increase if keeping the highlighted region# predication on masked inputblur_input = norm_img * norm_saliency_map + blur_img * (1 - norm_saliency_map)norm_img = self.transform_norm(blur_input)blur_img = blur(norm_img)img = self.Nutransform(blur_img)outcls = self.model.model.track(torch.cat([img[:, 2, :, :][:, None, :, :],img[:, 1, :, :][:, None, :, :],img[:, 0, :, :][:, None, :, :]], dim=1) * 255.0)["cls"][:, 1, :, :][:, None, :, :].reshape(-1)[idx]score = outcls - base_line# score_saliency_map += score * saliency_mapscore_saliency_map += score * norm_saliency_mapscore_saliency_map = F.relu(score_saliency_map)score_saliency_map_min, score_saliency_map_max = score_saliency_map.min(), score_saliency_map.max()if score_saliency_map_min == score_saliency_map_max:return None, None, Nonescore_saliency_map = (score_saliency_map - score_saliency_map_min) / (score_saliency_map_max - score_saliency_map_min).datareturn score_saliency_map.cpu().data, x_crop.cpu().numpy(), bbox
CAM-demo.py
引入Softmax模块:由于在SiamCAR中ModelBuilder类中,在__init__()初始化引入各个模块中,在car_head以ModelBuilder本身并未包含softmax模块,然而在测试阶段却引入了这一模块,为了绘制heatmap图像,将softmax模块引入进来是必须的,因此,在CAM-demo中,我们将ModelBuilder定义的模型进行了二次封装,这也是我们在代码中看到多个model.model结构的原因。我们也能发现,实际上这种封装,并未破坏模型的整体结构,只不过需要在测试阶段的代码中需要将siamcar_tracker.py中,track()里的softmax去除即可。至于为什么要加入softmax,这与模型的分类分支定义有直接关系,通常,对于二分类模型,即前景和背景进行分类的模型,若分类输出为两分支,都需要经过softmax来预测分数,而在SiamCAR中恰巧对于训练和推理阶段,都引入了Softmax函数,因此将softmax作为SiamCAR的一部分是必须的。同理,对于Centerness分支,由于推理阶段进行了归一化,为了方便查看heatmap输出情况,需要将Centerness的归一化方式也作为一个模块注册进来(这里,我们没有提供其归一化的模块注册,若想使用,可以自行修改代码),才能进行进一步分析。
class Model(nn.Module):def __init__(self, model):super(Model,self).__init__()self.model = modelself.softmax = nn.Softmax(dim=1)def template(self, z):self.model.template(z)def track(self, x):outputs = self.model.track(x)outputs['cls'] = self.softmax(outputs['cls'])return outputs
最后注意一点,前面图片中Model类的定义中,引入了Softmax函数,从而方便CAM进行注册,因此在/path/to/SiamCAR-CAM/pysot/tracker/siamcar_tracker_cam.py中,要将原来的/path/to/SiamCAR-CAM/pysot/tracker/siamcar_tracker.py中的Softmax去除掉,避免出两个Softmax参与计算。
单目标跟踪通过CAM绘制heatmap图像(以SiamCAR为例)相关推荐
- 单目标跟踪SiamMask:特定目标车辆追踪 part2
日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) CNN:RCNN.SPPNet.Fast RCNN.Faste ...
- Video Target Tracking Based on Online Learning—TLD单目标跟踪算法详解
视频目标跟踪问题分析 视频跟踪技术的主要目的是从复杂多变的的背景环境中准确提取相关的目标特征,准确地识别出跟踪目标,并且对目标的位置和姿态等信息精确地定位,为后续目标物体行为分析提供足 ...
- 352万帧标注图片,1400个视频,亮风台推最大单目标跟踪数据集
CVPR 2019期间,专注于AR技术,整合软硬件的人工智能公司亮风台公开大规模单目标跟踪高质量数据集LaSOT,包含超过352万帧手工标注的图片和1400个视频,这也是目前为止最大的拥有密集标注的单 ...
- CVPR 2019 | 亮风台发布全球最大单目标跟踪数据集LaSOT
点击我爱计算机视觉标星,更快获取CVML新技术 CVPR 2019 正在美国加州举行,发布交流来自全球的工业界与学术界最新研究成果. 会上,亮风台公开大规模单目标跟踪高质量数据集LaSOT,包含超过3 ...
- 视觉单目标跟踪任务概述
视觉目标跟踪的主要目的是:模仿生理视觉系统的运动感知功能,通过对摄像头捕获到的图像序列进行分析,计算出运动目标在每一帧图像中的位置:然后,根据运动目标相关的特征值,将图像序列中连续帧的同一运动目标 ...
- 单目标跟踪——【数据集基准】RGB数据集OTB / NFS / TrackingNet / LaSOT / GOT-10k / UAV123 / VOT 简介
入手单目标跟踪的三个方面之数据集简介 目前单目标跟踪领域有哪些公认比较好的数据集? 这可以从一些优秀论文中找,看他们在验证自己的tracker用到哪些数据集.这些数据集的侧重不一,有的会包含快速移动: ...
- TrackingNet:最经典大规模、多样化的单目标跟踪数据集
除了多目标跟踪任务外,研究经典的.通用的单目标跟踪任务对于整个跟踪领域的发展有重要意义. 本期给大家介绍一个包含包含超过3万个视频,共有27个目标类别,视频数量和标注数量比以往的跟踪数据集更大的数据集 ...
- python 粒子滤波目标追踪_Python实现基于相关滤波的单目标跟踪算法
最近在阅读一些单目标跟踪的算法论文,主要看了一系列基于相关滤波的算法,尝试着用python实现了其中一些比较经典的算法,地址在https://github.com/wwdguu/pyCFTracker ...
- 单目标跟踪——个人笔记
单目标跟踪--个人笔记 以<Handcrafted and Deep Trackers: A Review of Recent ObjectTracking Approaches>为主线看 ...
最新文章
- qq空间网页版电脑_常用QQ软件,电脑C盘快满了,占用太多空间,如何快速清理?...
- [COGS58] 延绵的山峰
- 【转】void及void指针的深刻解析
- play2框架 jpa mysql_单元测试 – Playframework 2.2.x Java JPA – 用于单元测试和生产的独立数据库...
- 软件性能测试——负载测试的最佳实践
- POJ1006 中国剩余定理
- Python数据结构与算法(2.3)——链表
- 支持向量机SVM分析
- 奥维中如何关闭gcj02坐标_QGIS入门知识:啥是坐标系
- mysql3819错误,微软 Office 3819.20006 预览版发布:修复 Excel 导出 PDF 错误等问题
- 屏幕尺寸、分辨率、像素密度三者关系
- 一个完整的项目管理流程!
- 人工智能会为远程学习带来春天吗?
- eclipse 插件教程_编写Eclipse插件教程–第1部分
- 动态测试与静态测试--手工测试和自动化测试
- jmeter调试取样器
- LINUX下,C语言MALLOC可能达到的最大空间测试
- 微信又更新了,这次新增了一个大家喜闻乐见的新功能?
- SSL/TLS证书1年有效期新规已至,被“证书过期”支配的恐惧又增加了!
- 无线网络-LTE (01) LTE Overview
热门文章
- CentOS 7使用samba共享文件夹
- R语言关联规则及其可视化(Foodmart数据)
- 8月2日Pytorch笔记——梯度、全连接层、GPU加速、Visdom
- Python爱好者 socket模块传输文件 -
- OpenCV学习(二)---树莓派上安装opencv
- 【USACO题库】1.2.1 Milking Cows挤牛奶
- 用switch语句输入英文单词的星期几会显示中午的星期几
- Spring Cloud Task 主要是干什么的啊?跟 Quartz 和 Spring Task 有啥关系?
- php 循环电泳函数,【分享】单细胞凝胶电泳(彗星实验)分析软件CASP及教程【已搜索无重复】...
- 苹果AppStore被拒理由大全