Pytorch:关于numpy与PIL转换的问题

对数据集的处理会遇到很多的问题,相信这些问题也经常困扰各位,尤其是关于transforms的使用。不得不说这个库非常的好用,帮助我们节省了很多的时间,但是不可避免我们会遇到关于pytorch中的Tensor,numpy以及PIL之间的转化,这主要是因为transforms进行转换时接受的是PIL Image类型,要不会报错,下面就这一部分讲解一下这部分的转化。

# 首先来说明一下如何使用transforms来进行定义
from torchvision import datasets, transforms
# 下面这段代码的意思是说将PIL Image对象转化成Tensor对象,并进行Normalize(归一化)操作
# 0.1307,0.3081这两个值是对mnist手写数据集归一化的数字(不用在意,网上一搜就有)
# 第一个值代表均值,第二个代表方差
data_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

此文主要讲解的是numpy、PIL Image之间的转化,所以关于transforms的各种转换函数就不说明了,文章的末尾会给个链接。

进入正题:

(1)如何把numpy传入transform中

# 首先我们要知道transforms接受的类型是PIL Image类型的,所以需要将numpy转换成PIL Image类型
# 这里我们需要注意,如果我们要将numpy转换成PIL格式,那么要求数据类型是dtype=uint8, range[0, 255] 
# 同时传入的numpy数组的类型为(H, W, C),即(高,宽,通道),如果C=1,就不用写了
# 比如处理mnist灰度图像,则为(28, 28)
# 如果处理彩色图像,则为(高, 宽, 3)
# 以mnist图像为例,上代码
 
# x.shape =  (28, 28), 数据类型为numpy
# 要想用transforms进行处理,就要转换成PIL对象
# 此时的pil_image 就是PIL Image类型
pil_image = transforms.ToPILImage()(x)
# 之后在进行转换就行了,data_transform就是上面定义的transforms
# 此时x.shape = (1, 28, 28), 这个shape中的1是转换之后自动加的,表示通道数,灰色图像就为1,彩色图像就为3,至于为什么1在前28在后进行表示,这是一位pytorch是通道优先规则,数据类型为tensor, 数据范围0-1
x = data_transform(pil_image)
 

看似代码很简单,其实还是有需要注意的地方,尤其是transform只接收uint8数据类型的numpy,如果为其他格式,很容易出错。很多人很可能就会想着转换一下就行,但其实转换也需要注意结合具体实际。

比如int16转unint8:

img = Image.open(r"/Users/Sakura_Yuki/Downloads/16bit.tif")
# 转成array
re_img = np.asarray(img)
print(re_img.dtype)


# array转回Image对象
 
# 显示方法一
im = Image.fromarray(np.uint8(img))
 
#显示方法二,更合理
img_nrm = (img - np.min(img)) / (np.max(img) - np.min(img))
im = Image.fromarray(np.uint8(255*img_nrm))
im.show()

第一种直接使用numpy.uint8函数进行强转

第二种是先进行0-255范围的标准化,然后使用numpy.uint8


上述两者做的是不同的事情,看自己需要哪一种情况

np.uint8只考虑数字的最低字节。就像在做value && 0xff

>>> img = np.array([2000, -150, 11], dtype=np.int16)
>>> np.uint8(img)
array([208, 106,  11], dtype=uint8)

第二种先使值正常化

img_new = (img - img.min()) * ((max_new - min_new) / (img.max() - img.min())) + min_new

它有效地将一个范围更改为另一个范围,并相应地缩放两者之间的所有值。根据定义,原始最小/最大值将成为目标最小/最大值

opencv中也有函数能直接实现该功能

>>> cv2.normalize(img, out, 0, 255, cv2.NORM_MINMAX)
array([255,   0,  19], dtype=int16)

对于需要不失真的图片格式转换,显然第二种为合理

 

 2)如何把Tensor传入transform中

# 下面来讲解tensor转PIL,有了上面numpy转PIL,这个就非常好理解了,照猫画虎,不过有两点需要注意
# 1.tensor在转PIL时,size必须是(C, H, W), C是通道,W是宽,H是高,C=1时不写
# 2.tensor数据类型为float,至于数据范围,我们只需要保证要是0-1就全是0-1,要是0-255就全是0-255,最后经过转换之后会变成0-1
# 话不多说,上代码
# x.shape = (28, 28), 类型为tensor,数据范围(0-1)
pil_image = transforms.ToPILImage()(x)
# x.shape = (1, 28, 28),经过transforms处理之后默认转化成(1, 28, 28)形状,数据范围0-1
x = data_transform(pil_image)

下面链接的这个博客上是关于transforms的转化函数,放在transforms.Compose([这里放转化函数,以逗号进行分割])

博客地址:https://sakura.blog.csdn.net/article/details/107533668

posted @ 2024-08-26 11:16  龙雪  阅读(15)  评论(0编辑  收藏  举报  来源