图片处理库之PIL

2,737 阅读2分钟

PIL库的基础

在使用torchvision.transforms时,其中的数据增强通常使用的图片类型为PIL.Image。因此如果使用imageio或者skimage库读取的图片类型为np.ndarray就需要进行转换。本节来对PIL库做一个简单的总结。

Image的基础信息

shape与类型

PIL库中常用的模块是Image模块。读取图片有两种方式:分别是Image.open()或者Image.fromarray()

img = imageio.imread("0.jpg")
img1 = Image.fromarray(img)

img2 = Image.open("0.jpg")

img1或者img2的类型为<class 'PIL.Image.Image'>,并不是np.array。其shape为HWC,torchvision.transforms中的数据增强默认使用的是PIL格式的图像,因此如果读入的是np.ndarray格式需要转换为PIL.Image类型。

属性

image = Image.open("0.jpg")
print('width: ', image.width)
print('height: ', image.height)
print('size: ', image.size)
print('mode: ', image.mode)
print('format: ', image.format)
print('category: ', image.category)
print('readonly: ', image.readonly)
print('info: ', image.info)

#output
width:  320
height:  223
size:  (320, 223)
mode:  RGB
format:  JPEG
category:  0
readonly:  1
info:  {'jfif': 257, 'jfif_version': (1, 1), 'dpi': (72, 72), 'jfif_unit': 1, 'jfif_density': (72, 72)}

数据格式

PIL中保存的数据以uint8的格式存储,其表示八位无符号数,取值范围为0~255。在转换时如果不满足此格式条件,会报错。

PIL库的基础函数操作

待添加....

PIL与numpy相互转换

为什么需要转换

常用于读取图片的库有imageio和skimage以及cv2,其读取数据的类型如下所示。由于torchvision.transforms默认使用的图片处理库为PIL,因此需要将np.ndarray转换为PIL处理。 PIL.Image类型与np.ndarray类型可以相互转换,转换过程中shape不变,仍然是HWC

img_ski = io.imread("0.jpg")
img_cv2 = cv2.imread("0.jpg")
img_imgIO = imageio.imread("0.jpg")

print(type(img_ski))
print(type(img_cv2))
print(type(img_imgIO))

#output:
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'imageio.core.util.Array'>

注意:imageio.core.util.Array类型的相关解释,其为np.ndarray的子类型。【stackoverflow】

imageio.core.util.Array is a subclass of the NumPy array, so it is correct to say that imread returns a NumPy array. You can find the source code for Array at github.com/imageio/ima….

Image与np.ndarray的转换

Image转为np.ndarray

image = Image.open("0.jpg")
img = np.array(image)
print(img.shape)

#output:
(223, 320, 3)

np.ndarray转为Image

img = imageio.imread("0.jpg")
img1 = Image.fromarray(img)
img1.show()

类型报错范例:

img = np.ones((448, 448, 3))
print(img.dtype)
img1 = Image.fromarray(img)

#output:
float64
TypeError: Cannot handle this data type: (1, 1, 3), <f8

从上述的输出可以看出,img的数据类型为float64,而PIL需要的数据类型为uint8,因此会报错。修改方式为:

img = np.ones((448, 448, 3))
print(img.dtype)
img2 = Image.fromarray(np.uint8(img))

PIL与transforms

transforms是torch中的数据增强库,有关PIL的常用库为ToPILImageToTensor

ToPILImage

ToPILImage是为了将np.ndarray或者tensor转换为PIL.Image类型,其中进行了通道转换,最终的转换效果为HWC

#是tensor才进行通道变换
npimg = pic
if isinstance(pic, torch.FloatTensor) and mode != 'F':
    pic = pic.mul(255).byte()
if isinstance(pic, torch.Tensor):
    npimg = np.transpose(pic.numpy(), (1, 2, 0))

ToTensor

PIL读取的图片shape为HWC,torch中tensor默认的shape为CHW,此转换操作在ToTensor()已经实现,因此不需要另外操作。

但是在show图片时,tensor的shape为CHW,而matplotlib中需要HWC因此还需要再变换回来。

def to_tensor(pic):
    """Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor.
    Args:
        pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
    Returns:
        Tensor: Converted image.
    """
    ......
    # ---------# handle numpy array------------
    if isinstance(pic, np.ndarray):
        if pic.ndim == 2:
            pic = pic[:, :, None]

        img = torch.from_numpy(pic.transpose((2, 0, 1))) #此处进行了变换
        # backward compatibility
        if isinstance(img, torch.ByteTensor):
            return img.float().div(255)
        else:
            return img
    ......

    # -----------handle PIL Image----------------
    #从此处可以看出PIL格式转变为torch中的tensor还是通过np.array转换得到的。
    if pic.mode == 'I':
        img = torch.from_numpy(np.array(pic, np.int32, copy=False))
    elif pic.mode == 'I;16':
        img = torch.from_numpy(np.array(pic, np.int16, copy=False))
    elif pic.mode == 'F':
        img = torch.from_numpy(np.array(pic, np.float32, copy=False))
    elif pic.mode == '1':
        img = 255 * torch.from_numpy(np.array(pic, np.uint8, copy=False))
    else:
        img = torch.ByteTensor(torch.ByteStorage.from_buffer(pic.tobytes()))

    img = img.view(pic.size[1], pic.size[0], len(pic.getbands()))
    # put it from HWC to CHW format
    img = img.permute((2, 0, 1)).contiguous()  #此处进行了转换
    if isinstance(img, torch.ByteTensor):
        return img.float().div(255)
    else:
        return img

总结:此次记录了三个方面的内容,分别是PIL的基础内容、PIL与np.ndarray的转换、PIL在transforms中的两个类。PIL读取数据时的基础信息,如shape为HWC,数据类型为uint8,以及一些基础属性。在读取数据时读取类型常常为np.ndarray,因此需要通过转换得到PIL.Image类型的图片。同时在transforms中也有相应的处理类,分别是ToPILImage以及ToTensor,前者的功能可以将np.ndarray或者tensor转为PIL,在其中会调整通道为HWC,而后者会将PIL转为tensor,同时也会调整通道。

收藏链接

各种图像处理库中imread函数的区别 · Zodiac Wang (zodiac911.github.io)

python中不同函数读取图片格式的区别_u013044310的博客-CSDN博客_img.astype

Numpy,Opencv及PIL.Image 格式相互转换_Onwaier的博客-CSDN博客_numpy转opencv

Pytorch:关于numpy与PIL转换的问题_龙雪之樱的博客-CSDN博客