如何将PIL图像转换成NumPy数组?

470
我怎样将PIL中的Image和NumPy数组相互转换,以便我能够进行比PIL的PixelAccess更快速的像素级变换?我可以使用以下方式将其转换为NumPy数组:
pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)

但是,当我修改了数组后,如何将其重新加载到PIL Image中呢?pic.putdata()不起作用。


9
请注意,pic.size[0]pic.size[1] 应该交换位置(即reshape(pic.size[1], pic.size[0], 3)),因为 size 表示的是宽度乘以高度,而矩阵排序应该是行数乘以列数。请注意不要改变原始含义。 - foges
9个回答

460

您没有说明putdata()的具体行为方式。我假设您正在进行

>>> pic.putdata(a)
Traceback (most recent call last):
  File "...blablabla.../PIL/Image.py", line 1185, in putdata
    self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple

这是因为putdata期望一个元组序列,而你提供了一个numpy数组。这

>>> data = list(tuple(pixel) for pixel in pix)
>>> pic.putdata(data)

这段代码可以工作,但速度非常慢。

从PIL 1.1.6版本开始,正确的将图像和numpy数组之间进行转换的方法就是:

>>> pix = numpy.array(pic)

尽管生成的数组格式与您的不同(在这种情况下是三维数组或行/列/RGB),但之后您对数组进行更改后,应该能够使用pic.putdata(pix)或使用Image.fromarray(pix)创建一个新图像。

44
该页面将numpy.asarray(pic)列为“适当”的转换方式,而不是numpy.array(pic)。根据此答案array会创建一个副本,而asarray则不会(但是结果将是只读的)。 - Arthur Tacca
3
第二个答案更好。 - Nathan majicvr.com
1
类型检查警告:'期望类型为 'Union[ndarray, Iterable, int, float]',但实际得到的是 'Image'' - David Taub

351

I作为一个数组打开:

>>> I = numpy.asarray(PIL.Image.open('test.jpg'))

I进行一些处理,然后将其转换回图像:

>>> im = PIL.Image.fromarray(numpy.uint8(I))

来源:使用FFT和Python过滤numpy图像

如果由于某些原因您希望明确执行此操作,则可以使用pil2array()和array2pil()函数并在此页面中的correlation.zip上使用getdata()。


