使用 PIL,我可以以可定制的方式组合两个(或更多)频段吗?

fra*_*ans 2 python python-imaging-library

我想“分解”一个Image(即split()),通过将其中一个通道与另一个通道组合来修改它,然后再次重新“组合”(即merge())它们。

此示例应使用我希望存在的功能将边缘映射到红色通道来突出显示边缘:

from PIL import Image, ImageFilter
base = Image.open("example.jpg")
base.show()
Run Code Online (Sandbox Code Playgroud)

基础图像

edges = (base
    .convert("L")
    .filter(ImageFilter.Kernel((3, 3), (-1, -1, -1, -1, 8,-1, -1, -1, -1), 1, 0))
    .point(lambda p: 255 if p > 10 else 0))
edges.show()
Run Code Online (Sandbox Code Playgroud)

边缘

r, g, b = base.split()

# now I want the blue channel/band for each pixel to be whatever respective
# pixel is brighter on a given set of bands

# this is what I want to do, but `combine()` does not exist
b_with_edges = Image.combine((r, edges), lambda pixels: max(pixels))
# instead - for the example I'm dropping blue and take just the edges
b_with_edges = edges

new_image = Image.merge("RGB", (r, g, b_with_edges))
new_image.show()
Run Code Online (Sandbox Code Playgroud)

具有边缘突出显示的组合图像

当然,我可以手动迭代所有像素并手动组合它们。从我看来,PIL它似乎是一个开发得相当完善且灵活的库,所以我希望有一些内置的方式..

更新

由于 CrazyChucky 的答案非常适合这个用例,所以我敢添加一个精炼版本:

from PIL import Image, ImageFilter, ImageChops
base = Image.open("example.jpg").reduce(1)
r, g, b = base.split()
edges = (base
    .convert("L")
    .filter(ImageFilter.Kernel((3, 3), (-1, -1, -1, -1, 8,-1, -1, -1, -1), 1, 0))
    .point(lambda p: 255 if p > 10 else 0))
image_with_edge_highlighting = Image.merge("RGB", (r, g, ImageChops.lighter(b, edges)))
image_with_edge_highlighting.show()
Run Code Online (Sandbox Code Playgroud)

Cra*_*cky 5

您在这种特殊情况下的描述听起来像ImageChops.lighter

\n
from PIL import ImageChops\n\nb_with_edges = ImageChops.lighter(r, edges)\n
Run Code Online (Sandbox Code Playgroud)\n

它使用两个给定图像中较亮(具有较高值)的像素生成新图像。

\n

按照您的代码合并后,我得到以下结果:

\n

输出图像

\n

这就是说......虽然该ImageChops模块有很多有用的函数,但我不知道有一个更通用的、可定制的函数可以接受您所描述的可调用函数。这样的函数如果存在的话,可能会很有用,但性能可能不会很高。它本质上是一个for循环的包装器,在 Python 级别进行迭代,而不是 C\xe2\x80\x94,就像 Pandas 的apply方法一样。

\n

point您已经在问题中证明了这一点,是我所知道的最接近的东西......但当然它仅使用查找表对单个图像进行操作。)

\n

您可以这样实现它,通过PixelAccess每个图像的load方法返回的类直接访问和设置像素:

\n
def custom_image_combine(images, fn):\n    width, height = images[0].size\n    return_image = Image.new(images[0].mode, (width, height))\n    return_image_access = return_image.load()\n    accessors = [image.load() for image in images]\n    for x in range(width):\n        for y in range(height):\n            return_image_access[x, y] = fn(\n                accessor[x, y] for accessor in accessors\n            )\n    return return_image\n\nb_with_edges = custom_image_combine((r, edges), max)\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,您可以简单地提供max,而不是lambda使用它的 。

\n