使用PIL中的Image.point()方法来处理像素数据

时间:2010-02-02 01:12:34

标签: python image image-processing image-manipulation python-imaging-library

我使用Python Imaging Library使用定义颜色关系的查找表为黑白图像着色。查找表只是一个256元素的RGB元组列表:

>>> len(colors)
256
>>> colors[0]
(255, 237, 237)
>>> colors[127]
(50, 196, 33)
>>> 

我的第一个版本使用了getpixel()putpixel()方法:

    for x in range(w):
        for y in range(h):
            pix = img.getpixel((x,y))
            img.putpixel((x,y), colors[pix[0]])

这非常缓慢。 profile报告指出putpixelgetpixel方法是罪魁祸首。稍微调查(即阅读文档),我发现“请注意,此方法相对较慢。”re:putpixel。 (实际运行时putpixel中的53s和1024x1024图像的50s getpixel

根据文档中的建议,我使用im.load()代替直接像素访问:

    pixels = img.load()
    for x in range(w):
        for y in range(h):
            pix = pixels[x, y]
            pixels[x, y] = colors[pix[0]]                

处理加速了一个数量级,但仍然慢:约3.5秒来处理1024x1024图像。

对PIL文档进行更彻底的研究似乎表明Image.point() 正好用于此目的:

  

im.point(table) =>图像

     

im.point(function) =>图像

     

返回图像的副本,其中每个像素已通过给定的表进行映射。该表应包含图像中每个波段256个值。如果使用函数,则应该使用单个参数。对每个可能的像素值调用该函数一次,并将结果表应用于图像的所有波段。

我花了一些时间来讨论这个界面,但看起来似乎并不是很正确。原谅我的无知,但PIL的文档很简单,我没有太多的图像处理经验。我已经用Google搜索了几个例子,但是没有任何东西可以让我使用“点击”。最后,我的问题:

  • Image.point()是否适合这项工作?
  • Image.point()期望表格的格式/结构是什么?
  • 有人可以粗略地执行示例吗?到目前为止,我尝试过的每次迭代最终都会产生一个直的黑色图像。

2 个答案:

答案 0 :(得分:15)

  

Image.point()是否是正确的工具   这份工作?

是的,Image.point()非常适合这项工作

  

什么格式/结构   Image.point()期望表?

您应该扁平化列表,而不是[(12, 140, 10), (10, 100, 200), ...]使用:

[12, 140, 10, 10, 100, 200, ...]

以下是我刚试过的一个简单示例:

im = im.point(range(256, 0, -1) * 3)

alt text alt text

顺便说一句,如果您需要更多控制颜色并且感觉Image.point不适合您,您还可以使用Image.getdataImage.putdata更快地更改颜色{{1 }和load。它虽然比putpixel慢。

Image.point为您提供所有像素的列表,修改它们并使用Image.getdata将其写回。就这么简单。但首先尝试使用Image.putdata


修改

我在第一个解释中犯了一个错误,我现在会正确解释:

颜色表实际上就像这样

Image.point

每个频段都在另一个频段旁边。 要将颜色(0,0,0)更改为(10,100,10),需要变为如下:

[0, 1, 2, 3, 4, 5, ...255, 0, 1, 2, 3, ....255, 0, 1, 2, 3, ...255]

要将颜色列表转换为正确的格式,请尝试:

[10, 1, 2, 3, 4, 5, ...255, 100, 1, 2, 3, ....255, 10, 1, 2, 3, ...255]

我认为我的第一个例子应该为你演示合成。

答案 1 :(得分:3)

我认为point在逐个频段的基础上可能更为典型(直接从PIL tutorial提取):

# split the image into individual bands
source = im.split()

R, G, B = 0, 1, 2

# select regions where red is less than 100
mask = source[R].point(lambda i: i < 100 and 255)

# process the green band
out = source[G].point(lambda i: i * 0.7)

# paste the processed band back, but only where red was < 100
source[G].paste(out, None, mask)

# build a new multiband image
im = Image.merge(im.mode, source)