2
@ArditS:你是否先导入了Image?你是否已经安装了PIL? - endolith
9
需要进行 uint8 转换吗? - Neil Traft
6
numpy.asarray(Image.open(filename)) 似乎适用于 .jpg 图像但不适用于 .png。结果显示为 array(<PngImagePlugin.PngImageFile image mode=LA size=500x500 at 0x3468198>, dtype=object)。似乎没有显式命名的方法可以解决这个 PngImagePlugin.PngImageFile 对象的问题。我猜我应该将这个问题作为一个新问题提出,但它与本主题非常相关。有人明白这里出了什么问题吗? - jez
5
这是为什么速度快得多的原因:getdata()返回类似序列的对象(http://pillow.readthedocs.io/en/3.4.x/reference/Image.html#PIL.Image.Image.getdata),但 Pillow 图像实现了 __array_interface__,这使得 numpy 可以访问图像的原始字节,而不必经过迭代器(请参见 https://github.com/python-pillow/Pillow/blob/730cf93c32ffb747c018afffe597ef9ae264a20a/PIL/Image.py#L633 和 https://docs.scipy.org/doc/numpy/reference/arrays.interface.html)。你甚至可以使用 numpy.array(PIL.Image.open('test.jpg')) - tdp2110
6
在将图像对象转换为numpy数组之前,请检查该对象是否已关闭。我遇到过同样的问题,发现在某处关闭了图像对象。 - Shaohua Li
显示剩余4条评论

129

我正在使用Python 3.5中的Pillow 4.1.1(PIL的后继者)。在Pillow和numpy之间进行转换非常简单。

from PIL import Image
import numpy as np
im = Image.open('1.jpg')
im2arr = np.array(im) # im2arr.shape: height x width x channel
arr2im = Image.fromarray(im2arr)
需要注意的一件事是,Pillow风格的im是按列主顺序排列的,而numpy风格的im2arr是按行主顺序排列的。然而,函数Image.fromarray已经考虑到了这一点。也就是说,在上面的例子中,arr2im.size == im.sizearr2im.mode == im.mode。在处理转换后的numpy数组时,我们应该注意HxWxC数据格式,例如将变换im2arr = np.rollaxis(im2arr, 2, 0)im2arr = np.transpose(im2arr, (2, 0, 1))转换为CxHxW格式。

6
这是一个非常干净的例子,包括导入语句(感谢提供细节)。让我们投票支持这个答案,以增加其可见性。 - David Parks
1
我发现当将PIL绘制的图像转换为numpy数组时,使用matplotlib imshow在数组上显示它时,会显示为倒置的,需要使用np.flipud进行修复。尽管我的PIL图像是使用ImageDraw.Draw从头创建的。我认为人们必须小心他们的坐标原点来自哪里。 - CMCDragonkai
1
谢谢!我已经找了半天了,终于找到了答案。这解决了我的问题,使得绘图图像的轴恢复到原始状态。 - Tinkerbell

38
您需要这样将图像转换为numpy数组:

您需要通过以下方式将图像转换为numpy数组:

import numpy
import PIL

img = PIL.Image.open("foo.jpg").convert("L")
imgarr = numpy.array(img) 

这种转换方式可以保留图像,但会导致颜色的损失。有没有什么方法可以避免颜色损失? - Moondra
11
如果我理解您的问题,您可以通过将.convert("L")替换为.convert("RGB")来实现。 - Billal Begueradj
"L" 会将图像转换为灰度。 - qwr

35

Numpy转换为PIL图像,以及PIL转换为Numpy

import numpy as np
from PIL import Image

def pilToNumpy(img):
    return np.array(img)

def NumpyToPil(img):
    return Image.fromarray(img)

3
今天我使用的例子是:
import PIL
import numpy
from PIL import Image

def resize_image(numpy_array_image, new_height):
    # convert nympy array image to PIL.Image
    image = Image.fromarray(numpy.uint8(numpy_array_image))
    old_width = float(image.size[0])
    old_height = float(image.size[1])
    ratio = float( new_height / old_height)
    new_width = int(old_width * ratio)
    image = image.resize((new_width, new_height), PIL.Image.ANTIALIAS)
    # convert PIL.Image into nympy array back again
    return array(image)

0

我可以为 svgtrace 作证,我发现它既超级简单又相对快速。在这里找到它:https://pypi.org/project/svgtrace/

这是我使用它的方法:

from svgtrace import trace

asset_path = 'image.png'
save_path = 'traced_image.svg'

Path(save_path).write_text(trace(asset_path), encoding='utf-8')

在我的机器上(MacBook Pro 2017),一张 1080x1080px 的图片平均需要 3 秒钟。

0
如果您的图像以Blob格式(即在数据库中)存储,您可以使用Billal Begueradj解释的相同技术将图像从Blobs转换为字节数组。
在我的情况下,我需要将我的图像存储在数据库表中的blob列中:
def select_all_X_values(conn):
    cur = conn.cursor()
    cur.execute("SELECT ImageData from PiecesTable")    
    rows = cur.fetchall()    
    return rows

我随后创建了一个辅助函数,将我的数据集转换为np.array:
X_dataset = select_all_X_values(conn)
imagesList = convertToByteIO(np.array(X_dataset))

def convertToByteIO(imagesArray):
    """
    # Converts an array of images into an array of Bytes
    """
    imagesList = []

    for i in range(len(imagesArray)):  
        img = Image.open(BytesIO(imagesArray[i])).convert("RGB")
        imagesList.insert(i, np.array(img))

    return imagesList

此后,我能够在我的神经网络中使用byteArrays。

plt.imshow(imagesList[0])

-4
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

您可以将图像转换为numpy数组,方法是在挤压出特征(反归一化)后将图像解析到numpy()函数中。


这是用于在例如PyTorch中numpy和张量之间进行转换的。这个问题是关于PIL的。 - moi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接