本文相关代码下载:https://github.com/hanlinshi/test_TORCHVISION.TRANSFORMS
TORCHVISION.TRANSFORMS变换是常见的图像变换。它们可以用Compose
链接在一起。此外,还有torchvision.transforms.functional
模块。函数转换提供对转换的细粒度控制。如果您必须构建一个更复杂的转换管道(例如,在分段任务的情况下),这很有用。
所有变换都接受PIL图像、Tensor图像或一批Tensor图像作为输入。Tensor图像是一种(C,H,W)形状的tensor,其中C是多个通道,H和W是图像的高度和宽度。一批Tensor图像是(B,C,H,W)形状的tensor,其中B是批中的若干图像。对一批Tensor图像应用的确定性或随机变换对该批的所有图像进行相同的变换。
从v0.8.0版后,所有随机转换都使用torch默认随机生成器来采样随机参数。这是一个破坏向后兼容性的更改,用户应将随机状态设置为:
# Previous versions # import random # random.seed(12) # Now import torch torch.manual_seed(17)
请记住,torch随机生成器和Python随机生成器的相同种子不会产生相同的结果。
可脚本化转换
为了编写转换脚本,请使用torch.nn.Sequential,而不是Compose。
transforms = torch.nn.Sequential(
transforms.CenterCrop(10),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
)
scripted_transforms = torch.jit.script(transforms)
确保只使用可编写脚本转换,即torch.Tensor,不需要lambda函数PIL.Image
任何自定义转换要与torch.jit.script一起使用,它们应该源于torch.nn.模块.
torchvision.transforms.Compose(transforms)
主要作用是串联多个图片变换的操作。
transforms
是一个Transform对象列表,Transform对象对图像进行操作,如裁剪、翻转等变换操作。
Compose
会将transforms列表遍历操作一次。
transforms.Compose([
transforms.CenterCrop(10),
transforms.ToTensor(),
])
CLASS torchvision.transforms.CenterCrop(size)
在中心位置裁剪给定图像。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数.
参数
- size(sequence 或 int)---- 裁剪后的大小。如果size是一个int而不是像(h,w)这样的序列,则会进行一个方形裁剪(size,size)。如果提供一个长度为1的元组size=(h,)或列表size=[h],它将被解释为(size[0],size[0])
–也就是四种写法: size=(h,w) ; size=h ; size=(h,) ; size=[h].
返回
剪切后的图像
返回类型:PIL 图像或Tensor
下图是四种写法中心裁剪后的图片
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/img.jpeg') # 原图大小695,391
sizes = [224, (224, 224), (224,), [224]]
for i, size in enumerate(sizes):
transform = transforms.CenterCrop(size)
imgt = transform(img)
imgt.save('./data/center_crop_' + str(i) + '.jpg')
CLASS torchvision.transforms.RandomCrop(size, padding=None, pad_if_needed=False, fill=0, padding_mode='constant')
在随机位置裁剪给定图像。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- size(sequence 或 int)---- 裁剪后的大小。如果size是一个int而不是像(h,w)这样的序列,则会进行一个方形裁剪(size,size)。如果提供一个长度为1的元组size=(h,)或列表size=[h],它将被解释为(size[0],size[0]).
– 也就是四种写法: size=(h,w) ; size=h ; size=(h,); size=[h].- padding(int 或 sequence,可选)---- 图像每个边填充长度(单位是像素)。 默认值为“无”。如果提供了一个int,则用于填充所有边界。如果提供长度为2的元组,则分别在左/右和上/下填充。如果提供了长度为4的元组,则这是分别用于左边框、上边框、右边框和下边框的填充。在torchscript模式下,由于不支持单个int,请使用长度为1的元组或列表:[padding,]
– 也就是七种写法: padding=p; padding=(p,); padding=(p1,p2); padding=(p1,p2,p3,p4); padding=[p]; padding=[p1,p2]; padding=[p1,p2,p3,p4]
– 这里的p是指上下左右边界用fill的值填充p个像素的长度。- pad_if_needed(boolean)---- 如果图像小于所需大小,它将填充图像,以避免引发异常。由于裁剪是在填充之后完成的,所以填充看起来像是在随机偏移处完成的。
– 如果pad_if_needed为False(默认值), 图像padding后的大小 < size,就会报错;如果pad_if_needed为True,图像padding后的大小 < size,则会继续padding到size大小后,再裁剪,就不会报错了。- fill (int 或 tuple) ---- 常量填充的像素填充值。 默认值为0。如果一个元组的长度为3,则分别用于填充R、G、B通道。此值仅在填充模式为常量时使用.
– 也就是两种写法: fill=f; fill=(f1,f2,f3).- padding_mode (str) ---- 填充类型. 应为:constant常数、edge边、reflect反射或symmetric对称。默认值为常量。张量输入尚不支持模式对称。
- constant 常数,用常数值进行填充,这个常数值由fill指定。
- edge 边, 用图像最边缘的值进行填充。
- reflect 反射,用图像的反射进行填充(不重复最边缘的值),例如,使用反射模型在[1, 2, 3, 4]的两边填充2个元素的结果为[3, 2
, 1, 2, 3, 4,3, 2
]
- symmetric 对称,用图像的对称进行填充(重复最边缘的值),例如,使用对称模型在[1, 2, 3, 4]的两边填充2个元素的结果为[2, 1
, 1, 2, 3, 4,4, 3
]
– 如果使用了edge,reflect,symmetric这三种模式,fill的值就会被无视
– 从结果上看reflect和symmetric这两种模式没有什么大的区别,不过我私以为这可能是设计上的问题,应该有一种模式是重复,而不是镜像方式。(不过也可能是我的实验有问题)
– 图像padding后的大小 < size时,选择pad_if_needed为True和自己padding图像得到的结果还是有区别的,没有去研究系统是怎么自动padding的。感兴趣的可以研究试一试。
返回
剪切后的图像
返回类型:PIL 图像或Tensor
下图是随机裁剪四种size写法得到图片,注意因为随机所以每次得到的图片都可能不一样。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/img.jpeg') # 原图大小695,391
sizes = [224, (224, 224), (224,) ,[224]]
for i, size in enumerate(sizes):
transform = transforms.RandomCrop(size)
imgt = transform(img)
imgt.save('./data/random_crop_s' +str(i)+ '.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/img.jpeg') # 原图大小695,391
size = 400
paddings = [50,(50,),(50, 100),(50, 100, 150, 200),
[50],[50, 100],[50, 100, 150, 200]]
# padding = 4 (pad_if_needed=False会报错,True不会);
for i, padding in enumerate(paddings):
transform = transforms.RandomCrop(size,
padding=padding,pad_if_needed=False)
imgt = transform(img)
imgt.save('./data/random_crop_p' + str(i)+'.jpeg')
下图是指定颜色填充裁剪,为了显示白色填充,所以背景颜色涂成黑色。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/img.jpeg') # 原图大小695,391
size = 500
padding = 100
fills = [255,(0,255,255)]
for i, fill in enumerate(fills):
transform = transforms.RandomCrop(size,
padding=padding, fill=fill)
imgt = transform(img)
imgt.save('./data/random_crop_f'+str(i)+'.jpeg')
下图是制定模式填充。看出其实reflect填充和symmetric填充没有什么区别.
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
size = 400
padding = 100
fill = (255,0,255)
padding_modes = ["constant","edge" ,"reflect" ,"symmetric"]
for i, padding_mode in enumerate(padding_modes):
transform = transforms.RandomCrop(size, padding=padding,
pad_if_needed=True, padding_mode=padding_mode)
imgt = transform(img)
imgt.save('./data/random_crop_m'+str(i)+'.jpeg')
CLASS torchvision.transforms.RandomSizedCrop(*args, **kwargs)
-注意:此转换被弃用,取而代之的是RandomResizedCrop。
CLASStorchvision.transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(0.75, 1.3333333333333333), interpolation=2)
将给定图像裁剪为随机大小和长宽比。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
先将原始图片重置为原始尺寸的随机倍数(默认值:从0.08到1.0)以及随机调整长宽比(默认值:从3/4到4/3),然后进行裁剪。最终被调整到给定的大小。这通常用于训练初始网络。
参数
- size(sequence 或 int)---- 裁剪后的大小。如果size是一个int而不是像(h,w)这样的序列,则会进行一个方形裁剪(size,size)。如果提供一个长度为1的元组size=(h,)或列表size=[h],它将被解释为(size[0],size[0])
– 也就是四种写法: size=(h,w) ; size=h ; size=(h,); size=[h].- scale (python:float的元祖) ---- 原始尺寸缩放比率取值。
- ratio (python:float的元祖) ---- 缩放后的长宽比范围取值。
- interpolation (int) ---- 由
filters
(过滤器)定义的所需插值枚举。
默认为PIL.Image.BILINEAR. (双线性插值)。如果输入是Tensor对象,仅支持PIL.Image.NEAREST, PIL.Image.BILINEAR 以及 PIL.Image.BICUBIC
– 取值0到5六个分别是(实验结果猜测的,以后有时间再研究这几个插值):PIL.Image.NEAREST
(0), PIL.Image.ANTIALIAS
(1), PIL.Image.BILINEAR
(2), PIL.Image.BICUBIC
(3), PIL.Image.BOX
(4), PIL.Image.HAMMING
(5)
返回
随机剪裁和重置大小的图像
返回类型:PIL图像或Tenso
默认模式的随机缩放裁剪。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
sizes = [224,(224,),[224]]
interpolation = 0
for i,size in enumerate(sizes):
transform = transforms.RandomResizedCrop(size,
interpolation=interpolation)
imgt = transform(img)
imgt.save('./data/random_resize_crop_s'+str(i)+'.jpeg')
下图是扩大一倍的填充,看不出什么,其实还是有差别的。因为这里缩小了所以看不出来。可以自己实验。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
size = 512
interpolations = [0, 1, 2, 3, 4, 5]
scale = (2.0, 2.0)
ratio = (1.0, 1.0)
for i,interpolation in enumerate(interpolations):
transform = transforms.RandomResizedCrop(size, scale=scale,
ratio=ratio, interpolation=interpolation)
imgt = transform(img)
imgt.save('./data/random_resize_crop_i'+str(i)+'.jpeg')
CLASS torchvision.transforms.FiveCrop(size)
给定图像的四个角和中心裁剪。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- size(sequence 或 int)---- 裁剪后的大小。如果size是一个int而不是像(h,w)这样的序列,则会进行一个方形裁剪(size,size)。如果提供一个长度为1的元组size=(h,)或列表size=[h],它将被解释为(size[0],size[0])
– 也就是四种写法: size=(h,w) ; size=h ; size=(h,); size=[h].
返回
五张裁剪的图像
返回类型:PIL图像或Tenso
这个转换返回的是一组图片的元组(四个角和中心共五张图片)。因此经过这个转换后所得到的数据作为输入数据,和直接由数据集的返回得到的数据量不一样。假设从数据集中拿出batch数为8,经过这个变换以后数据量就为8*5=40了。
请参阅下面的示例,了解如何处理此问题。
transform = Compose([
FiveCrop(size), # 得到一个PIL图片数组
Lambda(lambda crops: torch.stack([ToTensor()(crop) for crop in crops])) # 返回一个4D tensor
])
input, target = batch # input 是一个 5d tensor, target 是 2d
bs, ncrops, c, h, w = input.size()
result = model(input.view(-1, c, h, w)) # 融合 batch size 和 ncrops 变成4d
result_avg = result.view(bs, ncrops, -1).mean(1) # 拆结果
下图是一张图一次
操作得到的五张结果。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
size = 100
transform = transforms.FiveCrop(size)
imgt = transform(img)
for i, imgt in enumerate(imgt):
imgt.save('./data/five_crop_'+str(i)+'.jpeg')
CLASS torchvision.transforms.TenCrop(size, vertical_flip=False)
给定图像的四个角和中心裁剪,以及这些裁剪的翻转(默认情况下使用水平翻转)图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- size(sequence 或 int)---- 裁剪后的大小。如果size是一个int而不是像(h,w)这样的序列,则会进行一个方形裁剪(size,size)。如果提供一个长度为1的元组size=(h,)或列表size=[h],它将被解释为(size[0],size[0])
– 也就是四种写法: size=(h,w) ; size=h ; size=(h,); size=[h].- vertical_flip (bool) ---- 使用垂直翻转代替水平翻转,默认False,不代替。
返回
十张裁剪的图像
返回类型:PIL图像或Tenso
这个转换返回的是一组图片的元组(四个角和中心以及他们的翻转共十张图片)。因此经过这个转换后所得到的数据作为输入数据,和直接由数据集的返回得到的数据量不一样。假设从数据集中拿出batch数为8,经过这个变换以后数据量就为8*10=80了。
请参阅下面的示例,了解如何处理此问题。
transform = Compose([
TenCrop(size), # 得到一个PIL图片数组
Lambda(lambda crops: torch.stack([ToTensor()(crop) for crop in crops])) # 返回一个4D tensor
])
input, target = batch # input 是一个 5d tensor, target 是 2d
bs, ncrops, c, h, w = input.size()
result = model(input.view(-1, c, h, w)) # 融合 batch size 和 ncrops 变成4d
result_avg = result.view(bs, ncrops, -1).mean(1) # 拆结果
下图是一张图一次
操作得到的十张结果。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
size = 100
transform = transforms.TenCrop(size)
imgts = transform(img)
for i, imgt in enumerate(imgts):
imgt.save('./data/ten_crop_'+str(i)+'.jpeg'
CLASS torchvision.transforms.RandomHorizontalFlip(p=0.5)
以给定的概率p, 水平翻转给定的图像。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- p (float) –图像被翻转的概率。默认值为0.5
–实验结论:p小于0的被视为0,p大于1的被视为1;
返回
水平翻转后的图像
返回类型:PIL图像或Tenso
下图左边是原图,右边是水平翻转后的原图
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
transform = transforms.RandomHorizontalFlip(p=1)
imgt = transform(img)
imgt.save('./data/horizontal_flip.jpeg')
CLASS torchvision.transforms.RandomVerticalFlip(p=0.5)
以给定的概率p, 垂直翻转给定的图像。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- p (float) ---- 图像被翻转的概率。默认值为0.5
–实验结论:p小于0的被视为0,p大于1的被视为1;
返回
垂直翻转后的图像
返回类型:PIL图像或Tenso
下图左边是原图,右边是垂直翻转后的原图
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
transform = transforms.RandomVerticalFlip(p=1)
imgt = transform(img)
imgt.save('./data/vertical_flip.jpeg')
CLASS torchvision.transforms.RandomRotation(degrees, resample=0, expand=False, center=None, fill=None)
按角度旋转图像。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- degrees (sequence or float or int) –旋转角度的范围。如果角度是一个数字而不是类似(min,max)的序列,则度数的范围将是(-degrees,+degrees)
–两种写法:degrees=d; degrees=(min,max);- resample (int, 可选) ---- 重采样过滤器(可选项)。默认值是0,更多过滤器的详细见链接。如果省略,或者图像具有模式“1”或“P”(
这里不懂
),则将其设置为PIL.Image.NEAREST.如果输入是Tensor对象,仅支持PIL.Image.NEAREST, PIL.Image.BILINEAR以及PIL.Image.BICUBIC。
–取值0到5六个分别是(实验结果猜测的,以后有时间再研究这几个插值):PIL.Image.NEAREST
(0), PIL.Image.ANTIALIAS
(1), PIL.Image.BILINEAR
(2), PIL.Image.BICUBIC
(3), PIL.Image.BOX
(4), PIL.Image.HAMMING
(5)
– 貌似只支持0,2,3三种过滤器。- expand (bool, 可选) ---- 扩展标志(可选项)。默认值是false.如果为true,则展开输出以使其足够大以容纳整个旋转图像。如果为false或省略,则使输出图像与输入图像的大小相同。请注意,expand标志假定围绕中心旋转而不平移。
- center (list or tuple, 可选) ---- 旋转中心(可选项),(x,y)。原点在左上角。默认值为图像的中心。
–两种写法:center=[x,y]; center=(x,y)- fill (3-tuple or int) ---- 旋转图像外部区域的像素填充值。如果是int或float,则该值用于所有外部区域。默认填充值为0。此选项仅适用于Pillow>=5.2.0. Tensor输入不支持此选项。因此Tensor输出图像中变换外部区域的填充值始终为0。
– 两种写法:fill=x; fill=(x,y,z)
返回
随机旋转后的图像
返回类型:PIL图像或Tenso
下图是随机随机旋转
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
degrees = 30
for i in range(30):
transform = transforms.RandomRotation(degrees=degrees)
imgt = transform(img)
imgt.save('./data/random_rotation_' + str(i) + ".jpeg")
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
degrees = 30
resamples = [0,2,3]
for i,resample in enumerate(resamples)
transform = transforms.RandomRotation(degrees=degrees,
resample=resample)
imgt = transform(img)
imgt.save('./data/random_rotation_r'+str(i)+'.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
degrees = 45
expands = [True,False]
for i, expand in enumerate(expands):
transform = transforms.RandomRotation(degrees=degrees,
expand=expand)
imgt = transform(img)
imgt.save('./data/random_rotation_e'+str(i)+'.jpeg')
下图是是Center设置为(10,10)的时候,左边是expand为True,右边是expand为False。因为截的是缩略图,所以看起来都一样大,其实左边的图片每一张大小都不一样。而右边的每一张都和原图一样大。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
degrees = 45
expands = [True,False]
center = (10,10)
for j,expand in enumerate(expands):
for i in range(9):
transform = transforms.RandomRotation(degrees=degrees,
expand=expand, center=center)
imgt = transform(img)
imgt.save('./data/random_rotation_c' +str(j)+'_'+ str(i)+'.jpeg')
下图是fill填写不一样的值。左边fill=123,右边fill=(0,255,255)
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 图片大小是 256*256
degrees = 34.5
fills = [123,(0,255,255)]
center = (100,100)
for i, fill in enumerate(fills):
transform = transforms.RandomRotation(degrees=degrees,
fill=fill,center=center)
imgt = transform(img)
imgt.save('./data/random_rotation_f'+str(i)+'.jpeg')
CLASS torchvision.transforms.Resize(size, interpolation=2)
重置给定图片的大小。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- size (sequence or int) ----所需的输出大小。如果size是一个类似(h,w)的序列,则输出大小将与此匹配。如果size为int,则图像的较小边将与该数字匹配。例如,如果height>width,则图像将被重新缩放为(size*height/width,size)。在torchscript模式下,由于不支持单个int填充,请使用长度为1的元组或列表:[size,]。
– 两种写法:size=s; size=(h,w)- interpolation (int) ---- 由
filters
(过滤器)定义的所需插值枚举。
默认为PIL.Image.BILINEAR. (双线性插值)。如果输入是Tensor对象,仅支持PIL.Image.NEAREST, PIL.Image.BILINEAR 以及 PIL.Image.BICUBIC
– 取值0到5六个分别是(实验结果猜测的,以后有时间再研究这几个插值):PIL.Image.NEAREST
(0), PIL.Image.ANTIALIAS
(1), PIL.Image.BILINEAR
(2), PIL.Image.BICUBIC
(3), PIL.Image.BOX
(4), PIL.Image.HAMMING
(5)
返回
重置大小后的图像
返回类型:PIL图像或Tenso
下图是重置大小
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/img.jpeg') # 原图大小695,391
sizes = [150,(150, 150)]
for i, size in enumerate(sizes):
transform = transforms.Resize(size)
imgt = transform(img)
imgt.save('./data/resize_s'+str(i)+'.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/img.jpeg') # 原图大小695,391
size = (150, 150)
interpolations = [0,1,2,3,4,5]
for i, interpolation in enumerate(interpolations):
transform = transforms.Resize(size=size,
interpolation=interpolation)
imgt = transform(img)
imgt.save('./data/resize_i'+str(i)+'.jpeg')
CLASS torchvision.transforms.Scale(*args, **kwargs)
-注意:此转换被弃用,取而代之的是Resize。
CLASS torchvision.transforms.Pad(padding, fill=0, padding_mode='constant')
用给定的填充值填充给定图像的边缘。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数。
参数
- padding(int 或 sequence,可选)---- 图像每个边填充长度(单位是像素)。 默认值为“无”。如果提供了一个int,则用于填充所有边界。如果提供长度为2的元组,则分别在左/右和上/下填充。如果提供了长度为4的元组,则这是分别用于左边框、上边框、右边框和下边框的填充。在torchscript模式下,由于不支持单个int,请使用长度为1的元组或列表:[padding,]
– 也就是七种写法: padding=p; padding=(p,); padding=(p1,p2); padding=(p1,p2,p3,p4); padding=[p]; padding=[p1,p2]; padding=[p1,p2,p3,p4]
– 这里的p是指上下左右边界用fill的值填充p个像素的长度。- fill (int 或 tuple) ---- 常量填充的像素填充值。 默认值为0。如果一个元组的长度为3,则分别用于填充R、G、B通道。此值仅在填充模式为常量时使用.
– 也就是两种写法: fill=f; fill=(f1,f2,f3).- padding_mode (str) ---- 填充类型. 应为:constant常数、edge边、reflect反射或symmetric对称。默认值为常量。张量输入尚不支持模式对称。
- constant 常数,用常数值进行填充,这个常数值由fill指定。
- edge 边, 用图像最边缘的值进行填充。
- reflect 反射,用图像的反射进行填充(不重复最边缘的值),例如,使用反射模型在[1, 2, 3, 4]的两边填充2个元素的结果为[3, 2
, 1, 2, 3, 4,3, 2
]
- symmetric 对称,用图像的对称进行填充(重复最边缘的值),例如,使用对称模型在[1, 2, 3, 4]的两边填充2个元素的结果为[2, 1
, 1, 2, 3, 4,4, 3
]
– 如果使用了edge,reflect,symmetric这三种模式,fill的值就会被无视
– 从结果上看reflect和symmetric这两种模式没有什么大的区别,不过我私以为这可能是设计上的问题,应该有一种模式是重复,而不是镜像方式。(不过也可能是我的实验有问题)
– 图像padding后的大小 < size时,选择pad_if_needed为True和自己padding图像得到的结果还是有区别的,没有去研究系统是怎么自动padding的。感兴趣的可以研究试一试。
返回
填充后的图像
返回类型:PIL图像或Tenso
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
paddings = [50,(50,),(50, 100),(50, 100, 150, 200),
[50], [50, 100], [50, 100, 150, 200]]
for i, padding in enumerate(paddings):
transform = transforms.Pad(padding=padding)
imgt = transform(img)
imgt.save('./data/pad_' + str(i) + '.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
padding = 50
fills = [255, (0,255,255)]
for i, fill in enumerate(fills):
transform = transforms.Pad(padding=padding, fill=fill)
imgt = transform(img)
imgt.save('./data/pad_f' + str(i) + '.jpeg')
reflect 和 symmetric还是有点不同的,因为是缩略图所以看不出来。自己运行代码后,通过快速切换两张图片,在动态视觉上可以比较明显的看出不同。
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
padding = 256*2
fill = (255,0,255)
padding_modes = ["constant", "edge", "reflect", "symmetric"]
for i, padding_mode in enumerate(padding_modes):
transform = transforms.Pad(padding=padding, fill=fill,padding_mode=padding_mode)
imgt = transform(img)
imgt.save('./data/pad_m' + str(i) + '.jpeg')
CLASS torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)
随机改变图像的亮度、对比度和饱和度。
参数
- brightness(float or tuple of python:float (min, max)) ---- 亮度的偏差程度。亮度系数从[max(0,1-brightness),1+brightness]或给定的[min,max]中随机的选择某个值。应是非负数。
- contrast(float or tuple of python:float (min, max)) ---- 对比度的偏差程度。。对比度系数从[max(0,1-contrast),1+contrast]或给定的[min,max]中随机的选择某个值。应是非负数。
- saturation(float or tuple of python:float (min, max)) ---- 饱和的偏差程度。饱和系数从[max(0,1-saturation),1+saturation]或给定的[min,max]中随机的选择某个值。应是非负数。
- hue(float or tuple of python:float (min, max)) ---- 色调的偏差程度。色调系数从[-hue,hue]或给定的[min,max]随机的选择某个值。应具有0<=hue<=0.5或-0.5<=min<=max<=0.5。
返回
色彩变换后的图像
返回类型:PIL图像或Tenso
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
transform = transforms.ColorJitter(brightness=2,
contrast=0, saturation=0, hue=0)
for i in range(6):
imgt = transform(img)
imgt.save('./data/colorjitter_b'+str(i)+'.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
transform = transforms.ColorJitter(brightness=0,
contrast=2, saturation=0, hue=0)
for i in range(6):
imgt = transform(img)
imgt.save('./data/colorjitter_c'+str(i)+'.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
transform = transforms.ColorJitter(brightness=0,
contrast=0, saturation=2, hue=0)
for i in range(6):
imgt = transform(img)
imgt.save('./data/colorjitter_s'+str(i)+'.jpeg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg')
transform = transforms.ColorJitter(brightness=0, contrast=0,
saturation=0, hue=0.5)
for i in range(6):
imgt = transform(img)
imgt.save('./data/colorjitter_h'+str(i)+'.jpeg')
CLASS torchvision.transforms.RandomAffine(degrees, translate=None, scale=None, shear=None, resample=0, fillcolor=0)
图像保持中心不变的随机仿射变换。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数.
参数
- degrees (sequence or float or int) ---- 旋转,角度选择的范围。如果度数是一个数字而不是类似(min,max)的序列,则度数的范围是(-degrees,+degrees)。设置为0可禁用旋转。
- translate (tuple, 可选项) ---- 平移,水平和垂直平移的最大绝对分数元组。例如translate=(a,b),a,b取值范围[0,1].则水平偏移在范围 - img_width * a
- scale (tuple, 可选项) ---- 缩放,比例系数选择区间,例如(a,b)a<=b,且取值范围(0,正无穷),那么比例从a<=scale<=b的范围内随机抽样,默认情况下保持原始比例。
- shear (sequence or float or int, optional) ---- 对角轴翻转的选择范围。如果“shear”是一个数字,则将进行与可与x轴平行的剪切的对角翻转,范围为(-shear,+shear)。如果shear”是一个2个值的元组或列表,则将进行与可与x轴平行的剪切的对角翻转,范围在(shear[0],shear[1])。如果剪切是一个4个值的元组或列表,则将在范围(shear[0],shear[1])进行x轴剪切以及在范围(shear[2],shear[3])进行y轴剪切的对角翻转。默认情况下不会应用的对角翻转。
- resample (int, 可选项) ---- 重采样过滤器(可选项)。缩放时候采用的算法。更多过滤器的详细见链接。如果省略,或者图像具有模式“1”或“P”(
这里不懂
),则将其设置为PIL.Image.NEAREST.如果输入是Tensor对象,仅支持PIL.Image.NEAREST, PIL.Image.BILINEAR以及PIL.Image.BICUBIC。
–取值0到5六个分别是(实验结果猜测的,以后有时间再研究这几个插值):PIL.Image.NEAREST
(0), PIL.Image.ANTIALIAS
(1), PIL.Image.BILINEAR
(2), PIL.Image.BICUBIC
(3), PIL.Image.BOX
(4), PIL.Image.HAMMING
(5)- fillcolor (tuple or int) ---- 变换图像输出中外部区域的可选填充颜色(RGB图像为元组,灰度为int)(Pillow>=5.0.0)。此输入不支持Tensor。输出的变换图像中外部区域的填充值始终为0。旋转,翻转,平移,缩小时候要填充的颜色
返回
仿射变换后的图像
返回类型:PIL图像或Tenso
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
degreeses = [0, 30, 400, (-30, 60)]
for i, degrees in enumerate(degreeses):
transform = transforms.RandomAffine(degrees)
imgt = transform(img)
imgt.save('./data/random_affine_d' + str(i) + '.jpg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
degrees = 0
translates = [(0,0.5),(0.5,0),(1,1)]
for i, translate in enumerate(translates):
transform = transforms.RandomAffine(degrees, translate=translate)
imgt = transform(img)
imgt.save('./data/random_affine_t' + str(i) + '.jpg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
degrees = 0
scales = [(0.1,0.2),(1,100),(1,1)]
for i, scale in enumerate(scales):
transform = transforms.RandomAffine(degrees, scale=scale)
imgt = transform(img)
imgt.save('./data/random_affine_sc' + str(i) + '.jpg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
degrees = 0
shears = [30,[-30,30],[0,10,20,30]]
for i, shear in enumerate(shears):
transform = transforms.RandomAffine(degrees, shear=shear)
imgt = transform(img)
imgt.save('./data/random_affine_sh' + str(i) + '.jpg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
degrees = 0
scale=(3,3)
resamples = [0,2,3]
for i, resample in enumerate(resamples):
transform = transforms.RandomAffine(degrees,scale=scale,
resample=resample)
imgt = transform(img)
imgt.save('./data/random_affine_r' + str(i) + '.jpg')
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
degrees = 0
translate=(0.5,0.5)
fillcolors = [122,(0,234,121)]
for i, fillcolor in enumerate(fillcolors):
transform = transforms.RandomAffine(degrees,
translate=translate, fillcolor=fillcolor)
imgt = transform(img)
imgt.save('./data/random_affine_f' + str(i) + '.jpg')
degrees = 0
scale=(0.5,0.5)
fillcolors = [122,(0,234,121)]
for i, fillcolor in enumerate(fillcolors):
transform = transforms.RandomAffine(degrees,scale=scale,
fillcolor=fillcolor)
imgt = transform(img)
imgt.save('./data/random_affine_f' + str(i+2) + '.jpg')
CLASS torchvision.transforms.Grayscale(num_output_channels=1)
把图像转成灰度图。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,3,H,W]形状,其中…表示任意数量的前导维数
参数
- num_output_channels (int) ----(1或3)输出图像所需的通道数
返回
输入的灰度版本。
- 如果num_output_channels==1:返回的图像为单通道
- 如果num_output_channels==3:返回的图像是3个通道,r == g == b
返回类型:PIL图像或Tensor
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
num_output_channelses =[1,3]
for i, num_output_channels in enumerate(num_output_channelses):
transform = transforms.Grayscale(num_output_channels)
imgt = transform(img)
imgt.save('./data/grayscale_' + str(i) + '.jpg')
CLASS torchvision.transforms.RandomGrayscale(p=0.1)
以概率为p(默认值为0.1),随机将图像转换为灰度。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,3,H,W]形状,其中…表示任意数量的前导维数.
参数
- p (float) ---- 图像应转换为灰度的概率。
返回
概率为p,输入图像的灰度版本,概率(1-p)不变。
- 如果输入图像为1通道,灰度版本为1通道。
- 如果输入图像为3通道,灰度版本为3通道,r == g == b
返回类型:PIL图像或Tensor
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
for i in range(6):
transform = transforms.RandomGrayscale(p=0.5)
imgt = transform(img)
imgt.save('./data/random_grayscale_' + str(i) + '.jpg')
CLASS torchvision.transforms.RandomPerspective(distortion_scale=0.5, p=0.5, interpolation=2, fill=0)
以给定的概率对给定图像执行随机透视变换。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,H,W]形状,其中…表示任意数量的前导维数.
参数
- distortion_scale (float) ---- 控制失真程度的参数,范围从0到1。默认值为0.5。
- p(float) ---- 图像被变换的概率。默认值为0.5。
- interpolation (int) ---- 由
filters
(过滤器)定义的所需插值枚举。
默认为PIL.Image.BILINEAR. (双线性插值)。如果输入是Tensor对象,仅支持PIL.Image.NEAREST, PIL.Image.BILINEAR 以及 PIL.Image.BICUBIC- fill (n-tuple or int or float) ---- 旋转图像外部区域的像素填充值。如果是int或float,则该值用于所有外部区域。默认填充值为0。此选项仅适用于Pillow>=5.2.0. Tensor输入不支持此选项。因此Tensor输出图像中变换外部区域的填充值始终为0。
返回
随机变换的图像
返回类型:PIL图像或Tenso
默认值
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
for i in range(6):
transform = transforms.RandomPerspective(distortion_scale=0.5, p=0.5,
interpolation=2, fill=0)
imgt = transform(img)
imgt.save('./data/random_perspective' + str(i) + '.jpg')
不同的插值方式
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
interpolations = [0,2,3]
for i,interpolation in enumerate(interpolations):
transform = transforms.RandomPerspective(distortion_scale=0.5, p=1,
interpolation=interpolation, fill=0)
imgt = transform(img)
imgt.save('./data/random_perspective_i' + str(i) + '.jpg')
CLASS torchvision.transforms.GaussianBlur(kernel_size, sigma=(0.1, 2.0))
使用随机选择的高斯模糊模糊模糊图像。图像可以是PIL图像或Tensor,在这种情况下,它应该具有[…,C,H,W]形状,其中…表示任意数量的前导维数.
参数
- kernel_size (int or sequence) ---- 高斯核的大小。
- sigma(float or tuple of python:float (min, max)) ---- 用于创建内核以执行模糊处理的标准偏差。如果是一个float,sigma是固定的。如果它是float的元组 (min, max),sigma随机地均匀地在给定的范围内选择。
返回
输入图像的高斯模糊版本。
返回类型:PIL图像或Tenso
高斯核的大小
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
kernel_sizes = [1,3,5,7,9,11]
for i, size in enumerate(kernel_sizes):
transform = transforms.GaussianBlur(kernel_size=size,
sigma=(10.0, 10.0))
imgt = transform(img)
imgt.save('./data/gaussian_blur_' + str(i) + '.jpg')
sigma
import torchvision.transforms as transforms
from PIL import Image
img = Image.open('./data/timg.jpeg') # 原图大小256*256
for i in range(6):
transform = transforms.GaussianBlur(kernel_size=11,
sigma=(0.1, 10.0))
imgt = transform(img)
imgt.save('./data/gaussian_blur_s' + str(i) + '.jpg')
CLASS torchvision.transforms.RandomChoice(transforms)
从列表中随机选取的单个变换进行应用。此转换不支持torchscript.
CLASS torchvision.transforms.RandomOrder(transforms)
以随机顺序应用变换列表。此转换不支持torchscript。
CLASS torchvision.transforms.LinearTransformation(transformation_matrix, mean_vector)
用平方变换矩阵和均值向量变换tensor图像。给定变换矩阵和平均向量,将使torch.*Tensor变平并从中减去平均向量,然后用转换矩阵计算点积,然后将tensor重塑为其原始形状。
参数
- transformation_matrix (Tensor) – tensor [D x D], D = C x H x W
- mean_vector (Tensor) – tensor [D], D = C x H x W
返回
变换后的图像。
返回类型:Tensor
应用
白化变换:假设X是列向量零中心数据。然后用torch.mm(X.t(), X),计算数据的阵协方差矩阵 [D x D],再计算该矩阵的SVD ,并将其作为转换矩阵传递。
CLASS torchvision.transforms.Normalize(mean, std, inplace=False)
用均值和标准差归一化张量图像。给定n个通道的平均值:(mean[1],…,mean[n])和std(标准差):(std[1],…,std[n]),此转换将规范化torch.*Tensor的每个通道的输入。即,output[channel] = (input[channel] - mean[channel]) / std[channel]
参数
- mean (sequence) ---- 每个通道的平均值。
- std (sequence) ---- 每个通道的标准差。
- inplace (bool,optional) — 是否覆盖原图片
返回
标准化后的图像。
返回类型:Tensor
*如果有自己的数据集,如何计算mean以及std,可以见脚本:normalize.py
compute_mean_std方法。注意的是opencv 读图的顺序BGR; PIL读图的顺序是RGB。
下图是标准化前后的对比图
下图是inplace取不同的值的图片,可以看出inplace为true替换的是输入图片。如果是false,输入图片不会替换。
输出图片不管是true还false都会改变。
- 标准化是通过特征的平均值和标准差,将特征缩放成一个标准的正态分布,缩放后均值为0,方差为1
CLASS torchvision.transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False)
随机选择图像中的矩形区域并删除其像素。Zhong等人的“随机擦除数据扩充”,见网址:https://arxiv.org/abs/1708.04896
参数
- p ---- 执行随机擦除操作的概率。
- scale ---- 擦除区域相对于输入图像的比例范围。
- ratio — 擦除区域的纵横比范围。
- value---- 删除值。默认值为0。如果是一个整型,它将被用来清除所有像素。如果是长度为3的元组,则分别用于擦除R、G、B通道。如果“random”的str,则用每个像素用随机值擦除。
- inplace — 布尔值是否替换原图。默认设置为False。
返回
擦除后的图像。
返回类型:Tensor
transform = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
transforms.RandomErasing(),
])
CLASS torchvision.transforms.ConvertImageDtype(dtype: torch.dtype)
将tensor图像转换为给定的数据类型并相应地缩放值。
参数
- dtype(torch.dpython:type) ---- 输出的所需数据类型。
从较小的整数数据类型转换为较大的整数数据类型时,最大值不会精确映射。如果来回转换,此不匹配将不起作用。
RuntimeError
–尝试强制将torch.float32转换至torch.int32或者torch.int64以及试图将torch.float64转换成torch.int64. 这些转换可能会导致溢出错误,因为浮点数据类型无法在整数数据类型的整个范围内连续存储。
CLASS torchvision.transforms.ToPILImage(mode=None)
将 tensor或ndarray转换为PIL图像。此转换不支持torchscript。
将形状C x H x W的 torch.*Tensor 或形状H x W x C的numpy ndarray转换为PIL图像,同时保留值范围。
参数
- mode (PIL.Image mode) ---- 输入数据的颜色空间和像素深度(可选)。如果mode为None(默认),则对输入数据进行一些假设:-如果输入有4个通道,则假定模式为RGBA。-如果输入有3个通道,则假定模式为RGB。-如果输入有2个通道,则假定模式为LA。-如果输入有1个通道,则模式由数据类型(即int、float、short)决定。
CLASS torchvision.transforms.ToTensor
将PIL图片或者numpy.ndarray转成tensor。这个转换操作不支持torchscript。
如果PIL图片属于模式 (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)之一,或者如果numpy.ndarray的dtype = np.uint8,那么PIL或者numpy.ndarray在范围[0, 255]的 (H x W x C)将转换为[0.0, 1.0]范围的 (C x H x W)形状的torch.FloatTensor。
在其他情况下,张量返回时不会缩放。
- 如果用opencv读图后,转成了tensor后, 再转回来保存的时候,需要转成numpy格式, 然后放大255倍,再用转置改变形状的顺序才可以保存。
- 如果用PIL也要注意顺序问题。
注意形状顺序和通道顺序是有区别的。ToTensor是做归一化且改变了形状顺序。
CLASS torchvision.transforms.Lambda(lambd)
应用用户定义的lambda作为转换。此转换不支持torchscript。
参数
- lambd (function) ---- 要用于转换的Lambda/函数。
CLASS torchvision.transforms.RandomApply(transforms, p=0.5)
给定概率下随机应用变换列表。
参数
- transforms (list or tuple or torch.nn.Module) ---- 转换的列表。
- p(float) ---- 概率。
为了编写转换脚本,请使用torch.nn.ModuleList作为输入,而不是转换的列表/元组,如下所示:
transforms = transforms.RandomApply(torch.nn.ModuleList([
transforms.ColorJitter(),]), p=0.3)
scripted_transforms = torch.jit.script(transforms)
确保只使用可编写脚本的转换,即torch.Tensor,不需要lambda函数或PIL.Image
略,有时间再补吧