使用自定义transforms对图片每个像素位置随机添加黑白噪声并展示结果,具体看下面的代码,只需修改图片路径即可运行。
# -*- coding: utf-8 -*-
import os
import numpy as np
import torch
import random
import math
import torchvision.transforms as transforms
from PIL import Image
from matplotlib import pyplot as plt
# 自定义一个类,对图片每个像素随机添加黑白噪声
class AddNoise(object):
"""
Args:
s(float): 噪声率
p (float): 执行该操作的概率
"""
def __init__(self, s=0.5, p=0.9):
assert isinstance(s, float) or (isinstance(p, float)) # 判断输入参数格式是否正确
self.s = s
self.p = p
# transform 会调用该方法
def __call__(self, img): # 使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用并执行对应的代码。
"""
(PIL全称 Python Imaging Library,是 Python 平台一个功能非常强大而且简单易用的图像处理库。python3叫pillow)
Args:
img (PIL Image): PIL Image
Returns:
PIL Image: PIL image.
"""
# 如果随机概率小于 seld.p,则执行 transform
if random.uniform(0, 1) < self.p: # random.uniform(参数1,参数2) 返回参数1和参数2之间的任意值
# 把 image 转为 array
img_ = np.array(img).copy()
# 获得 shape,高*宽*通道数
h, w, c = img_.shape
# 信噪比
signal_pct = self.s
# 噪声的比例 = 1 -信噪比
noise_pct = (1 - self.s)
# 选择的值为 (0, 1, 2),每个取值的概率分别为 [signal_pct, noise_pct/2., noise_pct/2.]
# 1 为白噪声,2 为 黑噪声
#numpy.random.choice(a, size=None, replace=True, p=None)解释
#从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
#replace:True表示可以取相同数字,False表示不可以取相同数字
#数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。
mask = np.random.choice((0, 1, 2), size=(h, w, 1), p=[signal_pct, noise_pct / 2., noise_pct / 2.])
mask = np.repeat(mask, c, axis=2)
img_[mask == 1] = 255 # 白噪声
img_[mask == 2] = 0 # 黑噪声
# 再转换为 image
return Image.fromarray(img_.astype('uint8')).convert('RGB')
# 如果随机概率大于 seld.p,则直接返回原图
else:
return img
#指定随机数种子方法
def set_seed(seed=1):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
# 对 tensor 进行反标准化操作(就是通过均值和方差再转为255*255*255的RGB图像),方便可视化使用。
def transform_invert(img_, transform_train):
"""
将data 进行反transfrom操作
:param img_: tensor
:param transform_train: torchvision.transforms
:return: PIL image
"""
# 如果有标准化操作
if 'Normalize' in str(transform_train):
# 取出标准化的 transform
# filter用法:用于过滤序列,过滤掉不符合条件的元素,返回符合条件的元素组成新列表。
# filter(function,iterable),function -- 判断函数,iterable -- 可迭代对象
norm_transform = list(filter(lambda x: isinstance(x, transforms.Normalize), transform_train.transforms))
# 取出均值
mean = torch.tensor(norm_transform[0].mean, dtype=img_.dtype, device=img_.device)
# 取出标准差
std = torch.tensor(norm_transform[0].std, dtype=img_.dtype, device=img_.device)
# 乘以标准差,加上均值
#torch.mul(a, b) 是矩阵a和b对应位相乘,a和b的维度必须相等。
#这里是img_先×方差再加上平均值,与normalize的操作相反
img_.mul_(std[:, None, None]).add_(mean[:, None, None])
# 把 C*H*W 变为 H*W*C
#transpose()函数改变索引值,如(1,0,2),对应(y,x,z)
img_ = img_.transpose(0, 2).transpose(0, 1) # C*H*W --> H*W*C
# 把 0~1 的值变为 0~255
img_ = np.array(img_) * 255
# 如果是 RGB 图
if img_.shape[2] == 3:
img_ = Image.fromarray(img_.astype('uint8')).convert('RGB')
# 如果是灰度图
elif img_.shape[2] == 1:
img_ = Image.fromarray(img_.astype('uint8').squeeze())
else:
raise Exception("Invalid img shape, expected 1 or 3 in axis 2, but got {}!".format(img_.shape[2]))
return img_
# 下面开始调用上面的方法
set_seed(1) # 设置随机种子为1
# 参数设置
MAX_EPOCH = 10 # 最大迭代次数
BATCH_SIZE = 1 # 批大小
LR = 0.01 # 学习率/步长
log_interval = 10 # 日志更新间隔
val_interval = 1
#定义normalize标准化的平均值和方差
norm_mean = [0.485, 0.456, 0.406] # normalize各个通道的平均值
norm_std = [0.229, 0.224, 0.225] # normalize各个通道数的方差
#定义对图片的操作组合,上一个教程有详细介绍方法
train_transform = transforms.Compose([ # 定义transforms组
# 缩放到 (224, 224) 大小,会拉伸
transforms.Resize((224, 224)),
# 1 CenterCrop 中心裁剪
# transforms.CenterCrop(512), # 512
# transforms.CenterCrop(196), # 512
# 2 RandomCrop
# transforms.RandomCrop(224, padding=16),
# transforms.RandomCrop(224, padding=(16, 64)),
# transforms.RandomCrop(224, padding=16, fill=(255, 0, 0)),
# transforms.RandomCrop(512, pad_if_needed=True),
# transforms.RandomCrop(224, padding=64, padding_mode='edge'),
# transforms.RandomCrop(224, padding=64, padding_mode='reflect'),
# transforms.RandomCrop(1024, padding=1024, padding_mode='symmetric'),
# 3 RandomResizedCrop
# transforms.RandomResizedCrop(size=224, scale=(0.08, 1)),#scale在(0.08, 1)范围内缩放
# transforms.RandomResizedCrop(size=224, scale=(0.5, 0.5)),
# 4 FiveCrop
# transforms.FiveCrop(112),
# 返回的是 tuple,因此需要转换为 tensor
#沿着一个新维度对输入张量序列进行连接,序列中所有的张量都应该为相同形状
# transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])),
# 5 TenCrop
# transforms.TenCrop(112, vertical_flip=False),
# transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])),
# 1 Horizontal Flip
# transforms.RandomHorizontalFlip(p=1),
# 2 Vertical Flip
# transforms.RandomVerticalFlip(p=1),
# 3 RandomRotation
# transforms.RandomRotation(90),
# transforms.RandomRotation((90), expand=True),
# transforms.RandomRotation(30, center=(0, 0)),
# transforms.RandomRotation(30, center=(0, 0), expand=True), # expand only for center rotation
# 1 Pad
# transforms.Pad(padding=32, fill=(255, 0, 0), padding_mode='constant'),
# transforms.Pad(padding=(8, 64), fill=(255, 0, 0), padding_mode='constant'),
# transforms.Pad(padding=(8, 16, 32, 64), fill=(255, 0, 0), padding_mode='constant'),
# transforms.Pad(padding=(8, 16, 32, 64), fill=(255, 0, 0), padding_mode='symmetric'),
# 2 ColorJitter
# transforms.ColorJitter(brightness=0.5),
# transforms.ColorJitter(contrast=0.5),
# transforms.ColorJitter(saturation=0.5),
# transforms.ColorJitter(hue=0.3),
# 3 Grayscale
# transforms.Grayscale(num_output_channels=3),
# 4 Affine
# transforms.RandomAffine(degrees=30),
# transforms.RandomAffine(degrees=0, translate=(0.2, 0.2), fillcolor=(255, 0, 0)),
# transforms.RandomAffine(degrees=0, scale=(0.7, 0.7)),
# transforms.RandomAffine(degrees=0, shear=(0, 0, 0, 45)),
# transforms.RandomAffine(degrees=0, shear=90, fillcolor=(255, 0, 0)),
# 5 Erasing
# transforms.ToTensor(),
# transforms.RandomErasing(p=1, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=(254/255, 0, 0)),
# transforms.RandomErasing(p=1, scale=(0.02, 0.33), ratio=(0.3, 3.3), value='fads43'),
# 1 RandomChoice
# transforms.RandomChoice([transforms.RandomVerticalFlip(p=1), transforms.RandomHorizontalFlip(p=1)]),
# 2 RandomApply
# transforms.RandomApply([transforms.RandomAffine(degrees=0, shear=45, fillcolor=(255, 0, 0)),
# transforms.Grayscale(num_output_channels=3)], p=0.5),
# 3 RandomOrder
# transforms.RandomOrder([transforms.RandomRotation(15),
# transforms.Pad(padding=32),
# transforms.RandomAffine(degrees=0, translate=(0.01, 0.1), scale=(0.9, 1.1))]),
# 调用自定义transforms方法
AddNoise(0.5, p=0.5), # 调用上面的类中的__Call__()
transforms.ToTensor(), #
transforms.Normalize(norm_mean, norm_std),
])
path_img = os.path.join("F:\\Desktop", "24110307_8.jpg") # 图片位置
img = Image.open(path_img).convert('RGB') # 0~255
img = transforms.Resize((224, 224))(img)
img_tensor = train_transform(img) # 执行定义的transforms组内的图片操作
## 展示单张图片
# 这里把转换后的 tensor 再转换为图片
convert_img = transform_invert(img_tensor, train_transform)
#绘制图像
plt.rcParams["font.sans-serif"]=["SimHei"]# 用来正常显示中文标签
plt.rcParams["axes.unicode_minus"]=False
plt.rcParams['figure.dpi'] = 300 #分辨率
plt.figure(figsize=(6,2.5)) # 表示figure 的大小为宽、长(单位为inch)
plt.subplot(1, 2, 1)
plt.imshow(img) #原始图像
plt.subplot(1, 2, 2)
plt.imshow(convert_img) #转换后的图像
plt.show()
# plt.pause(0.5)
plt.close()
## 展示 FiveCrop 和 TenCrop 的图片
# ncrops, c, h, w = img_tensor.shape
# columns=2 # 两列
# rows= math.ceil(ncrops/2) # 计算多少行
# # 把每个 tensor ([c,h,w]) 转换为 image
# for i in range(ncrops):
# img = transform_invert(img_tensor[i], train_transform)
# plt.subplot(rows, columns, i+1)
# plt.imshow(img)
# plt.show()
运行结果:
其它的注释的transforms图片操作方法都可以试一试。
参考资料:https://pytorch.zhangxiann.com/2-tu-pian-chu-li-yu-shu-ju-jia-zai/2.3-er-shi-er-zhong-transforms-tu-pian-shu-ju-yu-chu-li-fang-fa
欢迎关注个人公众号【智能建造小硕】(分享计算机编程、人工智能、智能建造、日常学习和科研经验等,欢迎大家关注交流。)