使用PIL中的Image.point()方法操作像素数据

发布于 2024-08-20 02:31:55 字数 1644 浏览 5 评论 0 原文

我正在使用 Python 成像库 通过查找表对黑白图像进行着色,该查找表定义了颜色关系。查找表只是一个包含 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 方法是罪魁祸首。经过一番调查(即阅读文档),我发现“请注意,此方法相对较慢。”回复:putpixel。 (实际运行时间:对于 1024x1024 图像,putpixel 需要 53 秒,getpixel 需要 50 秒)

根据文档中的建议,我使用了 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]]                

处理速度加快了一个数量级,但仍然:处理 1024x1024 图像大约需要 3.5 秒。

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

im.point(table) =>;图片

im.point(函数) =>图片

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

我花了一些时间修改界面,但似乎不太正确。原谅我的无知,但是 PIL 的文档很简短,而且我没有太多的图像处理经验。我用谷歌搜索了一下,找到了一些例子,但没有什么能让我“点击”这个用法。因此,最后我的问题

  • 是:Image.point() 是完成这项工作的正确工具吗?
  • Image.point() 需要什么格式/结构的表格?
  • 有人可以粗略地给出一个示例实现吗?到目前为止,我尝试过的每次迭代都以纯黑色图像结束。

I am using the Python Imaging Library to colorize a black and white image with a lookup table that defines the color relationships. The lookup table is simply a 256-element list of RGB tuples:

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

My first version used the getpixel() and putpixel() methods:

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

This was horribly slow. A profile report pointed to the putpixel and getpixel methods as the culprits. A little investigation (i.e, read the docs) and I find "Note that this method is relatively slow." re: putpixel. (actual runtime: 53s in putpixel and 50s getpixel for a 1024x1024 image)

Based on the suggestion in the docs, I used im.load() and direct pixel access instead:

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

Processing sped up by an order of magnitude, but is still slow: about 3.5s to process a 1024x1024 image.

A more thorough study of the PIL docs seems to indicate Image.point() is exactly intended for this purpose:

im.point(table) => image

im.point(function) => image

Returns a copy of the image where each pixel has been mapped through the given table. The table should contains 256 values per band in the image. If a function is used instead, it should take a single argument. The function is called once for each possible pixel value, and the resulting table is applied to all bands of the image.

I've spent some time hacking around with the interface, but can't quite seem to get it right. Forgive my ignorance, but PIL's docs are curt and I don't have much image processing experience. I've googled around a bit and turned up a few examples, but nothing that made the usage "click" for me. Thus, finally, my questions:

  • Is Image.point() the right tool for this job?
  • What format/structure does Image.point() expect the table?
  • Can someone rough out an example implementation? Every iteration I've tried so far has ended up with a straight black image.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

第几種人 2024-08-27 02:31:55

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

顺便说一下,如果您需要更多地控制颜色并且您觉得 Image.point 不适合您,您可以还可以使用 Image.getdataImage.putdataloadputpixel 更快地更改颜色。但它比 Image.point 慢。

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


编辑

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

颜色表实际上是这样的

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

每个波段范围彼此相邻。
要将颜色 (0, 0, 0) 更改为 (10, 100, 10),它需要变成这样:

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

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

table = sum(zip(*colors), ())

我认为我的第一个示例应该为您演示格式。

Is Image.point() the right tool for
this job?

Yes indeed, Image.point() is perfect for this job

What format/structure does
Image.point() expect the table?

You should flatten the list so instead of [(12, 140, 10), (10, 100, 200), ...] use:

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

Here is a quick example I just tried:

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

alt text
alt text

And by the way, if you need more control over colors and you feel Image.point is not for you you can also use Image.getdata and Image.putdata to change colors more quickly than both load and putpixel. It is slower than Image.point though.

Image.getdata gives you the list of all pixels, modify them and write them back using Image.putdata. It is that simple. But try to do it using Image.point first.


EDIT

I made a mistake in the first explanation, I'll explain correctly now:

The color table actually is like this

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

Each band range next to the other.
To change the color (0, 0, 0) to (10, 100, 10) it need to become like this:

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

To transform your color list into the right format try this:

table = sum(zip(*colors), ())

I think my first example should demonstrate the formate for you.

橘虞初梦 2024-08-27 02:31:55

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

# 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)

I think it might be more typical to point on a band-by-band basis like so (lifted directly from the 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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文