使用 "addition" 混合模式将两个图像相加:在 python 中重新创建 GIMP "addition" 模式会给出不同的结果

Adding two images togheter using "addition" blending mode: recreating GIMP "addition" mode in python gives different results

我正在尝试将两张图片相加。

当我在 GIMP 中这样做时,使用 'Addition' layer-mode 我得到以下所需的输出。

现在,我正尝试在 Python 中执行此操作,但我无法使其正常工作。根据 gimp 文档,据我了解,加法公式只是将每个像素相加,最大设置为 255。

加法模式方程:E = min((M+I), 255)

我尝试在 Python 中应用它,但输出看起来不同。如何在 Python 中获得与 gimp 添加层混合模式相同的输出?

我的尝试:

# Load in the images
diffuse = cv2.imread('diffuse.png')
reflection = cv2.imread('reflection.png',)
# Convert BGR to RGB
diffuse = cv2.cvtColor(diffuse, cv2.COLOR_BGR2RGB)
reflection = cv2.cvtColor(reflection, cv2.COLOR_BGR2RGB)
# Covnert to uint64 so values can overflow 255
diffuse = diffuse.astype('uint64')
reflection = reflection.astype('uint64')
added = diffuse + reflection # actually add
# clamp values above 255 to be 255
added[added > 255] = 255

# display 
im = Image.fromarray(added.astype('uint8'))
im.save('blended_python.png')

现在,这看起来很相似,但与 GIMP 添加相比实际上太亮了。只需在新选项卡中打开它们,然后在它们之间使用 alt+tab,区别就很明显了。

如何获得与 GIMP 中完全相同的结果?我尝试使用 Python-fu 或 gimp 批处理渲染(因为我有数千张图像想像这样混合)但我也无法正常工作。

在用户 Quang Hoang 的帮助下,我了解到 GIMP 默认情况下在应用加法之前对两层进行隐式转换(sRGB 到线性 sRGB)。

因此,要在 python 中重新创建此行为,我们需要以下函数在 sRGB 和线性 sRGB 之间来回转换。受 and this answer.

启发的函数
def srgb_to_linsrgb(srgb):
    # takes np array as input in sRGB color space and convert to linear sRGB
    gamma = pow(((srgb + 0.055) / 1.055), 2.4)
    scale = srgb / 12.92
    return np.where (srgb > 0.04045, gamma, scale)


def linsrgb_to_srgb(lin):
    # takes np array as input in linear sRGB color space and convert to sRGB
    gamma =  1.055 * (pow(lin, (1.0 / 2.4))) - 0.055
    scale =  12.92 * lin
    return np.where(lin > 0.0031308,  gamma, scale)

然后我们简单地将这些函数添加到原始问题的现有代码中。

# Load in the images
diffuse = cv2.imread('diffuse.png')
reflection = cv2.imread('reflection.png',)

# Convert BGR to RGB
diffuse = cv2.cvtColor(diffuse, cv2.COLOR_BGR2RGB)
reflection = cv2.cvtColor(reflection, cv2.COLOR_BGR2RGB)

# Convert to sRGB in linear space
lin_diffuse = srgb_to_linsrgb(diffuse)
lin_reflection = srgb_to_linsrgb(reflection)

lin_added = lin_diffuse + lin_reflection # actually add
added = linsrgb_to_srgb(lin_added)
added[added > 255] = 255

# # display 
im = Image.fromarray(added.astype('uint8'))