[9] 深度学习Pytorch-transforms 图像增强(裁剪、翻转、旋转)

0. 往期内容

[一]深度学习Pytorch-张量定义与张量创建

[二]深度学习Pytorch-张量的操作:拼接、切分、索引和变换

[三]深度学习Pytorch-张量数学运算

[四]深度学习Pytorch-线性回归

[五]深度学习Pytorch-计算图与动态图机制

[六]深度学习Pytorch-autograd与逻辑回归

[七]深度学习Pytorch-DataLoader与Dataset(含人民币二分类实战)

[八]深度学习Pytorch-图像预处理transforms

[九]深度学习Pytorch-transforms图像增强(剪裁、翻转、旋转)

[十]深度学习Pytorch-transforms图像操作及自定义方法

深度学习Pytorch-transforms图像增强

  • 0. 往期内容
  • 1. 数据增强
  • 2. 剪裁
  • 2.1 transforms.CenterCrop(size)
  • 2.2 transforms.RandomCrop(size, fill=0, padding_mode='constant')
  • 2.3 transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(3/4, 4/3), interpolation)
  • 2.4 transforms.FiveCrop(size)
  • 2.5 transforms.TenCrop(size, vertical_flip=False)
  • 3. 旋转
  • 3.1 transforms.RandomHorizontalFlip(p=0.5)
  • 3.2 transforms.RandomVerticalFlip(p=0.5)
  • 3.3 transforms.RandomRotation(degrees, expand=False, center=None, fill=0, resample=None)
  • 4. 完整代码示例
  • 1. 数据增强


    2. 剪裁

    2.1 transforms.CenterCrop(size)

    transforms.CenterCrop(size)
    

    (1)功能:从图像中心裁剪尺寸为size的图片;
    (2)参数
    size: 若为int,则尺寸为size*size; 若为(h,w),则尺寸为h*w.
    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 1 CenterCrop
        transforms.CenterCrop(196),     # 裁剪为196*196,如果是512的话,超出244的区域填充为黑色
    
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    2.2 transforms.RandomCrop(size, fill=0, padding_mode=‘constant’)

    transforms.RandomCrop(size, padding=None, pad_if_needed=False, fill=0, padding_mode='constant')
    

    (1)功能:对图像随机裁剪出尺寸为size的图片;
    (2)参数
    size: 若为int,则尺寸为size*size; 若为(h,w),则尺寸为h*w
    padding: 设置填充大小:
    I. 当paddinga时,左右上下均填充a个像素;
    II. 当padding(a,b)时,左右填充a个像素,上下填充b个像素;
    III. 当padding(a,b,c,d)时,左、上、右、下分别填充a、b、c、d
    pad_if_need:若设定的size大于原图像尺寸,则填充;
    padding_mode:填充模式,有4种模式:
    I. constant:像素值由fill设定;
    II. edge:像素值由图像边缘的像素值决定;
    III. reflect:镜像填充,最后一个像素不镜像,eg. [1,2,3,4] --> [3,2,1,2,3,4,3,2];

    向左:由于1不会镜像,所以左边镜像2、3
    向右:由于4不会镜像,所以右边镜像3、2

    IV. symmetric:镜像填充,最后一个像素镜像,eg. [1,2,3,4] --> [2,1,1,2,3,4,4,3];

    向左:1、2镜像
    向右:4、3镜像

    fill:padding_mode='constant'时,用于设置填充的像素值;

    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 2 RandomCrop
        transforms.RandomCrop(224, padding=16),
        transforms.RandomCrop(224, padding=(16, 64)),
        transforms.RandomCrop(224, padding=16, fill=(255, 0, 0)), #fill=(255, 0, 0)RGB颜色
        
        #当size大于图片尺寸,即512大于244,pad_if_needed必须设置为True,否则会报错,其他区域会填充黑色(0,0,0)
        transforms.RandomCrop(512, pad_if_needed=True),   # 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'), #镜像
    
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    2.3 transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(3/4, 4/3), interpolation)

    transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(3/4, 4/3), interpolation=<InterpolationMode.BILINEAR: 'bilinear'>)
    

    (1)功能:随机大小、随机长宽比裁剪图片;
    (2)参数
    size: 裁剪图片尺寸,若为int,则尺寸为size*size; 若为(h,w),则尺寸为h*w,size是最后图片的尺寸;
    scale: 随机裁剪面积比例,默认区间(0.08,1),scale默认是随机选取0.08-1之间的一个数
    ratio: 随机长宽比,默认区间(3/4,4/3),ratio默认是随机选取3/4-4/3之间的一个数
    interpolation: 插值方法,eg. PIL. Image. NEAREST, PIL. Image. BILINEAR, PIL. Image. BICUBIC;
    (3)步骤
    随机确定scaleratio,然后对原始图片进行选取,再将选取的片段缩放到size大小;
    (4)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 3 RandomResizedCrop
        transforms.RandomResizedCrop(size=224, scale=(0.5, 0.5)),
    
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    2.4 transforms.FiveCrop(size)

    transforms.FiveCrop(size)
    

    (1)功能:在图像的左上、右上、左下、右下、中心随机剪裁出尺寸为size5张图片;
    (2)参数
    size: 裁剪图片尺寸,若为int,则尺寸为size*size; 若为(h,w),则尺寸为h*w;
    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 4 FiveCrop
        transforms.FiveCrop(112), #单独使用错误,直接使用transforms.FiveCrop(112)会报错,需要跟下一行一起使用
        #lamda的冒号之前是函数的输入(crops),冒号之后是函数的返回值
        transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])), #这里进行了ToTensor(),后面不需要执行Totensor()和Normalize,第114-115行
    
    ])
    

    使用FiveCrop时需要使用五维可视化,这是因为inputs为五维(batch_size*ncrops*chanel*图像宽*图像高),代码如下:

    for epoch in range(MAX_EPOCH):
        for i, data in enumerate(train_loader):
    
            inputs, labels = data   
    
            #五维可视化
            #使用FiveCrop时inputs为五维:batch_size*ncrops*chanel*图像宽*图像高,此时ncrops=5
            bs, ncrops, c, h, w = inputs.shape
            for n in range(ncrops):
                img_tensor = inputs[0, n, ...]  # C H W
                img = transform_invert(img_tensor, train_transform)
                plt.imshow(img)
                plt.show()
                plt.pause(1)
    

    2.5 transforms.TenCrop(size, vertical_flip=False)

    transforms.TenCrop(size, vertical_flip=False)
    

    (1)功能:在图像的左上、右上、左下、右下、中心随机剪裁出尺寸为size5张图片,然后再对这5张照片进行水平或者垂直镜像来获得总共10张图片;
    (2)参数
    size: 裁剪图片尺寸,若为int,则尺寸为size*size; 若为(h,w),则尺寸为h*w;
    vertical_flip: 是否垂直翻转,默认为False代表进行水平翻转;
    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 5 TenCrop
        transforms.TenCrop(112, vertical_flip=False),
        #lamda的冒号之前是函数的输入(crops),冒号之后是函数的返回值
        transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])),
    
    ])
    

    使用TenCrop时需要使用五维可视化,这是因为inputs五维batch_size*ncrops*chanel*图像宽*图像高),代码如下:

    for epoch in range(MAX_EPOCH):
        for i, data in enumerate(train_loader):
    
            inputs, labels = data   
    
            #五维可视化
            #使用FiveCrop时inputs为五维:batch_size*ncrops*chanel*图像宽*图像高,此时ncrops=5
            bs, ncrops, c, h, w = inputs.shape
            for n in range(ncrops):
                img_tensor = inputs[0, n, ...]  # C H W
                img = transform_invert(img_tensor, train_transform)
                plt.imshow(img)
                plt.show()
                plt.pause(1)
    

    3. 旋转

    3.1 transforms.RandomHorizontalFlip(p=0.5)

    transforms.RandomHorizontalFlip(p=0.5)
    

    (1)功能:根据概率对图片进行水平(左右)翻转,每次根据概率来决定是否执行翻转;
    (2)参数
    p: 反转概率;
    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 1 Horizontal Flip
        transforms.RandomHorizontalFlip(p=1), #执行水平翻转的概率为1
    
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    3.2 transforms.RandomVerticalFlip(p=0.5)

    transforms.RandomVerticalFlip(p=0.5)
    

    (1)功能:根据概率对图片进行垂直(上下)翻转,每次根据概率来决定是否执行翻转;
    (2)参数
    p: 反转概率;
    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 2 Vertical Flip
        transforms.RandomVerticalFlip(p=0.5), #执行垂直翻转的概率为0.5
    
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    3.3 transforms.RandomRotation(degrees, expand=False, center=None, fill=0, resample=None)

    transforms.RandomRotation(degrees, expand=False, center=None, fill=0, resample=None)
    

    (1)功能:对图片旋转随机的角度;
    (2)参数
    degrees: 旋转角度;
    I. 当degreesa时,在区间(-a,a)之间随机选择旋转角度;
    II. 当degrees(a,b)时,在区间(a,b)之间随机选择旋转角度;
    resample: 重采样方法;
    expand: 是否扩大图片以保持原图信息,因为旋转后可能有些信息被遮挡了而丢失,如果扩大尺寸则可以显示完整图片信息;
    center: 旋转点设置,默认沿着中心旋转;
    (3)代码示例

    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 3 RandomRotation
        transforms.RandomRotation(90),
        transforms.RandomRotation((90), expand=True), #当batch_size不为1时,expand使用时,张量在第0个维度尺寸需要匹配,需要对图片缩放到统一的size
        transforms.RandomRotation(30, center=(0, 0)), #左上角旋转
        transforms.RandomRotation(30, center=(0, 0), expand=True),   #expand只可以针对中心旋转来扩展,无法用于左上角旋转来找回丢失的信息
    
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    4. 完整代码示例

    # -*- coding: utf-8 -*-
    """
    # @file name  : transforms_methods_1.py
    # @author     : tingsongyu
    # @date       : 2019-09-11 10:08:00
    # @brief      : transforms方法(一)
    """
    import os
    import numpy as np
    import torch
    import random
    from torch.utils.data import DataLoader
    import torchvision.transforms as transforms
    from tools.my_dataset import RMBDataset
    from PIL import Image
    from matplotlib import pyplot as plt
    
    
    def set_seed(seed=1):
        random.seed(seed)
        np.random.seed(seed)
        torch.manual_seed(seed)
        torch.cuda.manual_seed(seed)
    
    
    set_seed(1)  # 设置随机种子
    
    # 参数设置
    MAX_EPOCH = 10
    BATCH_SIZE = 1
    LR = 0.01
    log_interval = 10
    val_interval = 1
    rmb_label = {"1": 0, "100": 1}
    
    
    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):
            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)
            img_.mul_(std[:, None, None]).add_(mean[:, None, None]) #normalize是减去均值除以标准差,反操作就是乘以标准差加上均值
    
        img_ = img_.transpose(0, 2).transpose(0, 1)  # C*H*W --> H*W*C  通道变换
        img_ = np.array(img_) * 255 #将0-1转换为0-255
    
        #针对chanel是三通道还是一通道分别转换
        if img_.shape[2] == 3: #RGB图像
            img_ = Image.fromarray(img_.astype('uint8')).convert('RGB') #将ndarray数据转换为image
        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_
    
    
    # ============================ step 1/5 数据 ============================
    split_dir = os.path.join("..", "..", "data", "rmb_split")
    train_dir = os.path.join(split_dir, "train")
    valid_dir = os.path.join(split_dir, "valid")
    
    norm_mean = [0.485, 0.456, 0.406]
    norm_std = [0.229, 0.224, 0.225]
    
    
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)), #图片统一缩放到244*244
    
        # 1 CenterCrop
        # transforms.CenterCrop(196),     # 裁剪为196*196,如果是512的话,超出244的区域填充为黑色
    
        # 2 RandomCrop
        # transforms.RandomCrop(224, padding=16),
        # transforms.RandomCrop(224, padding=(16, 64)),
        # transforms.RandomCrop(224, padding=16, fill=(255, 0, 0)), #fill=(255, 0, 0)RGB颜色
        
        #当size大于图片尺寸,即512大于244,pad_if_needed必须设置为True,否则会报错,其他区域会填充黑色(0,0,0)
        # transforms.RandomCrop(512, pad_if_needed=True),   # 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.5, 0.5)),
    
        # 4 FiveCrop
        # transforms.FiveCrop(112), #单独使用错误,直接使用transforms.FiveCrop(112)会报错,需要跟下一行一起使用
        #lamda的冒号之前是函数的输入(crops),冒号之后是函数的返回值
        # transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])), #这里进行了ToTensor(),后面不需要执行Totensor()和Normalize,第114-115行
        # 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), #执行水平翻转的概率为1
    
        # 2 Vertical Flip
        # transforms.RandomVerticalFlip(p=0.5), #执行垂直翻转的概率为0.5
    
        # 3 RandomRotation
        # transforms.RandomRotation(90),
        # transforms.RandomRotation((90), expand=True), #当batch_size不为1时,expand使用时,张量在第0个维度尺寸需要匹配,需要对图片缩放到统一的size
        # transforms.RandomRotation(30, center=(0, 0)), #左上角旋转
        # transforms.RandomRotation(30, center=(0, 0), expand=True),   #expand只可以针对中心旋转来扩展,无法用于左上角旋转来找回丢失的信息
        
        #若使用FiveCrop或TenCrop,以下两行需要注释掉
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    
    valid_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std)
    ])
    
    # 构建MyDataset实例
    train_data = RMBDataset(data_dir=train_dir, transform=train_transform)
    valid_data = RMBDataset(data_dir=valid_dir, transform=valid_transform)
    
    # 构建DataLoder
    train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
    valid_loader = DataLoader(dataset=valid_data, batch_size=BATCH_SIZE)
    
    
    # ============================ step 5/5 训练 ============================
    for epoch in range(MAX_EPOCH):
        for i, data in enumerate(train_loader):
    
            inputs, labels = data   
           
            #四维可视化
            #input的大小为四维:batch_size*chanel*图像宽*图像高 # B C H W
            img_tensor = inputs[0, ...]     # C H W
            img = transform_invert(img_tensor, train_transform) #对transform进行逆变换,可视化图片
            plt.imshow(img)
            plt.show()
            plt.pause(0.5)
            plt.close()
    
            #五维可视化
            #使用FiveCrop时inputs为五维:batch_size*ncrops*chanel*图像宽*图像高,此时ncrops=5
            bs, ncrops, c, h, w = inputs.shape
            # for n in range(ncrops):
            #     img_tensor = inputs[0, n, ...]  # C H W
            #     img = transform_invert(img_tensor, train_transform)
            #     plt.imshow(img)
            #     plt.show()
            #     plt.pause(1)
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » [9] 深度学习Pytorch-transforms 图像增强(裁剪、翻转、旋转)

    发表评论