Python PIL - 分割混合两张图片的函数?

9

编辑:代码现在可以运行,感谢Mark和zephyr,zephyr还提供了两个可行的备选解决方案。

我想使用PIL来分割混合两张图片。我找到了ImageChops.multiply(image1, image2),但我找不到类似的divide(image, image2)函数。

解释混合模式(我将这里的前两张图像用作我的测试源)。

是否有我错过的内置分割混合函数(PIL或其他)?

我的测试代码如下,它可以运行并且接近我要寻找的目标。生成的图像输出与此处的分割混合示例图像相似:解释混合模式

是否有更有效的方法进行这种分割混合操作(步骤更少、速度更快)?起初,我尝试使用lambda函数在Image.evalImageMath.eval中检查黑色像素并在除法过程中将其翻转为白色,但我无法使它们产生正确的结果。

编辑:修正了代码并缩短了长度,感谢Mark和zephyr。生成的图像输出与zephyr的numpy和scipy解决方案的输出相匹配。

# PIL Divide Blend test

import Image, os, ImageMath

imgA = Image.open('01background.jpg')
imgA.load()
imgB = Image.open('02testgray.jpg')
imgB.load()

# split RGB images into 3 channels
rA, gA, bA = imgA.split()
rB, gB, bB = imgB.split()

# divide each channel (image1/image2)
rTmp = ImageMath.eval("int(a/((float(b)+1)/256))", a=rA, b=rB).convert('L')
gTmp = ImageMath.eval("int(a/((float(b)+1)/256))", a=gA, b=gB).convert('L')
bTmp = ImageMath.eval("int(a/((float(b)+1)/256))", a=bA, b=bB).convert('L')

# merge channels into RGB image
imgOut = Image.merge("RGB", (rTmp, gTmp, bTmp))

imgOut.save('PILdiv0.png', 'PNG')

os.system('start PILdiv0.png')
3个回答

4
你正在询问:
有没有更有效率的方法来进行除法混合操作(步骤更少,速度更快)?
你也可以使用Python包混合模式。它是用向量化的Numpy数学编写的,通常很快。通过pip install blend_modes安装它。我已经以更详细的方式编写了命令,以提高可读性,如果链式调用会更短。像这样使用blend_modes来分割您的图像:
from PIL import Image
import numpy
import os
from blend_modes import blend_modes

# Load images
imgA = Image.open('01background.jpg')
imgA = numpy.array(imgA)
# append alpha channel
imgA = numpy.dstack((imgA, numpy.ones((imgA.shape[0], imgA.shape[1], 1))*255))
imgA = imgA.astype(float)

imgB = Image.open('02testgray.jpg')
imgB = numpy.array(imgB)
# append alpha channel
imgB = numpy.dstack((imgB, numpy.ones((imgB.shape[0], imgB.shape[1], 1))*255))
imgB = imgB.astype(float)

# Divide images
imgOut = blend_modes.divide(imgA, imgB, 1.0)

# Save images
imgOut = numpy.uint8(imgOut)
imgOut = Image.fromarray(imgOut)
imgOut.save('PILdiv0.png', 'PNG')

os.system('start PILdiv0.png')

请注意,为了使此方法有效,两个图像需要具有相同的尺寸,例如 imgA.shape == (240,320,3)imgB.shape == (240,320,3)

3
这里有一个除法函数的数学定义: http://www.linuxtopia.org/online_books/graphics_tools/gimp_advanced_guide/gimp_guide_node55_002.html 以下是使用scipy/matplotlib的实现代码:
import numpy as np
import scipy.misc as mpl

a = mpl.imread('01background.jpg')
b = mpl.imread('02testgray.jpg')

c = a/((b.astype('float')+1)/256)
d = c*(c < 255)+255*np.ones(np.shape(c))*(c > 255)

e = d.astype('uint8')

mpl.imshow(e)
mpl.imsave('output.png', e)

如果您不想使用matplotlib,可以按照以下方式实现(假设您已经安装了numpy):
```python imgA = Image.open('01background.jpg') imgA.load() imgB = Image.open('02testgray.jpg') imgB.load()
a = asarray(imgA) b = asarray(imgB) c = a/((b.astype('float')+1)/256) d = c*(c < 255)+255*ones(shape(c))*(c > 255) e = d.astype('uint8')
imgOut = Image.fromarray(e) imgOut.save('PILdiv0.png', 'PNG') ```
注:以上内容保留了HTML标签。

感谢您的解决方案。这样更直接了当。我会查看pylab和matplotlib。我想在PIL中没有那么简单的东西了吧?该链接也非常有助于理解其他混合模式。 - moski
抱歉,我对PIL不是很熟悉...我只用它来加载/保存文件。我想可能有一种方法可以做到这一点,但我发现在numpy中进行数学运算更容易。 - so12311
再次感谢。我已经安装了numpy和scipy,所以我也会尝试您的numpy方法。(哦,我发现我也有matplotlib。)我正在尝试将您之前的帖子转换回我的PIL函数,但还没有完全成功。我看到您在此期间更新了一些内容,所以我会使用这些更改再次尝试。 - moski
谢谢。我运行了你们两个的解决方案,并将更改合并到了我的原始 PIL 版本中(削减了很多)。在我的测试图像上,这三个版本的位对位匹配。我不太明白你的 d= 行是怎么回事,但我猜它的工作方式类似于 GIMP 指南方程式中的 min(255,c)。我无法将其压缩到我的 ImageMath.eval 中,但输出仍与你的相匹配。ImageMath.eval 可能已经内置了 255 的剪裁。 - moski

1
你遇到的问题是当图像B中有一个零时,会导致除以零。如果你将所有这些值都转换为1,我认为你会得到期望的结果。这将消除检查零并在结果中修复它们的需要。

谢谢你的帮助。我会进行修复并查看它的效果。 - moski

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