*由于笔者用的笔记本是轻薄本,于是在代码里强制使用cpu进行操作,笔者可以根据自己的设备情况选择cpu或gpu
""" RGB图像模糊化处理(10×10卷积核) 该脚本使用PyTorch实现对RGB图像进行模糊处理。 核心操作是应用一个10x10的均值卷积核。 """ import torch # 导入PyTorch库,用于张量操作和神经网络 import torch.nn as nn # 导入PyTorch的神经网络模块 from PIL import Image # 导入PIL库(Python Imaging Library)用于图像处理 import torchvision.transforms as transforms # 导入torchvision的transforms模块,用于图像变换 import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块,用于绘图 # --------------------- 1. 加载图片 --------------------- input_image = Image.open("img.png").convert('RGB') # 打开名为 "img.png" 的图像文件,并将其转换为RGB模式。 # .convert('RGB') 确保图像是RGB格式,即使原始图像可能是其他格式(如RGBA或灰度)。 # --------------------- 2. 预处理 --------------------- transform = transforms.Compose([ transforms.ToTensor(), # 将PIL图像或NumPy数组转换为PyTorch张量。 # ToTensor() 还会自动将像素值从 [0, 255] 范围归一化到 [0, 1] 范围。 ]) input_tensor = transform(input_image).unsqueeze(0) # 对图像应用变换,并添加一个batch维度。 # unsqueeze(0) 在张量的第0维(即最外层)添加一个大小为1的维度, # 这是因为PyTorch的卷积层通常期望输入是4D张量 (batch_size, channels, height, width)。 # 因此,[C, H, W] 变为 [1, C, H, W]。 # --------------------- 3. 定义10×10模糊卷积 --------------------- kernel_size = 10 # 定义卷积核的大小为10x10。 conv_layer = nn.Conv2d( in_channels=3, # 输入通道数为3 (RGB) out_channels=3, # 输出通道数也为3 (保持RGB) kernel_size=kernel_size, # 卷积核大小 stride=1, # 步长为1 (卷积核每次滑动1个像素) padding=kernel_size // 2, # 填充大小,这里使用 (kernel_size // 2) 来保持输出尺寸与输入尺寸大致相同。 # 对于kernel_size=10, padding=5。 groups=3, # 设置 groups=3 意味着将输入和输出通道分成3组,每组独立进行卷积。 # 这实现了通道分离卷积,即每个输入通道只与对应的输出通道进行卷积。 # 对于RGB图像,这表示每个颜色通道(R, G, B)分别进行模糊处理。 bias=False, # 模糊操作通常不需要偏置项,因为模糊核本身决定了输出的平均值。 padding_mode='reflect' # 使用反射填充。 # 'reflect' 填充通过反射输入张量的内容来填充边界,这有助于减少边缘效应。 ) # 设置均值模糊核权重(所有值=1/100) with torch.no_grad(): # 这是一个上下文管理器,用于临时禁用梯度计算。 # 在with torch.no_grad(): 块中的操作不会被记录用于反向传播,这可以节省内存并提高速度。 blur_kernel = torch.ones((kernel_size, kernel_size)) / (kernel_size**2) # 创建一个全1的kernel_size x kernel_size张量,然后除以kernel_size**2, # 得到一个均值滤波核。例如,对于10x10的核,每个元素的值都是1/100。 conv_layer.weight.data = blur_kernel.view(1, 1, kernel_size, kernel_size).repeat(3, 1, 1, 1) # 将计算出的模糊核设置为卷积层的权重。 # blur_kernel.view(1, 1, kernel_size, kernel_size) 将模糊核重塑为 [1, 1, kernel_size, kernel_size] 的形状。 # repeat(3, 1, 1, 1) 将其沿通道维度 (第0维) 重复 3 次,以应用于RGB图像的每个通道。 # --------------------- 4. 执行卷积 --------------------- output_tensor = conv_layer(input_tensor) # 将模糊卷积层应用于输入张量。 # 这将生成模糊后的图像张量。 # --------------------- 5. 安全转换张量到numpy(修复报错) --------------------- def tensor_to_rgb_image(tensor): """ 将PyTorch张量转为numpy数组供显示 必须步骤: 1. .squeeze(0) 移除batch维度 [1,3,H,W] -> [3,H,W] 2. .permute(1,2,0) 调整通道顺序 [3,H,W] -> [H,W,3] 3. .detach() 断开梯度计算链(防止报错) 4. .cpu() 确保数据在CPU(兼容性) 5. .numpy() 转为numpy数组 """ # 1. 移除batch维度 tensor = tensor.squeeze(0) # 从形状为 [1, 3, H, W] 的张量中移除大小为 1 的 batch 维度,得到 [3, H, W]。 # 2. 调整通道顺序 tensor = tensor.permute(1, 2, 0) # 将通道维度从第一个位置移动到最后一个位置, # 将形状从 [3, H, W] 变为 [H, W, 3]。 这是因为matplotlib的imshow函数期望图像张量的形状为 [H, W, C]。 # 3. 断开梯度计算链 tensor = tensor.detach() # 从计算图中分离张量。 # 这是必要的,因为我们不希望在将张量转换为NumPy数组后,对NumPy数组的操作影响到原始张量的梯度。 # 如果不使用 detach(),可能会在尝试将需要梯度的张量转换为 NumPy 数组时引发错误。 # 4. 确保数据在CPU tensor = tensor.cpu() # 将张量移动到CPU。 # 虽然这不是绝对必要的,但这是一个好的实践, # 因为NumPy通常在CPU上运行,而且这样做可以避免潜在的设备兼容性问题。 # 5. 转为numpy数组 return tensor.numpy() # 将PyTorch张量转换为NumPy数组。 # --------------------- 6. 可视化 --------------------- plt.figure(figsize=(12, 6)) # 创建一个宽度为12英寸,高度为6英寸的图形。 plt.subplot(1, 2, 1) # 在1行2列的网格中创建一个子图,并选择第一个子图。 plt.imshow(tensor_to_rgb_image(input_tensor)) # 显示原始图像。 plt.title("Original Image") # 设置子图的标题。 plt.subplot(1, 2, 2) # 在1行2列的网格中创建一个子图,并选择第二个子图。 plt.imshow(tensor_to_rgb_image(output_tensor)) # 显示模糊后的图像。 plt.title(f"Blurred (10×10 Kernel)") # 设置子图的标题,包括使用的卷积核大小。 plt.show() # 显示整个图形。
下面是对谷歌图标处理后得到的结果
