*由于笔者用的笔记本是轻薄本,于是在代码里强制使用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() # 显示整个图形。
下面是对谷歌图标处理后得到的结果