Skip to content

Commit ffce121

Browse files
Support GradCAM (#7626)
* fix slice infer one image save_results (#7654) * Support GradCAM Cascade_rcnn forward bugfix * code style fix * BBoxCAM class name fix * Add gradcam tutorial and demo --------- Co-authored-by: Feng Ni <[email protected]>
1 parent d56cf3f commit ffce121

File tree

11 files changed

+515
-13
lines changed

11 files changed

+515
-13
lines changed
60.7 KB
Loading

docs/tutorials/GradCAM_cn.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 目标检测热力图
2+
3+
## 1.简介
4+
5+
基于backbone特征图计算物体预测框的cam(类激活图)
6+
7+
## 2.使用方法
8+
* 以PP-YOLOE为例,准备好数据之后,指定网络配置文件、模型权重地址和图片路径以及输出文件夹路径,使用脚本调用tools/cam_ppdet.py计算图片中物体预测框的grad_cam热力图。下面为运行脚本示例。
9+
```shell
10+
python tools/cam_ppdet.py -c configs/ppyoloe/ppyoloe_crn_l_300e_coco.yml --infer_img demo/000000014439.jpg --cam_out cam_ppyoloe -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_crn_l_300e_coco.pdparams
11+
```
12+
13+
* **参数**
14+
15+
| FLAG | 用途 |
16+
| :----------------------: |:-----------------------------------------------------------------------------------------------------:|
17+
| -c | 指定配置文件 |
18+
| --infer_img | 用于预测的图片路径 |
19+
| --cam_out | 指定输出路径 |
20+
| -o | 设置或更改配置文件里的参数内容, 如 -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_crn_l_300e_coco.pdparams |
21+
22+
* 运行效果
23+
24+
<center>
25+
<img src="../images/grad_cam_ppyoloe_demo.jpg" width="500" >
26+
</center>
27+
<br><center>cam_ppyoloe/225.jpg</center></br>
28+
29+
## 3. 目前支持基于FasterRCNN和YOLOv3系列的网络。
30+
* FasterRCNN网络热图可视化脚本
31+
```bash
32+
python tools/cam_ppdet.py -c configs/faster_rcnn/faster_rcnn_r50_vd_fpn_2x_coco.yml --infer_img demo/000000014439.jpg --cam_out cam_faster_rcnn -o weights=https://paddledet.bj.bcebos.com/models/faster_rcnn_r50_vd_fpn_ssld_2x_coco.pdparams
33+
```
34+
* PPYOLOE网络热图可视化脚本
35+
```bash
36+
python tools/cam_ppdet.py -c configs/ppyoloe/ppyoloe_crn_l_300e_coco.yml --infer_img demo/000000014439.jpg --cam_out cam_ppyoloe -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_crn_l_300e_coco.pdparams
37+
```

docs/tutorials/GradCAM_en.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Object detection grad_cam heatmap
2+
3+
## 1.Introduction
4+
Calculate the cam (class activation map) of the object predict bbox based on the backbone feature map
5+
6+
## 2.Usage
7+
* Taking PP-YOLOE as an example, after preparing the data, specify the network configuration file, model weight address, image path and output folder path, and then use the script to call tools/cam_ppdet.py to calculate the grad_cam heat map of the prediction box. Below is an example run script.
8+
```shell
9+
python tools/cam_ppdet.py -c configs/ppyoloe/ppyoloe_crn_l_300e_coco.yml --infer_img demo/000000014439.jpg --cam_out cam_ppyoloe -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_crn_l_300e_coco.pdparams
10+
```
11+
12+
* **Arguments**
13+
14+
| FLAG | description |
15+
| :----------------------: |:---------------------------------------------------------------------------------------------------------------------------------:|
16+
| -c | Select config file |
17+
| --infer_img | Image path |
18+
| --cam_out | Directory for output |
19+
| -o | Set parameters in configure file, for example: -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_crn_l_300e_coco.pdparams |
20+
21+
* result
22+
23+
<center>
24+
<img src="../images/grad_cam_ppyoloe_demo.jpg" width="500" >
25+
</center>
26+
<br><center>cam_ppyoloe/225.jpg</center></br>
27+
28+
29+
## 3.Currently supports networks based on FasterRCNN and YOLOv3 series.
30+
* FasterRCNN bbox heat map visualization script
31+
```bash
32+
python tools/cam_ppdet.py -c configs/faster_rcnn/faster_rcnn_r50_vd_fpn_2x_coco.yml --infer_img demo/000000014439.jpg --cam_out cam_faster_rcnn -o weights=https://paddledet.bj.bcebos.com/models/faster_rcnn_r50_vd_fpn_ssld_2x_coco.pdparams
33+
```
34+
* PPYOLOE bbox heat map visualization script
35+
```bash
36+
python tools/cam_ppdet.py -c configs/ppyoloe/ppyoloe_crn_l_300e_coco.yml --infer_img demo/000000014439.jpg --cam_out cam_ppyoloe -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_crn_l_300e_coco.pdparams
37+
```

ppdet/engine/trainer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ def setup_metrics_for_loader():
10391039
image.save(save_name, quality=95)
10401040

10411041
start = end
1042-
1042+
return results
10431043
def _get_save_image_name(self, output_dir, image_path):
10441044
"""
10451045
Get save image name from source image path.

ppdet/modeling/architectures/cascade_rcnn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def _forward(self):
108108
im_shape = self.inputs['im_shape']
109109
scale_factor = self.inputs['scale_factor']
110110

111-
bbox, bbox_num = self.bbox_post_process(
111+
bbox, bbox_num, before_nms_indexes = self.bbox_post_process(
112112
preds, (refined_rois, rois_num), im_shape, scale_factor)
113113
# rescale the prediction back to origin image
114114
bbox, bbox_pred, bbox_num = self.bbox_post_process.get_pred(

ppdet/modeling/architectures/faster_rcnn.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,21 @@ def _forward(self):
8282
self.inputs)
8383
return rpn_loss, bbox_loss
8484
else:
85+
cam_data = {} # record bbox scores and index before nms
8586
rois, rois_num, _ = self.rpn_head(body_feats, self.inputs)
8687
preds, _ = self.bbox_head(body_feats, rois, rois_num, None)
88+
cam_data['scores'] = preds[1]
8789

8890
im_shape = self.inputs['im_shape']
8991
scale_factor = self.inputs['scale_factor']
90-
bbox, bbox_num = self.bbox_post_process(preds, (rois, rois_num),
92+
bbox, bbox_num, before_nms_indexes = self.bbox_post_process(preds, (rois, rois_num),
9193
im_shape, scale_factor)
94+
cam_data['before_nms_indexes'] = before_nms_indexes # , bbox index before nms, for cam
9295

9396
# rescale the prediction back to origin image
9497
bboxes, bbox_pred, bbox_num = self.bbox_post_process.get_pred(
9598
bbox, bbox_num, im_shape, scale_factor)
96-
return bbox_pred, bbox_num
99+
return bbox_pred, bbox_num, cam_data
97100

98101
def get_loss(self, ):
99102
rpn_loss, bbox_loss = self._forward()
@@ -105,8 +108,8 @@ def get_loss(self, ):
105108
return loss
106109

107110
def get_pred(self):
108-
bbox_pred, bbox_num = self._forward()
109-
output = {'bbox': bbox_pred, 'bbox_num': bbox_num}
111+
bbox_pred, bbox_num, cam_data = self._forward()
112+
output = {'bbox': bbox_pred, 'bbox_num': bbox_num, 'cam_data': cam_data}
110113
return output
111114

112115
def target_bbox_forward(self, data):

ppdet/modeling/architectures/yolo.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ def _forward(self):
9898
return yolo_losses
9999

100100
else:
101+
cam_data = {} # record bbox scores and index before nms
101102
yolo_head_outs = self.yolo_head(neck_feats)
103+
cam_data['scores'] = yolo_head_outs[0]
102104

103105
if self.for_mot:
104106
# the detection part of JDE MOT model
@@ -118,14 +120,17 @@ def _forward(self):
118120
yolo_head_outs, self.yolo_head.mask_anchors)
119121
elif self.post_process is not None:
120122
# anchor based YOLOs: YOLOv3,PP-YOLO,PP-YOLOv2 use mask_anchors
121-
bbox, bbox_num = self.post_process(
123+
bbox, bbox_num, before_nms_indexes = self.post_process(
122124
yolo_head_outs, self.yolo_head.mask_anchors,
123125
self.inputs['im_shape'], self.inputs['scale_factor'])
126+
cam_data['before_nms_indexes'] = before_nms_indexes
124127
else:
125128
# anchor free YOLOs: PP-YOLOE, PP-YOLOE+
126-
bbox, bbox_num = self.yolo_head.post_process(
129+
bbox, bbox_num, before_nms_indexes = self.yolo_head.post_process(
127130
yolo_head_outs, self.inputs['scale_factor'])
128-
output = {'bbox': bbox, 'bbox_num': bbox_num}
131+
# data for cam
132+
cam_data['before_nms_indexes'] = before_nms_indexes
133+
output = {'bbox': bbox, 'bbox_num': bbox_num, 'cam_data': cam_data}
129134

130135
return output
131136

ppdet/modeling/heads/ppyoloe_head.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ def post_process(self, head_outs, scale_factor):
462462
# `exclude_nms=True` just use in benchmark
463463
return pred_bboxes, pred_scores
464464
else:
465-
bbox_pred, bbox_num, _ = self.nms(pred_bboxes, pred_scores)
466-
return bbox_pred, bbox_num
465+
bbox_pred, bbox_num, before_nms_indexes = self.nms(pred_bboxes, pred_scores)
466+
return bbox_pred, bbox_num, before_nms_indexes
467467

468468

469469
def get_activation(name="LeakyReLU"):

ppdet/modeling/post_process.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def __call__(self, head_out, rois, im_shape, scale_factor):
6767
"""
6868
if self.nms is not None:
6969
bboxes, score = self.decode(head_out, rois, im_shape, scale_factor)
70-
bbox_pred, bbox_num, _ = self.nms(bboxes, score, self.num_classes)
70+
bbox_pred, bbox_num, before_nms_indexes = self.nms(bboxes, score, self.num_classes)
7171

7272
else:
7373
bbox_pred, bbox_num = self.decode(head_out, rois, im_shape,
@@ -82,7 +82,10 @@ def __call__(self, head_out, rois, im_shape, scale_factor):
8282
bbox_pred = paddle.concat([bbox_pred, fake_bboxes])
8383
bbox_num = bbox_num + 1
8484

85-
return bbox_pred, bbox_num
85+
if self.nms is not None:
86+
return bbox_pred, bbox_num, before_nms_indexes
87+
else:
88+
return bbox_pred, bbox_num
8689

8790
def get_pred(self, bboxes, bbox_num, im_shape, scale_factor):
8891
"""

0 commit comments

Comments
 (0)