mmdetection中的backbone为了添加FPN进行多尺度的检测,backbone的输入是4个stage的feature map的tensor,详见resnet.py源码中的Example:

    1. >>> from mmdet.models import ResNet
    2. >>> import torch
    3. >>> self = ResNet(depth=18)
    4. >>> self.eval()
    5. >>> inputs = torch.rand(1, 3, 32, 32)
    6. >>> level_outputs = self.forward(inputs)
    7. >>> for level_out in level_outputs:
    8. ... print(tuple(level_out.shape))
    9. (1, 64, 8, 8)
    10. (1, 128, 4, 4)
    11. (1, 256, 2, 2)
    12. (1, 512, 1, 1)

    Backbone(以resnet.py为例)中的forward函数return的是一个tuple,并且包含四个不同stage的feature map:

    1. class ResNet(nn.Module):
    2. ........
    3. ........
    4. ........
    5. ........
    6. def forward(self, x):
    7. x = self.conv1(x)
    8. x = self.norm1(x)
    9. x = self.relu(x)
    10. x = self.maxpool(x)
    11. outs = []
    12. for i, layer_name in enumerate(self.res_layers):
    13. res_layer = getattr(self, layer_name)
    14. x = res_layer(x)
    15. if i in self.out_indices:
    16. outs.append(x)
    17. return tuple(outs)

    所以在测试的过程中就很好的就行特征图可视化的操作。

    首先在mmdet/models/detectors/two_stage.py中找到test的函数:

    1. class TwoStageDetector(BaseDetector, RPNTestMixin, BBoxTestMixin,
    2. MaskTestMixin):
    3. ........
    4. ........
    5. ........
    6. ........
    7. def extract_feat(self, img):
    8. """Directly extract features from the backbone+neck
    9. """
    10. x = self.backbone(img)
    11. # 此时在这里输出的就是backbone的4个stage的feature map
    12. if self.with_neck: # 如果有neck,比如FPN,最终的输出就是4个stage经过FPN处理后的feature map
    13. x = self.neck(x)
    14. return x
    15. ........
    16. ........
    17. ........
    18. ........
    19. def simple_test(self, img, img_meta, proposals=None, rescale=False):
    20. """Test without augmentation."""
    21. assert self.with_bbox, "Bbox head must be implemented."
    22. x = self.extract_feat(img)
    23. # print(x[0].shape, len(x))
    24. if proposals is None:
    25. proposal_list = self.simple_test_rpn(x, img_meta,
    26. self.test_cfg.rpn)
    27. else:
    28. proposal_list = proposals
    29. det_bboxes, det_labels = self.simple_test_bboxes(
    30. x, img_meta, proposal_list, self.test_cfg.rcnn, rescale=rescale)
    31. bbox_results = bbox2result(det_bboxes, det_labels,
    32. self.bbox_head.num_classes)
    33. if not self.with_mask:
    34. return bbox_results # modified origin : return bbox_results
    35. # return bbox_results
    36. else:
    37. segm_results = self.simple_test_mask(
    38. x, img_meta, det_bboxes, det_labels, rescale=rescale)
    39. return bbox_results, segm_results

    所以如果要将feature map输出,就要更改这两个函数的返回值,首先:

    1. def extract_feat(self, img):
    2. """Directly extract features from the backbone+neck
    3. """
    4. x = self.backbone(img)
    5. if self.with_neck:
    6. x = self.neck(x)
    7. return x
    8. # change the code to
    9. def extract_feat(self, img):
    10. """Directly extract features from the backbone+neck
    11. """
    12. x = self.backbone(img)
    13. if self.with_neck:
    14. _x = self.neck(x)
    15. return x, _x
    16. # 也可以在x = self.backbone(img)下面一行将这个元组中的tensor转变为numpy形式进行保存,
    17. # 在通过opencv, PIL进行图像可视化

    不让原来backbone中输出的feature map的变量被覆盖掉,所以可以使用不同的变量名进行赋值操作。

    原来的simple_test函数是只返回图片中检测到物体的坐标,这里我们稍微做一下修改:

    1. def simple_test(self, img, img_meta, proposals=None, rescale=False):
    2. """Test without augmentation."""
    3. assert self.with_bbox, "Bbox head must be implemented."
    4. # origin code : x = self.extract_feat(img)
    5. x, _x = self.extract_feat(img)
    6. '''
    7. x : backbone中四个stage的feature map的tensor
    8. _x : 经过FPN后4个stage的feature map的tensor
    9. '''
    10. # print(x[0].shape, len(x))
    11. if proposals is None:
    12. proposal_list = self.simple_test_rpn(x, img_meta,
    13. self.test_cfg.rpn)
    14. else:
    15. proposal_list = proposals
    16. det_bboxes, det_labels = self.simple_test_bboxes(
    17. x, img_meta, proposal_list, self.test_cfg.rcnn, rescale=rescale)
    18. bbox_results = bbox2result(det_bboxes, det_labels,
    19. self.bbox_head.num_classes)
    20. if not self.with_mask:
    21. '''
    22. 在这里加上x, _x的返回值就行!!!!
    23. '''
    24. return bbox_results, x # modified origin : return bbox_results
    25. # return bbox_results
    26. else:
    27. segm_results = self.simple_test_mask(
    28. x, img_meta, det_bboxes, det_labels, rescale=rescale)
    29. return bbox_results, segm_results