pytorch transform/tensor 基本操作

pytorch官方文档
transform源码解读
莫烦pytorch

1 .transform

  torchvision.transforms是pytorch中的图像预处理包
  有很多图像预处理方法, 今天从图像维度出发, 讲一些我们经常会用到的

  • Resize, 把给定的图片resize到target size
  • Normalize, Normalized an tensor image with mean and standard deviation
  • ToTensor, convert a PIL image to tensor (H, W, C) in range [0,255] to a torch.Tensor(C, H, W) in the range [0.0, 1.0]
  • ToPILImage, convert a tensor to PIL image
  • CenterCrop, 在图片的中间区域进行crop
  • RandomCrop, 在一个随机位置进行crop
  • FiveCrop, 把图像裁剪为四个角和一个中心
  • TenCrop, five crop+flip 1->10

1.1 ToTensor & ToPILImage

  从Numpy到Tensor的转换有两种方法, 可以用torch.from_numpy(ndarray), 也可以用ToTensor()(ndarray), 这两种有什么区别呢?

np_data = np.random.random((260, 194, 3))
# np_data size (height, width, channel)
torch_data = torch.from_numpy(np_data)  # numpy -> tensor 1
# torch_data size [260, 194, 3]
torch2array = torch_data.numpy()
# torch2array size[260, 194, 3]
tensor_data = ToTensor()(np_data)    # numpy -> tensor 2
#tensor_data size[3, 260, 194]
tensor2array = tensor_data.numpy()
#tensor2array size[3, 260, 194]

  可以看出, 如果用torch.from_numpy()的方法, 转换的tensor和ndarray是相同的shape, (height, width, channel)的shape进, 仍然变成(height, width, channel)的shape
  但是, 如果是用ToTensor()的方式, 那就是 (height, width, channel)的shape进, (channel, height, width)的shape出
  不同于numpy->tensor有两种做法, 从tensor->numpy却只有一种, 就是tensor.numpy(), 而这种做法的shape前后也是不会变化的, 所以我们看到如果按照ToTensor() -> tensor.numpy()的方式将np转成tensor再转为np, 那shape和一开始的np是不同的, 就是(height, width, channel)会变成(channel, height, width)

  下面我们举一个实际的读图转换的例子

# PIL -> np -> tensor -> PIL
PIL_img = Image.open('test_pic.jpeg')
#PIL_img size [194, 260, 3], range [0,255]
np_img = np.asarray(PIL_img)
# np_img size [260, 194, 3], range [0,255]
tensor_from_np = ToTensor()(np_img)
# tensor_img size [3, 260, 194], range [0,1]
tensor_from_PIL = ToTensor()(PIL_img)
# tensor_img size [3, 260, 194], range [0,1]
PIL_from_tensor = ToPILImage()(tensor_from_np)
# PIL_from_tensor size [194, 260, 3], range[0,255]
PIL_from_tensor.save('img_from_tensor.bmp')

# tensor -> numpy -> PIL
np_from_tensor = tensor_from_np.numpy()
# np_from_tensor size [3, 260, 194], range [0, 1]
np_from_tensor *= 255
# np_from_tensor size [3, 260, 194], range [0, 255]
np_from_tensor = np.transpose(np_from_tensor, (1, 2, 0))
# np_from_tensor size [260, 194, 3], range [0, 255]
PIL_from_np = Image.fromarray(np.uint8(np_from_tensor))
# PIL_from_np size [194, 260, 3], range [0, 255]
PIL_from_np.save('img_from_np.bmp')

  为了说明PIL/np/tensor相互转换之间产生的shape和range的变化, 我们这个例子稍微复杂了一点, 关键就是三点

  • 用ToTensor()转换, 不管input的是PIL还是np, 只要是uint8格式的, 都会直接转成[0,1]的float32格式, 所以我们看到的tensor_from_np和tensor_from_PIL是一样的, 而ToPILImage是将tensor转为PIL Image, 自动又会把float32的[0,1]转回uint8
  • 如果是先把tensor转为np, 再转为PIL Image, 那就有上面我们提到的np_img和tensor_from_np.numpy()的shape和range都是不同的, 因为.numpy()的过程就是把tensor格式的数据复制到np格式的数据, 重要的是, np_from_tensor和tensor_from_np共享了同一段内存, 所以我们可以看到在更改np_from_tensor的值的时候, tensor_from_np也在变, 所以要进行[0,1]->[0,255] + transpose更改shape + float32->uint8的过程改为和np_img的shape和range都一样, 这个时候再用Image.fromarray()才能还原成和PIL_img相同的图像
  • ToPILImage除了可以做tensor->PIL, 还可以做np->PIL, 就是将shape为(C,H,W)的Tensor或shape为(H,W,C)的numpy.ndarray转换成PIL.Image, 但注意, 输入是np的话它不会把[0,1]float32自动转成uint8, 所以用法就变成和Image.fromarray()一样了
#before np_from_tensor
tensor_from_np2 = tensor_from_np.clone()
...
...
...
np_from_tensor2 = tensor_from_np2.numpy()
np_from_tensor2 *= 255
np_from_tensor2 = np.transpose(np_from_tensor2, (1, 2, 0))
PIL_from_np2 = ToPILImage()(np.uint8(np_from_tensor2))
PIL_from_np2.save('img_from_np2.bmp')

Note:
  这个np.transpose()函数很有用, opencv的图像shape和np也不一样, 也需要用这个函数来转换维度
  上面提到.numpy()的做法是让tensor和np.ndarray()共享内存, 所以如果我们要用tensor原来的数据, 那就要在对np_from_tensor处理之前, 先用.clone()的方式复制出一个副本来

1.2 Resize

torchvision.transforms.Resize(size, interpolation=2)
  这个resize的函数很简单, 用的就是PIL图像处理的内核, 要注意的是, 这个size的格式是(height, width), 也就是说加入要用这个函数2倍downsample一个输入PIL Image图像, 应该是这样

input_image = Image.open('image path')
Resize((input_image.size[1], input_image.size[0]))(input_image)
#or
Resize((input_image.height, input_image.width))(input_image)

  推荐用第二种形式, 比较清楚地体现PIL Image和tensor的维度差别

1.3 Crop

  torchvision.transforms里有很多crop的方法, 选几个用的多的说一下

  • CenterCrop, 在图片的中间区域进行crop
  • RandomCrop, 在一个随机位置进行crop
  • FiveCrop, 把图像裁剪为四个角和一个中心
  • TenCrop, 在FiveCrop的基础上,再将输入图像进行水平或竖直翻转,然后再进行FiveCrop操作,这样一张输入图像就能得到10张crop结果
Crop Description
CenterCrop(size) 将给定的PIL.Image进行中心切割,得到给定的sizesize可以是tuple(target_height, target_width)size也可以是一个Integer,在这种情况下,切出来的图片的形状是正方形。
RandomCrop(szie, padding=0) 切割中心点的位置随机选取。size可以是tuple也可以是Integer
FiveCrop(size) 把图像裁剪为四个角和一个中心, 1张图生成5张图
TenCrop(size, vertical_flip=False) FiveCrop的基础上,再将输入图像进行水平或竖直翻转,然后再进行FiveCrop操作,这样一张输入图像就能得到10张crop结果
RandomSizedCrop(size, interpolation=2) 先将给定的PIL.Image随机切,然后再resize成给定的size大小

1.4 Normalize

1.5 Compose

  transforms.Compose()就是把以上提到的多个步骤整合到一起, 按顺序执行

transforms.Compose([
transforms.CenterCrop(10),
transforms.ToTensor(),
])

你可能感兴趣的:(pytorch transform/tensor 基本操作)