pytorch学习日记(二)——之cv2,matplotlib,PIL比较及与Tensor的转换

用python进行图像处理中分别用到过matplotlib.pyplot、PIL、cv2三种库,这三种库图像读取和保存方法各异,并且图像读取时顺序也有差异,如plt.imread和PIL.Image.open读入的都是RGB顺序,而cv2.imread读入的是BGR顺序。使用时需要倍加注意。

1. cv2,matplotlib,PIL比较

  • 读取图像

1.cv2.imread

opencv读进来的是numpy数组,是uint8类型,0-255范围,图像形状是(H,W,C),读入的顺序是BGR

img = cv2.imread(img_path)
print('cv2',img.shape)       #(H,W,C)

2.matplotlib.pyplot.imread

matplotlib读取进来的图片是numpy数组,是unit8类型,0-255范围,图像形状是(H,W,C),读入的顺序是RGB

img = plt.imread(img_path)
print('plt',img.shape)       #(H,W,C)

3.PIL.image.open

PIL是有自己的数据结构的,类型是<class 'PIL.Image.Image'>;但是可以转换成numpy数组,转换后的数组为unit8,0-255范围,图像形状是(H,W,C),读入的顺序是RGB

'''PIL转成numpy数组:'''

img = Image.open(img_path)
print(type(img))  #<class 'PIL.Image.Image'>
img = np.array(img)  
print(type(img))  #<class 'numpy.ndarray'>
print('PIL',img.shape)  #(H,W,C)
'''numpy 转成 PIL'''

#input img is numpy type
img = Image.fromarray(img.astype('uint8')).convert('RGB')
print(type(img))    #<class 'PIL.Image.Image'>
  • 显示图像

这三者均可以用plt.imshow(img),如代码:

import PIL.Image as Image
import matplotlib.pyplot as plt
import cv2

#cv2
img = cv2.imread(img_path)
plt.subplot(1,3,1)
plt.title('cv2')
plt.imshow(img)

#plt
img = plt.imread(img_path)
plt.subplot(1,3,2)
plt.title('plt')
plt.imshow(img)

#PIL
img = Image.open(img_path)
plt.subplot(1,3,3)
plt.title('PIL')
plt.imshow(img)

#show
plt.show()

效果图:

可以看到,第一幅cv2图变色了,因为opencv读取进来的是BGR顺序呢的,而imshow需要的是RGB顺序,因此需要把cv2读的顺序转成RGB顺序

第一种方法:

b,g,r = cv2.split(img)
img = cv2.merge([r,g,b])
img = cv2.imread(img_path)
#=======================
#convert BGR to RGB
b,g,r = cv2.split(img)
img = cv2.merge([r,g,b])
#========================

plt.title('cv2_RGB')
plt.imshow(img)
plt.show()

第二种方法:

img= img[:,:,::-1]
img = cv2.imread(img_path)
#=======================
#convert BGR to RGB
img = img[:,:,::-1]
#========================

plt.title('cv2_RGB')
plt.imshow(img)
plt.show()

两种方法效果图都一样:haha,这样就正常了,逍遥哥哥回来了......

  • 保存图像

1)cv2.imwrite - 保存numpy格式的图片

cv2.imwrite("cv2.jpg",img)

2)plt.imsave - 保存numpy格式的图片

plt.imsave('plt.jpg',img)

3)PIL.image - 保存PIL格式的图片

img.save("PIL.jpg")

===============================================================================================

2. PIL.Image / numpy.ndarray与Tensor的相互转换

以上就是图像的基本操作,为了方便进行图像数据的操作,pytorch团队提供了一个torchvision.transforms包,我们可以用transforms进行以下操作:

  • PIL.Image / numpy.ndarray与Tensor的相互转化;
  • 归一化;
  • 对PIL.Image进行裁剪、缩放等操作。

通常,在使用torchvision.transforms,我们通常使用transforms.Compose将transforms组合在一起。

  • 1)PIL.Image / numpy.ndarray与Tensor的相互转化

