【Pytorch学习笔记五】数据预处理自定义torchvision.transforms图片操作方法对图片随机添加黑白噪声并展示结果(实践)

【Pytorch学习笔记五】数据预处理自定义torchvision.transforms图片操作方法对图片随机添加黑白噪声并展示结果(实践)_第1张图片

使用自定义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

欢迎关注个人公众号【智能建造小硕】(分享计算机编程、人工智能、智能建造、日常学习和科研经验等,欢迎大家关注交流。)

你可能感兴趣的:(Pytorch学习笔记,pytorch,学习,python)