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 CenterCroptransforms.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. 当padding
为a
时,左右上下均填充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 RandomCroptransforms.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=Truetransforms.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)步骤:
随机确定scale
和ratio
,然后对原始图片进行选取,再将选取的片段缩放到size
大小;
(4)代码示例:
train_transform = transforms.Compose([transforms.Resize((224, 224)), #图片统一缩放到244*244# 3 RandomResizedCroptransforms.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)功能:在图像的左上、右上、左下、右下、中心随机剪裁出尺寸为size
的5
张图片;
(2)参数:
size:
裁剪图片尺寸,若为int
,则尺寸为size*size
; 若为(h,w)
,则尺寸为h*w
;
(3)代码示例:
train_transform = transforms.Compose([transforms.Resize((224, 224)), #图片统一缩放到244*244# 4 FiveCroptransforms.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=5bs, ncrops, c, h, w = inputs.shapefor n in range(ncrops):img_tensor = inputs[0, n, ...]# C H Wimg = 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)功能:在图像的左上、右上、左下、右下、中心随机剪裁出尺寸为size
的5
张图片,然后再对这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 TenCroptransforms.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=5bs, ncrops, c, h, w = inputs.shapefor n in range(ncrops):img_tensor = inputs[0, n, ...]# C H Wimg = 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 Fliptransforms.RandomHorizontalFlip(p=1), #执行水平翻转的概率为1transforms.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 Fliptransforms.RandomVerticalFlip(p=0.5), #执行垂直翻转的概率为0.5transforms.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. 当degrees
为a
时,在区间(-a,a)
之间随机选择旋转角度;
II. 当degrees
为(a,b)
时,在区间(a,b)
之间随机选择旋转角度;
resample:
重采样方法;
expand:
是否扩大图片以保持原图信息,因为旋转后可能有些信息被遮挡了而丢失,如果扩大尺寸则可以显示完整图片信息;
center:
旋转点设置,默认沿着中心旋转;
(3)代码示例:
train_transform = transforms.Compose([transforms.Resize((224, 224)), #图片统一缩放到244*244# 3 RandomRotationtransforms.RandomRotation(90),transforms.RandomRotation((90), expand=True), #当batch_size不为1时,expand使用时,张量在第0个维度尺寸需要匹配,需要对图片缩放到统一的sizetransforms.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 osimport numpy as npimport torchimport randomfrom torch.utils.data import DataLoaderimport torchvision.transforms as transformsfrom tools.my_dataset import RMBDatasetfrom PIL import Imagefrom matplotlib import pyplot as pltdef 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 = 10BATCH_SIZE = 1LR = 0.01log_interval = 10val_interval = 1rmb_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数据转换为imageelif 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)# 构建DataLodertrain_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 Wimg_tensor = inputs[0, ...] # C H Wimg = 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=5bs, 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)