PIL.Image / numpy.ndarray转化为Tensor,常常用在训练模型阶段的数据读取,而Tensor转化为PIL.Image / numpy.ndarray则用在验证模型阶段的数据输出。

注意:Tensor的形状是[C,H,W],而cv2,plt,PIL形状都是[H,W,C]

我们可以使用 transforms.ToTensor() 将 PIL.Image/numpy.ndarray 数据进转化为torch.FloadTensor,并归一化到[0, 1.0]:

  • 取值范围为[0, 255]的PIL.Image,转换成形状为[C, H, W],取值范围是[0, 1.0]的torch.FloadTensor;
  • 形状为[H, W, C]的numpy.ndarray,转换成形状为[C, H, W],取值范围是[0, 1.0]的torch.FloadTensor。

而transforms.ToPILImage则是将Tensor转化为PIL.Image。如果,我们要将Tensor转化为numpy,只需要使用 .numpy() 即可。如下:

import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

img_path = 'your graph path'

# transforms.ToTensor()
transform1 = transforms.Compose([
    transforms.ToTensor(),  # range [0, 255] -> [0.0,1.0] and convert [H,W,C] to [C,H,W]
])
img = plt.imread(img_path)
print('plt',img.shape)       #(H,W,C)
img = transform1(img)  # 归一化到 [0.0,1.0],并转成[C,H,W]
print(img.shape)         #torch.Size([C,H,W])
# 转化为numpy.ndarray并显示
img_arr = img.numpy() * 255  #use np.numpy(): convert Tensor to numpy
img_arr = img_arr.astype('uint8')  #convert Float to Int
print(img_arr.shape)                #[C,H,W]
img_new = np.transpose(img_arr, (1, 2, 0))   #use np.transpose() convert [C,H,W] to [H,W,C]
plt.imshow(img_new)
plt.show()
  • 2)归一化

归一化对神经网络的训练是非常重要的,那么我们如何归一化到[-1.0, -1.0]呢?只需要将上面的transform1改为如下所示:

#归一化
transform2 = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean = (0.5, 0.5, 0.5), std = (0.5, 0.5, 0.5))
    ]
)

(1)transforms.Compose就是将transforms组合在一起;

(2)transforms.Normalize使用如下公式进行归一化:

channel=(channel-mean)/ std

这样一来,我们的数据中的每个值就变成了[-1,1]的数了。来看下将上面transform1改为transform2所运行的效果图如下:

看上图已经出现失真了,原因是什么呢?

当上面操作已经将像素值转成[-1,1]时,执行代码img_arr = img.numpy() * 255时,举个例子,图像的某一行像素值变成

[ 207.   203.    203.  ... -173.   -175. -175. ],再执行代码img_arr = img_arr.astype('uint8')时,像素值变成

[207 203 203 ...  83  81  81]。就拿其中的 -173 变成 83 来说,首先我们要明确:符号数值在计算机里是用补码存储。

173原码=10101101,由于是负数,-173补码=01010010,计算机里储存的就是这个数值。那么当img_arr = img_arr.astype('uint8')读作无符号数uint8时,01010010连同符号位一起计算=83.故出现上面失真情况。

  • 3)PIL.Image的缩放裁剪等操作

此外,transforms还提供了裁剪,缩放等操作,以便进行数据增强。下面就看一个随机裁剪的例子,这个例子中,仍然使用 Compose 将 transforms 组合在一起,如下:

transform4 = transforms.Compose([
transforms.ToTensor(),
transforms.ToPILImage(),
transforms.RandomCrop((300,300)),
])
img = Image.open(img_path).convert('RGB')

img3 = transform4(img)

img3.show()

运行结果如下:

其他操作就不一一列举了。具体看http://pillow.readthedocs.io/en/5.2.x/handbook/tutorial.html#cutting-pasting-and-merging-images

猜你喜欢

转载自blog.csdn.net/m0_37673307/article/details/81271155