学习资源站

YOLOv11改进-可视化热力图-支持自定义模型,置信度选择等功能(论文必备)

一、本文介绍

本文给大家带来的机制是的 可视化热力图功能 热力图 作为我们论文当中的必备一环,可以展示出我们呈现机制的有效性,本文的内容支持YOLOv11最新版本,同时支持视频讲解,本文的内容是根据检测头的输出内容,然后来绘图, 产生6300张预测图片,从中选取出有效的热力图来绘图。

在开始之前给大家推荐一下我的专栏,本专栏每周更新3-10篇最新前沿机制 | 包括二次创新全网无重复,以及融合改进(大家拿到之后添加另外一个改进机制在你的 数据集 上实现涨点即可撰写论文),还有各种前沿顶会改进机制 |,更有包含我所有附赠的文件(文件内集成我所有的改进机制全部注册完毕可以直接运行)和交流群和视频讲解提供给大家。

欢迎大家订阅我的专栏一起学习YOLO!



二、项目完整代码

我们将这个代码,复制粘贴到我们YOLOv11的仓库里然后创建一个py文件存放进去即可。

  1. import warnings
  2. warnings.filterwarnings('ignore')
  3. warnings.simplefilter('ignore')
  4. import torch, yaml, cv2, os, shutil
  5. import numpy as np
  6. np.random.seed(0)
  7. import matplotlib.pyplot as plt
  8. from tqdm import trange
  9. from PIL import Image
  10. from ultralytics.nn.tasks import DetectionModel as Model
  11. from ultralytics.utils.torch_utils import intersect_dicts
  12. from ultralytics.utils.ops import xywh2xyxy
  13. from pytorch_grad_cam import GradCAMPlusPlus, GradCAM, XGradCAM
  14. from pytorch_grad_cam.utils.image import show_cam_on_image
  15. from pytorch_grad_cam.activations_and_gradients import ActivationsAndGradients
  16. def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
  17. # Resize and pad image while meeting stride-multiple constraints
  18. shape = im.shape[:2] # current shape [height, width]
  19. if isinstance(new_shape, int):
  20. new_shape = (new_shape, new_shape)
  21. # Scale ratio (new / old)
  22. r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
  23. if not scaleup: # only scale down, do not scale up (for better val mAP)
  24. r = min(r, 1.0)
  25. # Compute padding
  26. ratio = r, r # width, height ratios
  27. new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
  28. dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
  29. if auto: # minimum rectangle
  30. dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
  31. elif scaleFill: # stretch
  32. dw, dh = 0.0, 0.0
  33. new_unpad = (new_shape[1], new_shape[0])
  34. ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
  35. dw /= 2 # divide padding into 2 sides
  36. dh /= 2
  37. if shape[::-1] != new_unpad: # resize
  38. im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
  39. top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
  40. left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
  41. im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
  42. return im, ratio, (dw, dh)
  43. class yolov11_heatmap:
  44. def __init__(self, weight, cfg, device, method, layer, backward_type, conf_threshold, ratio):
  45. device = torch.device(device)
  46. ckpt = torch.load(weight)
  47. model_names = ckpt['model'].names
  48. csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32
  49. model = Model(cfg, ch=3, nc=len(model_names)).to(device)
  50. csd = intersect_dicts(csd, model.state_dict(), exclude=['anchor']) # intersect
  51. model.load_state_dict(csd, strict=False) # load
  52. model.eval()
  53. print(f'Transferred {len(csd)}/{len(model.state_dict())} items')
  54. target_layers = [eval(layer)]
  55. method = eval(method)
  56. colors = np.random.uniform(0, 255, size=(len(model_names), 3)).astype(np.int32)
  57. self.__dict__.update(locals())
  58. def post_process(self, result):
  59. logits_ = result[:, 4:]
  60. boxes_ = result[:, :4]
  61. sorted, indices = torch.sort(logits_.max(1)[0], descending=True)
  62. return torch.transpose(logits_[0], dim0=0, dim1=1)[indices[0]], torch.transpose(boxes_[0], dim0=0, dim1=1)[indices[0]], xywh2xyxy(torch.transpose(boxes_[0], dim0=0, dim1=1)[indices[0]]).cpu().detach().numpy()
  63. def draw_detections(self, box, color, name, img):
  64. xmin, ymin, xmax, ymax = list(map(int, list(box)))
  65. cv2.rectangle(img, (xmin, ymin), (xmax, ymax), tuple(int(x) for x in color), 2)
  66. cv2.putText(img, str(name), (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, tuple(int(x) for x in color), 2, lineType=cv2.LINE_AA)
  67. return img
  68. def __call__(self, img_path, save_path):
  69. # remove dir if exist
  70. if os.path.exists(save_path):
  71. shutil.rmtree(save_path)
  72. # make dir if not exist
  73. os.makedirs(save_path, exist_ok=True)
  74. # img process
  75. img = cv2.imread(img_path)
  76. img = letterbox(img)[0]
  77. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  78. img = np.float32(img) / 255.0
  79. tensor = torch.from_numpy(np.transpose(img, axes=[2, 0, 1])).unsqueeze(0).to(self.device)
  80. # init ActivationsAndGradients
  81. grads = ActivationsAndGradients(self.model, self.target_layers, reshape_transform=None)
  82. # get ActivationsAndResult
  83. result = grads(tensor)
  84. activations = grads.activations[0].cpu().detach().numpy()
  85. # postprocess to yolo output
  86. post_result, pre_post_boxes, post_boxes = self.post_process(result[0])
  87. for i in trange(int(post_result.size(0) * self.ratio)):
  88. if float(post_result[i].max()) < self.conf_threshold:
  89. break
  90. self.model.zero_grad()
  91. # get max probability for this prediction
  92. if self.backward_type == 'class' or self.backward_type == 'all':
  93. score = post_result[i].max()
  94. score.backward(retain_graph=True)
  95. if self.backward_type == 'box' or self.backward_type == 'all':
  96. for j in range(4):
  97. score = pre_post_boxes[i, j]
  98. score.backward(retain_graph=True)
  99. # process heatmap
  100. if self.backward_type == 'class':
  101. gradients = grads.gradients[0]
  102. elif self.backward_type == 'box':
  103. gradients = grads.gradients[0] + grads.gradients[1] + grads.gradients[2] + grads.gradients[3]
  104. else:
  105. gradients = grads.gradients[0] + grads.gradients[1] + grads.gradients[2] + grads.gradients[3] + grads.gradients[4]
  106. b, k, u, v = gradients.size()
  107. weights = self.method.get_cam_weights(self.method, None, None, None, activations, gradients.detach().numpy())
  108. weights = weights.reshape((b, k, 1, 1))
  109. saliency_map = np.sum(weights * activations, axis=1)
  110. saliency_map = np.squeeze(np.maximum(saliency_map, 0))
  111. saliency_map = cv2.resize(saliency_map, (tensor.size(3), tensor.size(2)))
  112. saliency_map_min, saliency_map_max = saliency_map.min(), saliency_map.max()
  113. if (saliency_map_max - saliency_map_min) == 0:
  114. continue
  115. saliency_map = (saliency_map - saliency_map_min) / (saliency_map_max - saliency_map_min)
  116. # add heatmap and box to image
  117. cam_image = show_cam_on_image(img.copy(), saliency_map, use_rgb=True)
  118. "不想在图片中绘画出边界框和置信度,注释下面的一行代码即可"
  119. cam_image = self.draw_detections(post_boxes[i], self.colors[int(post_result[i, :].argmax())], f'{self.model_names[int(post_result[i, :].argmax())]} {float(post_result[i].max()):.2f}', cam_image)
  120. cam_image = Image.fromarray(cam_image)
  121. cam_image.save(f'{save_path}/{i}.png')
  122. def get_params():
  123. params = {
  124. 'weight': 'yolov8n.pt', # 训练出来的权重文件
  125. 'cfg': 'ultralytics/cfg/models/v8/yolov8n.yaml', # 训练权重对应的yaml配置文件
  126. 'device': 'cuda:0',
  127. 'method': 'GradCAM', # GradCAMPlusPlus, GradCAM, XGradCAM , 使用的热力图库文件不同的效果不一样可以多尝试
  128. 'layer': 'model.model[9]', # 想要检测的对应层
  129. 'backward_type': 'all', # class, box, all
  130. 'conf_threshold': 0.01, # 0.6 # 置信度阈值,有的时候你的进度条到一半就停止了就是因为没有高于此值的了
  131. 'ratio': 0.02 # 0.02-0.1
  132. }
  133. return params
  134. if __name__ == '__main__':
  135. model = yolov11_heatmap(**get_params())
  136. model(r'ultralytics/assets/bus.jpg', 'result') # 第一个是检测的文件, 第二个是保存的路径

三、参数解析

下面上面项目核心代码的参数解析,共有7个,能够起到作用的参数并不多。

参数名 参数类型 参数讲解
0 weights str 用于检测视频的权重文件地址(可以是你训练好的,也可以是官方提供的)
1 cfg str 你选择的权重对应的yaml配置文件,请注意一定要对应否则会报错和不显示图片
2 device str 设备的选择可以用GPU也可以用CPU
3 method str 使用的热力图第三方库的版本,不同的版本效果也不一样。
4 layer str 想要检测的对应层,比如这里设置的是9那么检测的就是第九层
4 backward_type str 检测的类别
5 conf_threshold str 置信度阈值,有的时候你的进度条没有满就是因为没有大于这个阈值的图片了
6 ratio int YOLOv11一次产生6300张预测框,选择多少比例的图片绘画热力图。

四、项目的使用教程

4.1 步骤一

我们在Yolo仓库的目录下创建一个py文件将代码存放进去,如下图所示。


4.2 步骤二

我们按照参数解析部分的介绍填好大家的参数,主要配置的有两个一个就是权重文件地址另一个就是图片的地址。


4.3 步骤三

我们挺好之后运行文件即可,图片就会保存在同级目录下的新的文件夹result内。


4.4 置信度和检测框

看下下面的说明就行。


五、本文总结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~