如何将 PIL 图像转换为 numpy 数组?

Posted

技术标签:

【中文标题】如何将 PIL 图像转换为 numpy 数组?【英文标题】:How to convert a PIL Image into a numpy array? 【发布时间】:2010-09-27 22:31:19 【问题描述】:

好的,我正在尝试将 PIL 图像对象来回转换为 numpy 数组,这样我可以进行比 PIL 的 PixelAccess 对象允许的更快的逐像素转换。我已经想出了如何通过以下方式将像素信息放置在有用的 3D numpy 数组中:

pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)

但是在我完成了所有很棒的转换之后,我似乎无法弄清楚如何将它加载回 PIL 对象中。我知道 putdata() 方法,但似乎无法让它发挥作用。

【问题讨论】:

请注意pic.size[0]pic.size[1] 应该交换(即reshape(pic.size[1], pic.size[0], 3)),因为sizewidth x heightx * y,而矩阵排序是rows x columns 【参考方案1】:

你并不是说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 开始,"proper" way to convert between images and numpy arrays 很简单

>>> pix = numpy.array(pic)

尽管结果数组的格式与您的不同(在本例中为 3-d 数组或行/列/rgb)。

然后,在对数组进行更改后,您应该可以使用pic.putdata(pix) 或使用Image.fromarray(pix) 创建新图像。

【讨论】:

首先,不应该是pic.putdata(data)吗?并且 numpy.asarray(pic) 会生成一个只读数组,因此您需要调用 numpy.array(pic),而您没有回答问题...从您提供的链接来看,它似乎是 pic = Image.fromarray(像素)。修正你的答案,我会接受的。 感谢...Image.fromarray 没有在 PIL 文档中列出 (!) 所以如果不是因为这个,我永远不会找到它。 该页面将numpy.asarray(pic) 列为“正确”的转换方式,而不是numpy.array(pic)。根据this answer array 将复制而asarray 不会(但随后asarray 结果将是只读的)。 这里有一个警告(来自我自己的错误):您还需要考虑数据的规模和范围。在许多用例中,您会使用 0-255 字节渲染图像,但您可能希望这些图像在 numpy 数组中转换为例如 0.0-1.0。来自 uint8 的一些单位转换可以做到这一点,但在这种情况下,它不会......所以检查一下 :) 第二个答案更好。【参考方案2】:

以数组形式打开I

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

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

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

来源:Filter numpy images with FFT, Python

如果您出于某种原因想要明确地执行此操作,有 pil2array() 和 array2pil() 函数使用 getdata() on this page in correlation.zip。

【讨论】:

@ArditS.: 你是先import Image 吗?你有安装 PIL 吗? 是否需要uint8 转换? numpy.asarray(Image.open(filename)) 似乎适用于 .jpg 图像,但不适用于 .png。结果显示为array(<PngImagePlugin.PngImageFile image mode=LA size=500x500 at 0x3468198>, dtype=object)PngImagePlugin.PngImageFile 对象似乎没有明显命名的方法来解决这个问题。我想我应该将此作为一个新问题提出,但这与该线程非常相关。有人知道这里出了什么问题吗? @Rebs:这就是 why 这么快的原因:getdata() 返回一个类似对象的序列 (pillow.readthedocs.io/en/3.4.x/reference/…),但枕头图像实现了 @987654336 @ 其中numpy 可以用来访问图像的原始字节,而无需通过迭代器(参见github.com/python-pillow/Pillow/blob/… 和docs.scipy.org/doc/numpy/reference/arrays.interface.html)。你甚至可以只使用numpy.array(PIL.Image.open('test.jpg')) @jez 在将 Image 对象转换为 numpy 之前检查它是否已关闭。同样的事情发生在我身上,我发现我在某个地方关闭了图像对象。【参考方案3】:

我在 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 格式。

【讨论】:

这是关于最简洁的示例,包括导入语句(感谢您提供的详细信息)。让我们对此答案进行投票以提高知名度。 我发现当我将 PIL 绘制的图像转换为 numpy 数组时,在数组上使用 matplotlib imshow 时,它显示它颠倒了,需要 np.flipud 来修复。虽然我的 PIL 图像是使用 ImageDraw.Draw 从头开始​​创建的。我认为必须小心他们的坐标原点来自哪里。 祝福你!!这个答案找了半天。它解决了我将绘图图像恢复到原始轴后的原始轴的问题。【参考方案4】:

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

import numpy
import PIL

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

【讨论】:

这种转换方式会保留图像但会导致颜色丢失。无论如何要避免颜色损失? @moondra 如果我理解您的问题,您可以将.convert("L") 替换为.convert("RGB") "L" 生成灰度图像【参考方案5】:

转换Numpy to PIL图像和PIL to Numpy

import numpy as np
from PIL import Image

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

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

【讨论】:

【参考方案6】:

我今天使用的例子:

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)

【讨论】:

【参考方案7】:

如果您的图像以 Blob 格式存储(即在数据库中),您可以使用 Billal Begueradj 解释的相同技术将图像从 Blob 转换为字节数组。

就我而言,我需要将图像存储在 db 表的 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])

【讨论】:

【参考方案8】:
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

您可以将图像转换为 numpy 通过在挤出特征(非标准化)后将图像解析为 numpy() 函数

【讨论】:

这是用于 numpy 和张量之间的转换,例如火炬。这个问题是关于 PIL 的。

以上是关于如何将 PIL 图像转换为 numpy 数组?的主要内容,如果未能解决你的问题,请参考以下文章

将 NumPy 数组转换为 PIL 图像

如何将 .tif PIL 图像转换为火炬张量?

将 Numpy 数组保存为图像

PIL 图像到数组(numpy 数组到数组) - Python

如何使用 PIL 将 1 通道图像转换为 3 通道?

如何使用 PIL 放大图像的某个部分?