TORCHVISION.TRANSFORMS
一、变换的组合1 变换的组合:pose 二、PIL图象与torch.*Tensor的都可以变换* 裁剪--Crop1 中心裁剪:transforms.CenterCrop2 填充后随机裁剪:transforms.RandomCrop3 (已弃用)缩放后随机裁剪: transforms.RandomSizedCrop4 缩放后随机裁剪: transforms.RandomResizedCrop5 五张裁剪:transforms.FiveCrop6 十张裁剪: transforms.TenCrop * 翻转和旋转 -- Flip and Rotation1 依概率p水平翻转transforms.RandomHorizontalFlip2 依概率p垂直翻转transforms.RandomVerticalFlip3 随机旋转:transforms.RandomRotation * 其他图像变换1 重置大小:transforms.Resize2 (已弃用)重置大小:transforms.Scale3 填充:transforms.Pad4 修改亮度、对比度、饱和度、色调:transforms.ColorJitter5 仿射变换:transforms.RandomAffine6 转灰度图:transforms.Grayscale7 依概率p转为灰度图:transforms.RandomGrayscale8 随机透视变换:transforms.RandomPerspective9 随机高斯模糊:transforms.GaussianBlur 三、只针对PIL图像的变换1 随机选择:transforms.RandomChoice3 随机顺序:transforms.RandomOrder 四、只针对torch.*Tensor的变换1 线性变换:transforms.LinearTransformation2 标准化:transforms.Normalize3 随机区域擦除:transforms.RandomErasing`4 转换数据类型:transforms.ConvertImageDtype 五、数据类型转换变换1 将数据转换为PILImage:transforms.ToPILImage2 将数据转为tensor:transforms.ToTensor 六、通用变换1 Lambda函数:transforms.Lambda2 随机应用变换:transforms.RandomApply 七、变换函数本文相关代码下载:/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)# Nowimport torchtorch.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.模块.
一、变换的组合
1 变换的组合:pose
pose(transforms)
主要作用是串联多个图片变换的操作。
transforms
是一个Transform对象列表,Transform对象对图像进行操作,如裁剪、翻转等变换操作。
Compose
会将transforms列表遍历操作一次。
pose([transforms.CenterCrop(10),transforms.ToTensor(),])
下图是要操作的原图
二、PIL图象与torch.*Tensor的都可以变换
* 裁剪–Crop
1 中心裁剪:transforms.CenterCrop
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 transformsfrom PIL import Imageimg = Image.open('./data/img.jpeg') # 原图大小695,391sizes = [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')
2 填充后随机裁剪:transforms.RandomCrop
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 transformsfrom PIL import Imageimg = Image.open('./data/img.jpeg') # 原图大小695,391sizes = [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 transformsfrom PIL import Imageimg = Image.open('./data/img.jpeg') # 原图大小695,391size = 400paddings = [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 transformsfrom PIL import Imageimg = Image.open('./data/img.jpeg') # 原图大小695,391size = 500padding = 100fills = [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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256 size = 400padding = 100fill = (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')
下图是裁剪出更大的size进行观察
3 (已弃用)缩放后随机裁剪: transforms.RandomSizedCrop
CLASS torchvision.transforms.RandomSizedCrop(*args, **kwargs)
-注意:此转换被弃用,取而代之的是RandomResizedCrop。
4 缩放后随机裁剪: transforms.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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256sizes = [224,(224,),[224]] interpolation = 0for 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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256size = 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')
5 五张裁剪:transforms.FiveCrop
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 是 2dbs, ncrops, c, h, w = input.size()result = model(input.view(-1, c, h, w)) # 融合 batch size 和 ncrops 变成4dresult_avg = result.view(bs, ncrops, -1).mean(1) # 拆结果
下图是一张图一次
操作得到的五张结果。
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256size = 100 transform = transforms.FiveCrop(size)imgt = transform(img)for i, imgt in enumerate(imgt):imgt.save('./data/five_crop_'+str(i)+'.jpeg')
6 十张裁剪: transforms.TenCrop
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 是 2dbs, ncrops, c, h, w = input.size()result = model(input.view(-1, c, h, w)) # 融合 batch size 和 ncrops 变成4dresult_avg = result.view(bs, ncrops, -1).mean(1) # 拆结果
下图是一张图一次
操作得到的十张结果。
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256size = 100 transform = transforms.TenCrop(size)imgts = transform(img)for i, imgt in enumerate(imgts):imgt.save('./data/ten_crop_'+str(i)+'.jpeg'
* 翻转和旋转 – Flip and Rotation
1 依概率p水平翻转transforms.RandomHorizontalFlip
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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256transform = transforms.RandomHorizontalFlip(p=1)imgt = transform(img)imgt.save('./data/horizontal_flip.jpeg')
2 依概率p垂直翻转transforms.RandomVerticalFlip
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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256transform = transforms.RandomVerticalFlip(p=1)imgt = transform(img)imgt.save('./data/vertical_flip.jpeg')
3 随机旋转:transforms.RandomRotation
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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256degrees = 30for i in range(30):transform = transforms.RandomRotation(degrees=degrees)imgt = transform(img)imgt.save('./data/random_rotation_' + str(i) + ".jpeg")
下图是可支持的三种过滤器模式:
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256degrees = 30resamples = [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')
下图c.可以看出为True的时候图像大小变大了。
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256degrees = 45expands = [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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256degrees = 45expands = [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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 图片大小是 256*256degrees = 34.5fills = [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')
* 其他图像变换
1 重置大小:transforms.Resize
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 transformsfrom PIL import Imageimg = Image.open('./data/img.jpeg') # 原图大小695,391sizes = [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 transformsfrom PIL import Imageimg = Image.open('./data/img.jpeg') # 原图大小695,391size = (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')
2 (已弃用)重置大小:transforms.Scale
CLASS torchvision.transforms.Scale(*args, **kwargs)
-注意:此转换被弃用,取而代之的是Resize。
3 填充:transforms.Pad
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的。感兴趣的可以研究试一试。
padding参数返回
填充后的图像
返回类型:PIL图像或Tenso
import torchvision.transforms as transformsfrom PIL import Imageimg = 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')
fill参数
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg')padding = 50fills = [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')
padding_mode参数
reflect 和 symmetric还是有点不同的,因为是缩略图所以看不出来。自己运行代码后,通过快速切换两张图片,在动态视觉上可以比较明显的看出不同。
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg')padding = 256*2fill = (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')
4 修改亮度、对比度、饱和度、色调:transforms.ColorJitter
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。
brightness 亮度返回
色彩变换后的图像
返回类型:PIL图像或Tenso
import torchvision.transforms as transformsfrom PIL import Imageimg = 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')
contrast 对比度
import torchvision.transforms as transformsfrom PIL import Imageimg = 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')
saturation 饱和
import torchvision.transforms as transformsfrom PIL import Imageimg = 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')
hue 色调
import torchvision.transforms as transformsfrom PIL import Imageimg = 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')
5 仿射变换:transforms.RandomAffine
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<dx<img_width * a 范围内随机采样,垂直偏移在范围 - img_height * b<dy<img_height * b范围内随机采样。默认情况下不进行平移。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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256degreeses = [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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256degrees = 0translates = [(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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256degrees = 0scales = [(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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256degrees = 0shears = [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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256degrees = 0scale=(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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256degrees = 0translate=(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')
6 转灰度图:transforms.Grayscale
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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256num_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')
7 依概率p转为灰度图:transforms.RandomGrayscale
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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256for i in range(6):transform = transforms.RandomGrayscale(p=0.5)imgt = transform(img)imgt.save('./data/random_grayscale_' + str(i) + '.jpg')
8 随机透视变换:transforms.RandomPerspective
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.BICUBICfill(n-tuple or int or float) ---- 旋转图像外部区域的像素填充值。如果是int或float,则该值用于所有外部区域。默认填充值为0。此选项仅适用于Pillow>=5.2.0. Tensor输入不支持此选项。因此Tensor输出图像中变换外部区域的填充值始终为0。
返回
随机变换的图像
返回类型:PIL图像或Tenso
默认值
import torchvision.transforms as transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256for 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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256interpolations = [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')
9 随机高斯模糊:transforms.GaussianBlur
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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256kernel_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 transformsfrom PIL import Imageimg = Image.open('./data/timg.jpeg') # 原图大小256*256for 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')
三、只针对PIL图像的变换
1 随机选择:transforms.RandomChoice
CLASS torchvision.transforms.RandomChoice(transforms)
从列表中随机选取的单个变换进行应用。此转换不支持torchscript.
3 随机顺序:transforms.RandomOrder
CLASS torchvision.transforms.RandomOrder(transforms)
以随机顺序应用变换列表。此转换不支持torchscript。
四、只针对torch.*Tensor的变换
1 线性变换:transforms.LinearTransformation
CLASS torchvision.transforms.LinearTransformation(transformation_matrix, mean_vector)
用平方变换矩阵和均值向量变换tensor图像。给定变换矩阵和平均向量,将使torch.*Tensor变平并从中减去平均向量,然后用转换矩阵计算点积,然后将tensor重塑为其原始形状。
参数
transformation_matrix(Tensor) – tensor [D x D], D = C x H x Wmean_vector(Tensor) – tensor [D], D = C x H x W
返回
变换后的图像。
返回类型:Tensor
应用
白化变换:假设X是列向量零中心数据。然后用torch.mm(X.t(), X),计算数据的阵协方差矩阵 [D x D],再计算该矩阵的SVD ,并将其作为转换矩阵传递。
2 标准化:transforms.Normalize
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
3 随机区域擦除:transforms.RandomErasing`
CLASS torchvision.transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False)
随机选择图像中的矩形区域并删除其像素。Zhong等人的“随机擦除数据扩充”,见网址:/abs/1708.04896
参数
p---- 执行随机擦除操作的概率。scale---- 擦除区域相对于输入图像的比例范围。ratio— 擦除区域的纵横比范围。value---- 删除值。默认值为0。如果是一个整型,它将被用来清除所有像素。如果是长度为3的元组,则分别用于擦除R、G、B通道。如果“random”的str,则用每个像素用随机值擦除。inplace— 布尔值是否替换原图。默认设置为False。
返回
擦除后的图像。
返回类型:Tensor
transform = pose([transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),transforms.RandomErasing(),])
4 转换数据类型:transforms.ConvertImageDtype
CLASS torchvision.transforms.ConvertImageDtype(dtype: torch.dtype)
将tensor图像转换为给定的数据类型并相应地缩放值。
参数
dtype(torch.dpython:type) ---- 输出的所需数据类型。
从较小的整数数据类型转换为较大的整数数据类型时,最大值不会精确映射。如果来回转换,此不匹配将不起作用。
RuntimeError
–尝试强制将torch.float32转换至torch.int32或者torch.int64以及试图将torch.float64转换成torch.int64. 这些转换可能会导致溢出错误,因为浮点数据类型无法在整数数据类型的整个范围内连续存储。
五、数据类型转换变换
1 将数据转换为PILImage:transforms.ToPILImage
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)决定。
2 将数据转为tensor:transforms.ToTensor
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是做归一化且改变了形状顺序。
六、通用变换
1 Lambda函数:transforms.Lambda
CLASS torchvision.transforms.Lambda(lambd)
应用用户定义的lambda作为转换。此转换不支持torchscript。
参数
lambd(function) ---- 要用于转换的Lambda/函数。
2 随机应用变换:transforms.RandomApply
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
七、变换函数
略,有时间再补吧
如果觉得《TORCHVISION.TRANSFORMS的图像预处理》对你有帮助,请点赞、收藏,并留下你的观点哦!