美文网首页Pytorch 全流程学习
Pytorch的第一步:(2) transforms 的使用

Pytorch的第一步:(2) transforms 的使用

作者: 夜和大帝 | 来源:发表于2020-12-15 11:38 被阅读0次

    在我的前一篇文章:Pytorch的第一步:(1) Dataset类的使用 里,不论是使用 torchvision.datasets 还是我们自定义了 Dataset 子类,都有一个形参 transforms 被传入。上篇文章我没有详细讲解,是因为这是一块很大的内容,故专门写本文讲解。
    transforms 是图像处理函数,主要用于对索引出来的图片进行 剪切、翻转、平移、仿射等操作,也就是得到我们想要的预处理过程。

    pytorch 提供的 torchvision.transforms 模块是专门用来进行图像预处理的,本文按照处理方式的不同,分组介绍和试验这些预处理方法

    注意点

    • transforms.Compose() 可以把多类转换操作结合起来
    • 可转换的图像包括 PIL Image, Tensor Imagebatch of Tensor Images. Tensor Image是(C, H, W)形状的tensor, batch of Tensor Images是形状为(B, C, H, W) 的tensor
    • 设置随机数种子要用 torch.manual_seed(n)
    • 本文提到的 transform 方法都是一个类,我们有两种处理方式,一个是实例化这个 transform 类,然后把图片传入,另一种方式是实例化一个 transforms.Compose() 类。然后对 transforms.Compose 的实例传入图像处理。区别是如果直接实例化 transform 类,一次只能对图像做一种 transform 操作。但是 transforms.Compose() 类支持传入多个 transform 类,即一个 transforms.Compose() 包含多个 transform 类,这样 一次性能够实现多个操作:
    import torchvision.transforms as transforms
    pic = imread('...')
    #---------方 法 1---------- 一次一种处理方式
    transform = transforms.CenterCrop(720)  # 中心裁剪
    picProcessed1 = transform(pic)
    transform = transforms.RandomHorizontalFlip(p=0.5)  # 随机水平翻转
    picProcessed2 = transform(picProcessed1)
    
    #---------方 法 2----------一步到位
    transform = transforms.Compose([
        transforms.CenterCrop(720)
        transforms.RandomHorizontalFlip(p=0.5)
        ])
    picProcessed = transform(pic)
    

    0、 Transforms 方法概览

    1 裁剪 Crop

    1.1 中心裁剪: transforms.CenterCrop()
    1.2 随机裁剪: transforms.RandomCrop()
    1.3 随机长宽比裁剪: transforms.RandomResizedCrop()
    1.4 上下左右中心裁剪: transforms.FiveCrop()
    1.5 上下左右中心裁剪后翻转: transforms.TenCrop()

    2 翻转和旋转 Flip and Rotation

    2.1 依概率 p 水平翻转: transforms.RandomHorizontalFlip(p=0.5)
    2.2 依概率 p 垂直翻转: transforms.RandomVerticalFlip(p=0.5)
    2.3 随机旋转: transforms.RandomRotation()

    3 图像变换

    3.1 resize: transforms.Resize
    3.2 标准化: transforms.Normalize
    3.3 转为 tensor,并归一化至[0-1]: transforms.ToTensor
    3.4 填充: transforms.Pad
    3.5 修改亮度、对比度和饱和度: transforms.ColorJitter
    3.6 转灰度图: transforms.Grayscale
    3.7 线性变换: transforms.LinearTransformation()
    3.8 仿射变换: transforms.RandomAffine
    3.9 依概率 p 转为灰度图: transforms.RandomGrayscale
    3.10 将数据转换为 PILImage: transforms.ToPILImage
    3.11 transforms.Lambda: Apply a user-defined lambda as a transform.

    注意本文将用以下代码为测试代码,逐个说明.
    此外,如果没有特殊说明,被处理后的tensor或者图像,其数据类型不变。

    import matplotlib.pyplot as plt
    import torchvision.transforms as transforms
    import torchvision as tv
    
    # 定义转换函数
    transform = transforms.Compose([
        transforms.CenterCrop(720) #这是一个例子
        #========================================
        # 此处添加需要放入的变换函数
        #========================================
    ])
    
    # 读图
    picTensor = tv.io.read_image("testpic.jpg")
    
    # 转换图
    picTransformed = transform(picTensor)
    
    # 显示
    picNumpy = picTensor.permute(1, 2, 0).numpy()
    plt.imshow(picNumpy)
    plt.show()
    picNumpy = picTransformed.permute(1, 2, 0).numpy()
    plt.imshow(picNumpy)
    plt.show()
    

    本文使用的样例图片为:torch.Size([3, 2100, 3174])


    示例图片,图片被分为24格方便观察变化,图片尺寸是 torch.Size([3, 2100, 3174])

    1 裁剪 Crop

    1.1 中心裁剪: transforms.CenterCrop( size )

    *size* 可以是 int、list、turple, 功能是把图片剪裁,
    只保留以图片中心点为中心,*size* 为尺寸的那部分。代码如下:
    
    transforms.CenterCrop(size=1080)
    输出:
    CenterCrop(size=(1080, 1080))
    转换后图片形状: torch.Size([3, 2100, 3174])
    转换后图片形状: torch.uint8 
    
    转换后图片形状: torch.Size([3, 1080, 1080])
    转换后图片形状: torch.uint8
    
    中心裁剪


    1.2 随机裁剪: transforms.RandomCrop()

    transforms.RandomCrop(size=1600, padding=300,
                          pad_if_needed=False, fill=0, padding_mode='constant')
    这里的pad是先把四周填充 pad=300 像素,然后再剪裁,
    相当于先给图片扩大一圈边框,再随机找位置剪裁,有可能会把填充的边框裁剪进去,也可能不会。
    输出:
    RandomCrop(size=(1600, 1600), padding=300)
    转换后图片形状: torch.Size([3, 2100, 3174])
    转换后图片形状: torch.uint8 
    
    转换后图片形状: torch.Size([3, 1600, 1600])
    转换后图片形状: torch.uint8
    
    随机剪裁,此图把填充的边框也裁剪进去了


    1.3 随机长宽比裁剪: transforms.RandomResizedCrop()

    transforms.RandomResizedCrop(size=900, scale=(0.03, 1.8),
                                 ratio=(0.9999, 1.0), interpolation=2)
    带缩放的裁剪。
    scale表示 size 的缩放倍数,ratio 表示 size 的宽高比倍数,
    所以对于我们想要的 size ,首先会被缩放和改变宽高比,然后那得到的size去裁剪原图,
    最后把我们裁剪的图片再还原回 输入的 size 大小。
    这样就实现了我们带缩放和宽高比变换的裁剪。
    
    只设置了缩放,未设置宽高比的裁剪。 可以看出原图被缩小了
    同时设置了缩放、宽高比的裁剪。 可以看出原图被缩小了,并且宽度被压缩


    1.4 上下左右中心裁剪: transforms.FiveCrop(size:int)

    返回一个 `tuple`,(左上裁剪,右上裁剪,左下裁剪,右下裁剪,中间裁剪),
    这个`tuple`包含5个tensor ,每个tensor代表的是对原图进行的截图,
    截图位置就是左上,右上,左下,右下,中间。大小是传入的参数 size
    代码:
    transforms.FiveCrop(1000)
    输出:(以左上为例)
    FiveCrop(size=(1000, 1000))
    转换后图片形状: torch.Size([3, 2100, 3174])
    转换后图片形状: torch.uint8 
    
    转换后图片形状: torch.Size([3, 1000, 1000])
    转换后图片形状: torch.uint8
    
    FiveCrop左上角图片

    1.5 上下左右中心裁剪后翻转: transforms.TenCrop()
    与 FiveCrop() 类似,只不过它返回的是10元元组,这多出来的后 5 个元素是前五个的水平翻转罢了。
    
    带水平翻转

    2 翻转和旋转 Flip and Rotation


    2.1 依概率 p 水平翻转: transforms.RandomHorizontalFlip(p=0.5)
    平平无奇的水平翻转,并非一定会翻转,而是按照 p=0.5 的概率进行,可以设置为1 ,让其一定翻转。(下同)

    翻转了


    2.2 依概率 p 垂直翻转: transforms.RandomVerticalFlip(p=0.5)

    翻转了


    2.3 随机旋转: transforms.RandomRotation(degrees=90, resample=False, expand=False, center=None)

    平平无奇的图片旋转操作,这里需要注意一下,
    比如我们想旋转完,图片尺寸不变,就需要把 expand 设置为 False,
    这会自动截掉那些超出边框的部分;
    如果设置为True ,就会自动扩展图像边框,让图像完整地保留下来。
    degrees 不是设置的旋转角度,
    而是会在 [-degrees, +degrees] 间随机选角度。
    这个直接就是角度值,不是弧度制。
    center 是旋转中心
    
    未 expand
    设置了 expand

    3 图像变换


    3.1 resize: transforms.Resize(size=900)

    如果 size 只是一个 int ,那就会在保持长宽比的情况下,把短边缩放到指定 size ,
    长边按照长宽比缩放
    
    缩放效果


    3.2 标准化: transforms.Normalize(mean=[], var=[])

    mean、var 都是三元序列,分别对应三个通道的均值方差
    注意:此时输入的图像应是 0-1 之间的 float,而不是0-255的 int8 类型
    
    标准化的图片


    3.3 转为 tensor,并归一化至[0-1]: transforms.ToTensor

    这个函数的输入对象主要是 numpy.ndarray 以及 PIL.image 类型的图片。
    这方法是为了把其他库读入的图片转换成 pytorch 统一的图片 Tensor 形式。即:
    1. shape 为 [B, C, H, W]
    2. dtype 为 torch.float
    


    3.4 填充: transforms.Pad(padding, fill=0, padding_mode='constant')

    padding_mode='constant'模式下:
    padding:在哪些边填充多少像素。三种情况:
    1.单独一个 int ,表示图像四边都要填充 int 个像素值为 fill 的像素。
    2.一个长度为 2 的元组或数组 list。
    那么 list[0] 表示在图像左右填充list[0]个像素值为 fill 或者 fill[0] 的像素。
    3.一个长度为 4 的元组或数组 list。
    那么 list[0] 表示在图像左填充list[0]个像素值为 fill 或者 fill[0] 的像素; 
    list[1] 表示在图像左填充list[1]个像素值为 fill 或者 fill[1] 的像素;
    list[2] 表示在图像左填充list[2]个像素值为 fill 或者 fill[2] 的像素; 
    list[4] 表示在图像左填充list[4]个像素值为 fill 或者 fill[4] 的像素。
    padding_mode 还可以选:
    edge: pads with the last value at the edge of the image
    reflect: pads with reflection of image without repeating the last value on the edge。
    For example, padding [1, 2, 3, 4] with 2 elements on both sides 
    in reflect mode will result in [3, 2, 1, 2, 3, 4, 3, 2]
    以上两种模式下不需要设置fill。
    *注意:文档中说 fill 可以是个 3元 tuple ,
    然后就能分别给 RGB 三个通道添加不同灰度的边框,实现彩色边框,
    可我试了不行。*
    
    用 reflect 模式做的 pad


    3.5 修改亮度、对比度和饱和度

    transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)
    随机修改亮度、对比度、饱和度和色调,
    其随机范围是 【max(0,1-brightness), 1+brightness】,
    brightness,contrast,saturation都是可以【0,∞】
    hue比较特殊,只能在 【0,0.5】之间取值
    
    各类都修改了一下


    3.6 转灰度图: transforms.Grayscale(num_output_channels=1)

    简单地变换成灰度图,输出通道数可以是1或者3
    输出通道选择3的话,R=G=B
    
    灰度图


    3.7 线性变换: transforms.LinearTransformation()
    这个好麻烦,我不会,自己看文档

    3.8 仿射变换: transforms.RandomAffine

    transforms.RandomAffine(degrees=10, translate=None, scale=None, shear=(20, 30), resample=0, fillcolor=0)
    这个函数的 shear 就是剪切,比较重要
    translate是平移
    degrees是旋转
    这是个综合函数,可以实现很多功能,可以让其他为默认值,只用其中一种
    
    仿射变换


    3.9 随机透视: transforms.RandomPerspective()

    transforms.RandomPerspective(distortion_scale=0.5, p=0.5, interpolation=2, fill=0)
    随机按照概率 p 进行程度为 distortion_scale 的透视变换
    
    透视变换


    3.10 依概率 p 转为灰度图: transforms.RandomGrayscale
    就是不一定会转变成灰度图,按照某种概率搞

    3.11 将数据转换为 PILImage: transforms.ToPILImage
    转成 PILImage ,我用不到先不写了

    3.12 transforms.Lambda: Apply a user-defined lambda as a transform.
    这个不会哦。

    相关文章

      网友评论

        本文标题:Pytorch的第一步:(2) transforms 的使用

        本文链接:https://www.haomeiwen.com/subject/xcuigktx.html