code: def get_bboxes_cams(self):
      # Get the bboxes prediction(after nms result) of the input
      inference_result = self.get_bboxes()
  
      # read input image
      # Todo: Support folder multi-images process
      from PIL import Image
  
      img = np.array(Image.open(self.cfg.infer_img))
  
      # data for calaulate bbox grad_cam
      extra_data = inference_result["extra_data"]
      """
          Example of Faster_RCNN based architecture:
              extra_data: {'scores': tensor with shape [num_of_bboxes_before_nms, num_classes], for example: [1000, 80]
                         'nms_keep_idx': tensor with shape [num_of_bboxes_after_nms, 1], for example: [300, 1]
                        }
          Example of YOLOv3 based architecture:
              extra_data: {'scores': tensor with shape [1, num_classes, num_of_yolo_bboxes_before_nms], #for example: [1, 80, 8400]
                         'nms_keep_idx': tensor with shape [num_of_yolo_bboxes_after_nms, 1], # for example: [300, 1]
                        }
          """
  
      # array index of the predicted bbox before nms
      if self.cfg.architecture in self.nms_idx_need_divid_numclass_arch:
          # some network's bbox array shape before nms may be like [num_of_bboxes_before_nms, num_classes, 4],
          # we need to divide num_classes to get the before_nms_index；
          # currently, only include the rcnn architectures （fasterrcnn, maskrcnn, cascadercnn);
          before_nms_indexes = (
              extra_data["nms_keep_idx"].cpu().numpy() // self.num_class
          )  # num_class
      else:
          before_nms_indexes = extra_data["nms_keep_idx"].cpu().numpy()
  
      # Calculate and visualize the heatmap of per predict bbox
      for index, target_bbox in enumerate(inference_result["bbox"]):
          # target_bbox: [cls, score, x1, y1, x2, y2]
          # filter bboxes with low predicted scores
          if target_bbox[1] < self.FLAGS.draw_threshold:
              continue
  
          target_bbox_before_nms = int(before_nms_indexes[index])
  
          if len(extra_data["scores"].shape) == 2:
              score_out = extra_data["scores"][target_bbox_before_nms]
          else:
              score_out = extra_data["scores"][
                  0, :, target_bbox_before_nms
              ]
          """
              There are two kinds array shape of bbox score output :
                  1) [num_of_bboxes_before_nms, num_classes], for example: [1000, 80]
                  2) [num_of_image, num_classes, num_of_yolo_bboxes_before_nms], for example: [1, 80, 1000]
              """
  
          # construct one_hot label and do backward to get the gradients
          predicted_label = paddle.argmax(score_out)
          label_onehot = paddle.nn.functional.one_hot(
              predicted_label, num_classes=len(score_out)
          )
          label_onehot = label_onehot.squeeze()
          target = paddle.sum(score_out * label_onehot)
          target.backward(retain_graph=True)
  
          if (
              "backbone" in self.target_layer_name
              or "neck" in self.target_layer_name
          ):  # backbone/neck level feature
              if isinstance(
                  self.target_feats[self.target_layer_name], list
              ):
                  # when the featuremap contains of multiple scales,
                  # take the featuremap of the last scale
                  # Todo: fuse the cam result from multisclae featuremaps
                  if (
                      self.target_feats[self.target_layer_name][
                          -1
                      ].shape[-1]
                      == 1
                  ):
                      """
                      if the last level featuremap is 1x1 size,
                      we take the second last one
                      """
                      cam_grad = (
                          self.target_feats[self.target_layer_name][-2]
                          .grad.squeeze()
                          .cpu()
                          .numpy()
                      )
                      cam_feat = (
                          self.target_feats[self.target_layer_name][-2]
                          .squeeze()
                          .cpu()
                          .numpy()
                      )
                  else:
                      cam_grad = (
                          self.target_feats[self.target_layer_name][-1]
                          .grad.squeeze()
                          .cpu()
                          .numpy()
                      )
                      cam_feat = (
                          self.target_feats[self.target_layer_name][-1]
                          .squeeze()
                          .cpu()
                          .numpy()
                      )
              else:
                  cam_grad = (
                      self.target_feats[self.target_layer_name]
                      .grad.squeeze()
                      .cpu()
                      .numpy()
                  )
                  cam_feat = (
                      self.target_feats[self.target_layer_name]
                      .squeeze()
                      .cpu()
                      .numpy()
                  )
          else:  # roi level feature
              cam_grad = (
                  self.target_feats[self.target_layer_name]
                  .grad.squeeze()
                  .cpu()
                  .numpy()[target_bbox_before_nms]
              )
              cam_feat = (
                  self.target_feats[self.target_layer_name]
                  .squeeze()
                  .cpu()
                  .numpy()[target_bbox_before_nms]
              )
  
          # grad_cam:
          exp = grad_cam(cam_feat, cam_grad)
  
          if (
              "backbone" in self.target_layer_name
              or "neck" in self.target_layer_name
          ):
              """
              when use backbone/neck featuremap,
              we first do the cam on whole image,
              and then set the area outside the predic bbox to 0
              """
              # reshape the cam image to the input image size
              resized_exp = resize_cam(
                  exp, (img.shape[1], img.shape[0])
              )
              mask = np.zeros((img.shape[0], img.shape[1], 3))
              mask[
                  int(target_bbox[3]) : int(target_bbox[5]),
                  int(target_bbox[2]) : int(target_bbox[4]),
                  :,
              ] = 1
              resized_exp = resized_exp * mask
              # add the bbox cam back to the input image
              overlay_vis = np.uint8(resized_exp * 0.4 + img * 0.6)
          elif "roi" in self.target_layer_name:
              # get the bbox part of the image
              bbox_img = copy.deepcopy(
                  img[
                      int(target_bbox[3]) : int(target_bbox[5]),
                      int(target_bbox[2]) : int(target_bbox[4]),
                      :,
                  ]
              )
              # reshape the cam image to the bbox size
              resized_exp = resize_cam(
                  exp, (bbox_img.shape[1], bbox_img.shape[0])
              )
              # add the bbox cam back to the bbox image
              bbox_overlay_vis = np.uint8(
                  resized_exp * 0.4 + bbox_img * 0.6
              )
              # put the bbox_cam image to the original image
              overlay_vis = copy.deepcopy(img)
              overlay_vis[
                  int(target_bbox[3]) : int(target_bbox[5]),
                  int(target_bbox[2]) : int(target_bbox[4]),
                  :,
              ] = bbox_overlay_vis
          else:
              print(
                  "Only supported cam for  backbone/neck feature and roi feature,  the others are not supported temporarily!"
              )
              sys.exit()
  
          # put the bbox rectangle on image
          cv2.rectangle(
              overlay_vis,
              (int(target_bbox[2]), int(target_bbox[3])),
              (int(target_bbox[4]), int(target_bbox[5])),
              (0, 0, 255),
              2,
          )
  
          # save visualization result
          cam_image = Image.fromarray(overlay_vis)
          cam_image.save(self.FLAGS.cam_out + "/" + str(index) + ".jpg")
  
          # clear gradients after each bbox grad_cam
          target.clear_gradient()
          for n, v in self.trainer.model.named_sublayers():
              v.clear_gradients()
  
file_path: PaddleDetection/ppdet/utils/cam_utils.py
imports: import numpy
  import cv2
  import os
  import sys
  import glob
  from ppdet.utils.logger import setup_logger
  import copy
  import paddle
  from ppdet.engine import Trainer
label_desc: Use image node_id 5 for calling the BBoxCAM obj's underlying member method code with example usage: obj.get_bboxes_cams() without return types