当前位置:   article > 正文

【PIL】torch.tensor和为PIL.Image的相互转换;归一化和反归一化_torch.tensor与pil.image转换

torch.tensor与pil.image转换

图片和数据之间的转换,有两个要点,一个是size的transpose,一个是归一化和反归一化。
torch里数据的格式是(数量 × \times ×通道数 × \times × × \times ×宽),而初始图片读取得到的格式是(长 × \times × × \times ×通道数),因此在转变时得用 transpose 转置一下。归一化和反归一化,前者是为了把图片数据转换到神经网络友好的数值范围,后者是为了还原到人眼友好的数值范围。

1 将图片数值化

主要包括图片的读取和转置操作。图片的读取有更好的方式,请参阅:
https://blog.csdn.net/weixin_42468475/article/details/127536163

1.1 最便捷的读取图片方式

from torchvision.io import image

img=image.read_image('./test2.png')
print(type(img))
print(img.shape)
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 比较麻烦的读取方式:先用PIL读取,然后转成tensor

import numpy as np
from PIL import Image
import torchvision
import torchvision.transforms as transforms


path = './imgs/input/img1.jpg'
image = Image.open(path).convert("RGB")

# 查看shape
print(np.array(image).shape)
# 得到 (251, 201, 3),如果要转化成神经网络可读的格式,我们要转化成 (3, 251, 201) 
# ToTensor() 自带 transpose 的操作 ,很方便
transform_list = [transforms.ToTensor()]
transformer = transforms.Compose(transform_list)
print(transformer(image).shape)
# 得到 torch.Size([3, 251, 201])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

当然,也可以进行手动进行转置

import numpy as np
from PIL import Image
import torchvision
import torchvision.transforms as transforms


path = './imgs/input/img1.jpg'
image = Image.open(path).convert("RGB")

# 查看shape
data = np.array(image)
print(data.shape)
# 得到 (251, 201, 3),如果要转化成神经网络可读的格式,我们要转化成 (3, 251, 201) 
# 将第三个维度放到最前面
data = data.transpose(2,0,1)
data = torch.tensor(data)
print(data.shape)
# 得到 torch.Size([3, 251, 201])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

如何把 tensor 转换成 Image?:

torchvision.transforms.functional.to_pil_image(tensor)
  • 1

2 归一化和反归一化

为了神经网络更快收敛,我们需要把读取图片得到的 tensor 进行归一化,处理后的图片如果

如果不进行反归一化到 [0,1],出现了负值,就会变成下面这样,(即经过归一化后的图像,满足均值为0方差为1):
在这里插入图片描述

总结一下他们的关系:

读取的图片得到数值(范围大小在 [0,255] 或 [0,1] ) = = = 归 一 化 = = = 》 ===归一化===》 ====== 神经网络适合处理的数值(均值为0,方差为1) = = = 反 归 一 化 = = = 》 ===反归一化===》 ====== 原图得到数值(范围大小在 [0,255] 或 [0,1] )

反归一化是为了还原原来人眼可以识别的图。

归一化

归一化的目的:
经过这样处理后的数据符合标准正态分布,即均值为0,标准差为1。使模型更容易收敛。并非是归于[-1,1]或是[0,1]!!!

归一化需要计算 meanstd,我们先来看一下他的格式,即(数量 × \times ×通道数 × \times × × \times ×宽):
在这里插入图片描述
显然有负值,那么接下来我们开始计算meanstd吧:

nb_samples = 0
#创建3维的空列表
channel_mean = torch.zeros(3)
channel_std = torch.zeros(3)
print(image.shape)
N, C, H, W = image.shape[:4]
image = image.view(N, C, -1)     #将w,h维度的数据展平,为batch,channel,data,然后对三个维度上的数分别求和和标准差
print(image.shape)
#展平后,w,h属于第二维度,对他们求平均,sum(0)为将同一纬度的数据累加
channel_mean += image.mean(2).sum(0)  
#展平后,w,h属于第二维度,对他们求标准差,sum(0)为将同一纬度的数据累加
channel_std += image.std(2).sum(0)
#获取所有batch的数据,这里为1
nb_samples += N
#获取同一batch的均值和标准差
channel_mean /= nb_samples
channel_std /= nb_samples
print(channel_mean, channel_std)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

然后利用transforms.Normalize 进行转换:

# 归一化生成器
normalizer = transforms.Normalize(mean=channel_mean, std=channel_std)

# 归一化得到可处理的值
data = normalizer(image)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

反归一化

根据归一化计算得到的mean和std,我们可以反推出反归一化的 mean 和 std,从而利用 transforms.Normalize 进行转换,计算方法如下:
d e M E A N = [ − m e a n / s t d f o r m e a n , s t d i n z i p ( M E A N , S T D ) ] de_MEAN = [-mean/std for mean, std in zip(MEAN, STD)] deMEAN=[mean/stdformean,stdinzip(MEAN,STD)]
d e S T D = [ 1 / s t d f o r s t d i n S T D ] de_STD = [1/std for std in STD] deSTD=[1/stdforstdinSTD]
MEAN是指归一化时计算出来的均值,de_MEAN是计算出来反归一化的均值,后面需要用。

完整的流程是这样的:

# 定义一个image图像,torch.Size([1, 3, 319, 256])
image = torch.rand([1,3,319,256])

# 计算原图的 mean 和std
nb_samples = 0
#创建3维的空列表
channel_mean = torch.zeros(3)
channel_std = torch.zeros(3)
print(image.shape)
N, C, H, W = image.shape[:4]
#将w,h维度的数据展平,为batch,channel,data,然后对三个维度上的数分别求和和标准差
image = image.view(N, C, -1)     
print(image.shape)
#展平后,w,h属于第二维度,对他们求平均,sum(0)为将同一纬度的数据累加
channel_mean += image.mean(2).sum(0)  
#展平后,w,h属于第二维度,对他们求标准差,sum(0)为将同一纬度的数据累加
channel_std += image.std(2).sum(0)
#获取所有batch的数据,这里为1
nb_samples += N
#获取同一batch的均值和标准差
channel_mean /= nb_samples
channel_std /= nb_samples
print(channel_mean, channel_std)


# 这是归一化的 mean 和std 
channel_mean = torch.tensor([-0.5321, -0.8102, -0.5532])
channel_std = torch.tensor([1.2582, 1.0009, 0.9211])
# 这是反归一化的 mean 和std 
MEAN = [-mean/std for mean, std in zip(channel_mean, channel_std)]
STD = [1/std for std in channel_std]

# 归一化和反归一化生成器
normalizer = transforms.Normalize(mean=channel_mean, std=channel_std)
denormalizer = transforms.Normalize(mean=MEAN, std=STD)

# 归一化得到可处理的值
data = normalizer(image)
# 反归一化得到原图
image2 = denormalizer(data)

# image ≈ image2 因为浮点数计算有误差,
# 所以不会完全一样,但是问题不大~
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/213466
推荐阅读
相关标签
  

闽ICP备14008679号