python如何显示图片是一个谜题,今天就让我们来揭秘它!
首先,python中一般采用plt.imshow()
函数读取,今天我们主讲这个。
plt.imshow()
函数参数:通常直接采用 plt.imshow(img)
即可:
img图像数据,支持的数组形状是:
(M,N) : 带有标量数据的图像。数据可视化使用色彩图。
(M,N,3) :具有RGB值的图像(float或uint8)。
(M,N,4) :具有RGBA值的图像(float或uint8),即包括透明度。
前两个维度(M,N)定义了行和列图片,即图片的高和宽;
RGB(A)值应该在浮点数[0, …, 1]的范围内,或者整数[0, … ,255]。超出范围的值将被剪切为这些界限。
关键点:
(1)img必须是数组类型(不能是tensor或者dataframe)
(2)既可以是整数型[0, … ,255],也可以是浮点型[0, …, 1] 这俩显示的效果是一模一样的。
(3)必须是HWC类型的图像,通道必须在最后一维。(记住,只有卷积时通道的维度才是在前)
(1)tensor —> 数组转换:img=img.numpy()
(2)数组 —> tensor转换:img=torch.tensor(img)
(3)数组维度交换,例如将CHW
交换为HWC
:img = np.transpose(img, (1,2,0))
;
(4)tensor维度交换,例如将CHW
交换为HWC
:img.permute(1, 2, 0)
(5)数组行列交换(例如将第三行与第四行交换,有些图片格式是BGR
而不是RGB
,显示时需要先一步转换)img = img[:,:,[2,1,0]]
前面两个行和列不变,只变最后一个通道的顺序
(6)反归一化:有些时候处理的图是归一化的,如果想变为原来的像素,需要执行反归一化,只需令Mean = -Mean/Std,Std=1/Std
,然后再次调用torchvision.transforms.Normalize
函数,将反归一化后的平均值和标准差输入进去,即可实现。(当然,许多操作中直接进行了 img=(img+1)/2 操作,效果与反归一化一模一样, 这是由于dataset中往往会先Totensor,转换为0-1,再(0.5,0.5,0.5)的归一化为[-1,1] 因此上述操作等同于反归一化)
接上条,如果归一化时不是(0.5,0.5,0.5)这样简单的数怎么办呢?一方面还是通过上述公式手动计算,然后调用归一化函数。另一方面,如果步调用归一化函数,也可以采用如下代码:
```python
cifar_10_mean = (0.491, 0.482, 0.447) # mean for the three channels of cifar_10 images
cifar_10_std = (0.202, 0.199, 0.201) # std for the three channels of cifar_10 images
# convert mean and std to 3-dimensional tensors for future operations
mean = torch.tensor(cifar_10_mean).to(device).view(3, 1, 1)
std = torch.tensor(cifar_10_std).to(device).view(3, 1, 1)
#反归一化
img = ((img) * std + mean).clamp(0, 1) # to 0-1 scale
```
虽然这个img的维度是(batch, channel, height, weight)四维的,反归一化的维度是三维的的,不过tensor相乘时会自动转换为同样的size。
#从数据集中读取一张图片
img = dataset[4]
#做一次反归一化,不做的话图片显示与原图不符
#反归一化这一步是必须的,当然,许多操作中直接进行了 img=(img+1)/2 操作,效果与反归一化一模一样
# 这是由于dataset中往往会先Totensor,转换为0-1,再归一化为[-1,1] 因此上述操作等同于反归一化
x= transforms.Compose([transforms.Normalize((-1,-1,-1),(2,2,2))])
img = x(img)
#将图片转换为数组
img = img.numpy()
#维度转换(变为64*64*3)
img = np.transpose(img, (1,2,0))
#将BGR转换为RGB,并且乘上255.
#虽然已经进行了反归一化,但是图像归一化前会进行Totensor转换,会被除以255,因此要乘上255
#当然, 也可也不进行*255的操作,毕竟0-1之间的浮点数也是可以的,图像显示效果一样。
img2 = img[:,:,[2,1,0]]*255
plt.imshow(img2)
plt.show()
下面这段代码是将训练好的自编码器生成的图片显示出来。
#输出第41幅图片
z_sample = dataset[40]
#这一步根据自己的程序决定,我用的线性层作为模型,所以输入模型时需要将图像拉直
z_sample = z_sample.view(-1)
#注意,模型和图像得在同一个设备中
img = model(z_sample.to(get_device()))
#得到输出后的图片,进行反归一化,并转换为numpy数组
img = ((img+1)/2).detach().cpu().numpy()
#因为dataset中图片是CHW形式的,所以要先将此前拉直的图片,变为CHW形式
#注意一定CHW形式要对应,不能前面拉直时是CHW,后面还原时就不是CHW了,这样数据格式就对不上了
img = img.reshape((3,64,64))
#输入imshow函数的图像必须是HWC,所以要将维度交换
img = np.transpose(img,(1,2,0))
#将BGR转换为RGB
img = img[:,:,[2,1,0]]
plt.imshow(img)
plt.show()
#作为对比,输出原41图的图像
x = dataset[40]
x = ((x+1)/2).numpy()
x = np.transpose(x,(1,2,0))
x = x[:,:,[2,1,0]]
plt.imshow(x)
plt.show()
补充一些小知识点:如果我们想要显示动图(例如gym中的动图),动图也是一帧一帧图像而已,这时候我们可以用如下代码:
img = plt.imshow(data) #img是imshow返回值是一个实例
for i in data :
img.set_array(i) #img.set_data 也可以