从PIL获取像素值列表

53

我正在尝试将一张黑白的.jpg图像转换为列表,然后可以将其调制成音频信号。

我已经导入了PIL模块,并尝试调用内置函数:list(im.getdata())。当我调用它时,Python崩溃了。有没有一种方法将图像(始终为320x240)分解成240行,以便更轻松地进行计算?或者我只是调用了错误的函数?


4
包括崩溃时收到的信息会非常有帮助。 - dbr
9个回答

75

当您调用getdata()时,Python不应该崩溃。图像可能已损坏或您的PIL安装出现问题。请尝试使用另一张图像或发布您正在使用的图像。

这样可以按照您想要的方式拆分图像:

from PIL import Image
im = Image.open('um_000000.png')

pixels = list(im.getdata())
width, height = im.size
pixels = [pixels[i * width:(i + 1) * width] for i in xrange(height)]

8
list(im.getdata()) 的调用导致 Python 崩溃。该代码片段用于将图像数据转换为列表形式。 - SilentGhost
2
当我调用list(im.getdata())时,Python 崩溃了(可能是这样),而我的整个 Linux 系统也变得无响应。硬重启可以解决这个问题。 - Stephen Cagle
4
问题似乎在于转换为标准的python列表。如果我只调用pixels=im.getdata()(它返回一种特殊的、简化的列表类型),一切都很好。但是如果我添加了强制转换,Python就开始使用大量RAM,如果可用的PC不足,它就开始交换,最终崩溃。也许使用一个python列表来保存数百万个像素值有点过头了。 - jesjimher
2
谢谢。我没有遇到崩溃问题,但是我为想要使用numpy数组的其他人提供以下补充:pixels = np.array(im.getdata()).reshape((im.size[1], im.size[0])) - Bill
1
实际上,查看下面@tom10的答案以获取一个numpy数组。 - Bill
显示剩余6条评论

46

如果您已经安装了numpy,您可以尝试以下命令:


data = numpy.asarray(im)

我这里使用“尝试”(try)一词是因为不清楚为什么您的getdata()方法无法正常工作,也不确定asarray方法是否使用了getdata()方法,但进行测试是值得的。


21

我猜你遇到了这样的错误:TypeError: 'PixelAccess' object is not iterable,对吗?

查看Image.load文档以了解如何访问像素。

基本上,使用PIL获取图像中像素列表的方法是:

from PIL import Image
i = Image.open("myfile.png")

pixels = i.load() # this is not a list, nor is it list()'able
width, height = i.size

all_pixels = []
for x in range(width):
    for y in range(height):
        cpixel = pixels[x, y]
        all_pixels.append(cpixel)

这将每个像素附加到all_pixels列表中 - 如果该文件是RGB图像(即使它只包含黑白图像),则每个像素都将表示为元组,例如:

(255, 255, 255)

要将图像转换为单色图像,只需对三个值取平均值 - 因此,代码的最后三行将变为...

cpixel = pixels[x, y]
bw_value = int(round(sum(cpixel) / float(len(cpixel))))
# the above could probably be bw_value = sum(cpixel)/len(cpixel)
all_pixels.append(bw_value)

或者获取亮度(加权平均值):

cpixel = pixels[x, y]
luma = (0.3 * cpixel[0]) + (0.59 * cpixel[1]) + (0.11 * cpixel[2])
all_pixels.append(luma)

或者是纯粹的一位黑白:

cpixel = pixels[x, y]
if round(sum(cpixel)) / float(len(cpixel)) > 127:
    all_pixels.append(255)
else:
    all_pixels.append(0)

在PIL中可能有更快的方法来进行RGB到二值图像的转换,但这个方法可以工作,并且不是特别慢。

如果你只想对每一行执行计算,你可以跳过将所有像素添加到一个中间列表的步骤。例如,计算每一行的平均值:

from PIL import Image
i = Image.open("myfile.png")

pixels = i.load() # this is not a list
width, height = i.size
row_averages = []
for y in range(height):
    cur_row_ttl = 0
    for x in range(width):
        cur_pixel = pixels[x, y]
        cur_pixel_mono = sum(cur_pixel) / len(cur_pixel)
        cur_row_ttl += cur_pixel_mono
    
    cur_row_avg = cur_row_ttl / width
    row_averages.append(cur_row_avg)

print "Brighest row:",
print max(row_averages)

[提示] "Load" 函数链接: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.load - Cloud Cho

3

虽然不是PIL,但scipy.misc.imread可能仍然有趣:

import scipy.misc
im = scipy.misc.imread('um_000000.png', flatten=False, mode='RGB')
print(im.shape)

提供

(480, 640, 3)

因此它是(高度,宽度,通道数)。所以您可以通过迭代来访问它

for y in range(im.shape[0]):
    for x in range(im.shape[1]):
        color = tuple(im[y][x])
        r, g, b = color

2
scipy.misc.imread is now deprecated, use`import imageioim=imageio.imread('sogreche.jpg')` - Alessandro Jacopson

3

如果你想计算白色或黑色像素,以下是解决方案:

这也是一种解决方法:

from PIL import Image
import operator

img = Image.open("your_file.png").convert('1')
black, white = img.getcolors()

print black[0]
print white[0]

3
pixVals = list(pilImg.getdata())

输出是图片中所有RGB值的列表:

[(248, 246, 247), (246, 248, 247), (244, 248, 247), (244, 248, 247), (246, 248, 247), (248, 246, 247), (250, 246, 247), (251, 245, 247), (253, 244, 247), (254, 243, 247)]

2
data = numpy.asarray(im)

注意:在PIL中,img是RGBA格式。在cv2中,img是BGRA格式。

我的稳健解决方案:

def cv_from_pil_img(pil_img):
    assert pil_img.mode=="RGBA"
    return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGBA2BGRA)

1
正如我之前评论的那样,问题似乎是从PIL内部列表格式转换为标准Python列表类型。我发现Image.tostring()更快,并且根据您的需求,它可能已经足够了。在我的情况下,我需要计算图像数据的CRC32摘要,这很合适。
如果您需要执行更复杂的计算,涉及NumPy的tom10响应可能是您所需的。

1
看起来 PILlow 可能已经将 tostring() 改为 tobytes()。 当我尝试提取 RGBA 像素以将它们放入 OpenGL 纹理中时,以下代码适用于我(在 glTexImage2D 调用内,我省略了它的长度)。
from PIL import Image
img = Image.open("mandrill.png").rotate(180).transpose(Image.FLIP_LEFT_RIGHT)

# use img.convert("RGBA").tobytes() as texels

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