img_show.permute(1, 2, 0).numpy()

permute() 方法用于重新排列张量的维度顺序,而 .numpy() 方法用于将张量转换为 NumPy 数组。这两者结合使用时,通常是为了将 PyTorch 张量转换为适合显示或处理的格式。

背景:PyTorch 张量与 NumPy 数组的区别

  • PyTorch 张量:通常用于深度学习模型的输入和输出,支持 GPU 加速。
  • NumPy 数组:适合进行通用的数值计算和图像处理,但不支持 GPU。

img_show.permute(1, 2, 0).numpy()**

1. permute(1, 2, 0)
  • permute() 方法用于重新排列张量的维度顺序。
  • 在 PyTorch 中,图像张量通常以 [C, H, W] 的格式存储,其中:
    • C 是通道数(例如,RGB 图像的通道数为 3)。
    • H 是图像的高度。
    • W 是图像的宽度。
  • 但 NumPy 和许多图像处理库(如 OpenCV、Matplotlib)通常使用 [H, W, C] 的格式。
  • 因此,permute(1, 2, 0) 的作用是将张量从 [C, H, W] 重新排列为 [H, W, C],使其符合 NumPy 的格式。
2. .numpy()
  • .numpy() 方法将 PyTorch 张量转换为 NumPy 数组。
  • 在转换之前,张量必须在 CPU 上(如果张量在 GPU 上,需要先调用 .cpu())。
  • 转换后的 NumPy 数组可以用于图像显示、保存或其他处理。
img_show = img_show.permute(1, 2, 0).numpy()

这行代码的作用是:

  1. 将图像张量从 [C, H, W] 格式转换为 [H, W, C] 格式。
  2. 将转换后的张量转换为 NumPy 数组。

例子

一个 PyTorch 张量 img_show,表示一个 RGB 图像:

import torch
import numpy as np
import matplotlib.pyplot as plt

# 创建一个随机的 PyTorch 张量,模拟 RGB 图像
img_show = torch.randn(3, 256, 256)  # [C, H, W] 格式

# 转换为 NumPy 数组
img_show = img_show.permute(1, 2, 0).numpy()  # [H, W, C] 格式

# 显示图像
plt.imshow(img_show)
plt.show()

注意事项

  1. 张量必须在 CPU 上

    • 如果张量在 GPU 上,需要先调用 .cpu()

      Python复制

      img_show = img_show.cpu().permute(1, 2, 0).numpy()
      
  2. 数据类型

    • 如果张量的数据类型是 torch.float32,转换为 NumPy 数组后仍然是 float32

    • 如果需要将像素值范围从 [0, 1] 转换为 [0, 255],需要额外处理:

      img_show = (img_show * 255).astype(np.uint8)
      
  3. 通道顺序

    • 如果图像是灰度图像,permute() 是不必要的,因为灰度图像的格式是 [H, W]
    • 如果图像是 BGR 格式(如 OpenCV 使用的格式),需要额外处理通道顺序。

总结

img_show.permute(1, 2, 0).numpy() 的作用是将 PyTorch 张量从 [C, H, W] 格式转换为 [H, W, C] 格式,并将其转换为 NumPy 数组。这种操作通常用于将张量转换为适合图像显示或处理的格式。

