在我的前一篇文章:Pytorch的第一步:(1) Dataset类的使用 里,不论是使用 torchvision.datasets 还是我们自定义了 Dataset 子类,都有一个形参 transforms 被传入。上篇文章我没有详细讲解,是因为这是一块很大的内容,故专门写本文讲解。
transforms 是图像处理函数,主要用于对索引出来的图片进行 剪切、翻转、平移、仿射等操作,也就是得到我们想要的预处理过程。
pytorch 提供的 torchvision.transforms 模块是专门用来进行图像预处理的,本文按照处理方式的不同,分组介绍和试验这些预处理方法
注意点
- transforms.Compose() 可以把多类转换操作结合起来
- 可转换的图像包括
PIL Image
,Tensor Image
或batch 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])
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
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 是旋转中心
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 三个通道添加不同灰度的边框,实现彩色边框,
可我试了不行。*
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.
这个不会哦。