from skimage.segmentation import slic, mark_boundaries import torchvision.transforms as transforms import numpy as np from PIL import Image import matplotlib.pyplot as plt import torch.nn as nn import torch # 定义超像素池化层 class SuperpixelPooling(nn.Module): def init(self, n_segments): super(SuperpixelPooling, self).init() self.n_segments = n_segments def forward(self, x): # 使用 SLIC 算法生成超像素标记图 segments = slic(x.permute(0, 2, 3, 1).numpy(), n_segments=self.n_segments, compactness=10) # 将超像素标记图转换为张量 segments_tensor = torch.from_numpy(segments).unsqueeze(0).unsqueeze(0) # 将张量 x 与超像素标记图张量 segments_tensor 进行逐元素相乘 pooled = x * segments_tensor.float() # 在超像素维度上进行最大池化 pooled = nn.AdaptiveMaxPool2d((self.n_segments, 1))(pooled) # 压缩超像素维度 pooled = pooled.squeeze(3) # 返回池化后的特征图 return pooled # 加载图像 image = Image.open('3.jpg') # 转换为 PyTorch 张量 transform = transforms.ToTensor() img_tensor = transform(image).unsqueeze(0) # 将 PyTorch 张量转换为 Numpy 数组 img_np = img_tensor.numpy().transpose(0, 2, 3, 1)[0] # 使用 SLIC 算法生成超像素标记图 segments = slic(img_np, n_segments=60, compactness=10) # 将超像素标记图转换为张量 segments_tensor = torch.from_numpy(segments).unsqueeze(0).float() # 将超像素索引映射可视化 plt.imshow(segments, cmap='gray') plt.show() # 将 Numpy 数组转换为 PIL 图像 segment_img = Image.fromarray((mark_boundaries(img_np, segments) * 255).astype(np.uint8)) # 保存超像素索引映射可视化 segment_img.save('segment_map.jpg') # 使用超像素池化层进行池化 pooling_layer = SuperpixelPooling(n_segments=60) pooled_tensor = pooling_layer(img_tensor) # 将超像素池化后的特征图可视化 plt.imshow(pooled_tensor.squeeze().numpy().transpose(1, 0), cmap='gray') plt.show() ,上述代码出现问题:RuntimeError: adaptive_max_pool2d(): Expected 3D or 4D tensor, but got: [1, 1, 3, 512, 512],如何修改
06-09
from skimage.segmentation import slic, mark_boundaries import torchvision.transforms as transforms import numpy as np from PIL import Image import matplotlib.pyplot as plt import torch.nn as nn import torch # 定义超像素池化层 class SuperpixelPooling(nn.Module): def init(self, n_segments): super(SuperpixelPooling, self).init() self.n_segments = n_segments def forward(self, x): # 使用 SLIC 算法生成超像素标记图 segments = slic(x.numpy().transpose(1, 2, 0), n_segments=self.n_segments, compactness=10) # 将超像素标记图转换为张量 segments_tensor = torch.from_numpy(segments).unsqueeze(0).unsqueeze(0) # 将张量 x 与超像素标记图张量 segments_tensor 进行逐元素相乘 pooled = x * segments_tensor.float() # 在超像素维度上进行最大池化 pooled = nn.AdaptiveMaxPool2d((self.n_segments, 1))(pooled) # 压缩超像素维度 pooled = pooled.squeeze(3) # 返回池化后的特征图 return pooled # 加载图像 image = Image.open('3.jpg') # 转换为 PyTorch 张量 transform = transforms.ToTensor() img_tensor = transform(image).unsqueeze(0) # 将 PyTorch 张量转换为 Numpy 数组 img_np = img_tensor.numpy().transpose(0, 2, 3, 1)[0] # 使用 SLIC 算法生成超像素标记图 segments = slic(img_np, n_segments=60, compactness=10) # 将超像素标记图转换为张量 segments_tensor = torch.from_numpy(segments).unsqueeze(0).float() # 将超像素索引映射可视化 plt.imshow(segments, cmap='gray') plt.show() # 将 Numpy 数组转换为 PIL 图像 segment_img = Image.fromarray((mark_boundaries(img_np, segments) * 255).astype(np.uint8)) # 保存超像素索引映射可视化 segment_img.save('segment_map.jpg') # 使用超像素池化层进行池化 pooling_layer = SuperpixelPooling(n_segments=60) pooled_tensor = pooling_layer(img_tensor) # 将超像素池化后的特征图可视化 plt.imshow(pooled_tensor.squeeze().numpy().transpose(1, 0), cmap='gray') plt.show() ,上述代码出现问题:segments = slic(x.numpy().transpose(1, 2, 0), n_segments=self.n_segments, compactness=10) ValueError: axes don't match array,如何修改
06-09
``` class GradCAMpp: def __init__(self, model, target_layer): self.model = model self.target_layer = target_layer self.activations = None self.gradients = None # 注册前向传播和反向传播钩子 target_layer.register_forward_hook(self.save_activation) target_layer.register_backward_hook(self.save_gradient) def save_activation(self, module, input, output): self.activations = output.detach() def save_gradient(self, module, grad_input, grad_output): self.gradients = grad_output[0].detach() def __call__(self, input_tensor, class_idx=None): # 前向传播 self.model.eval() output = self.model(input_tensor) if class_idx is None: class_idx = output.argmax(dim=1) # 反向传播 self.model.zero_grad() one_hot = torch.zeros_like(output) one_hot[0][class_idx] = 1.0 output.backward(gradient=one_hot) # 计算权重 grad = self.gradients[0].numpy() act = self.activations[0].numpy() # Grad-CAM++核心计算 grad_power = grad ** 2 grad_power_times_act = grad_power * act weights = grad_power_times_act / (2 * grad_power_times_act + np.sum(act * grad_power_times_act, axis=(1,2), keepdims=True)) weights = np.sum(weights, axis=(1,2)) # 生成热力图 cam = np.zeros(act.shape[1:], dtype=np.float32) for i, w in enumerate(weights): cam += w * act[i] cam = np.maximum(cam, 0) cam = cv2.resize(cam, (224, 224)) cam = cam - np.min(cam) cam = cam / np.max(cam) return cam # 获取目标层(使用最后一个卷积层) target_layer = model.conv2 # 创建Grad-CAM++实例 gradcam_pp = GradCAMpp(model, target_layer) # 反标准化函数 def denormalize(tensor): mean = torch.tensor([0.5, 0.5, 0.5]) std = torch.tensor([0.5, 0.5, 0.5]) return tensor * std.view(3, 1, 1) + mean.view(3, 1, 1) # 可视化示例 num_samples = 3 # 可视化样本数量 fig, axes = plt.subplots(num_samples, 3, figsize=(15, 10)) for idx in range(num_samples): # 获取测试样本 img, label = test_dataset[idx] input_tensor = img.unsqueeze(0) # 获取预测结果 with torch.no_grad(): output = model(input_tensor) pred_class = output.argmax().item() # 生成热力图 cam = gradcam_pp(input_tensor, pred_class) # 处理原始图像 img_denorm = denormalize(img).permute(1, 2, 0).numpy() # 绘制结果 axes[idx, 0].imshow(img_denorm) axes[idx, 0].axis('off') axes[idx, 0].set_title(f'Original (True: {full_dataset.classes[label]})') axes[idx, 1].imshow(cam, cmap='jet') axes[idx, 1].axis('off') axes[idx, 1].set_title(f'Grad-CAM++ Heatmap (Pred: {full_dataset.classes[pred_class]})') axes[idx, 2].imshow(img_denorm) axes[idx, 2].imshow(cam, cmap='jet', alpha=0.5) axes[idx, 2].axis('off') axes[idx, 2].set_title('Overlay') plt.tight_layout() plt.show()```Warning (from warnings module): File "C:\Users\29930\AppData\Local\Programs\Python\Python311\Lib\site-packages\torch\nn\modules\module.py", line 1830 self._maybe_warn_non_full_backward_hook(args, result, grad_fn) FutureWarning: Using a non-full backward hook when the forward contains multiple autograd Nodes is deprecated and will be removed in future versions. This hook will be missing some grad_input. Please use register_full_backward_hook to get the documented behavior. Warning (from warnings module): File "D:/正式建模/CNN+++.py", line 150 weights = grad_power_times_act / (2 * grad_power_times_act + RuntimeWarning: invalid value encountered in divide
最新发布
03-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值