更改 PIL 图像覆盖层的颜色没有任何区别

Changing the color for the overlay of a PIL image makes no difference

我正在尝试使用此功能在透明徽标上应用叠加层:

def overlay(path):

    logo_img = cv2.imread(path, cv2.IMREAD_UNCHANGED)

    '''Saving the alpha channel of the logo to the "alpha" variable.'''
    alpha = logo_img[:, :, 3]

    '''Creating the overlay of the logo by first creating an array of zeros in the shape of the logo.
    The color on this will change later to become the overlay that will mask the logo.'''
    mask = np.zeros(logo_img.shape, dtype=np.uint8)

    '''Adding the alpha (transparency) of the original logo so that the overlay has the same transparecy
    that the original logo has.'''
    # mask[:, :, 2] = alpha

    '''This code chooses random values for Red, Green and Blue channels of the image so that the final
    overlay always has a different background'''
    # r, g, b = (random.randint(0, 255),
    #            random.randint(0, 255),
    #            random.randint(0, 255))

    r, g, b = (0, 255, 0)

    '''There is a risk of losing the transparency when randomizing the overlay color so here I'm saving the
    alpha value'''
    a = 255

    '''Creating the overlay'''
    mask[:, :] = r, g, b, a
    mask[:, :, 3] = alpha

    '''Alp, short for alpha, is separate from above. This determines the opacity level of the logo. The 
    beta parameter determines the opacity level of the overlay.'''
    alp = 1
    beta = 1 - alp

    '''addWeighted() is what masks the overlay on top of the logo'''
    dst = cv2.addWeighted(logo_img, alp, mask, beta, 0, dtype=cv2.CV_32F).astype(np.uint8)

    '''Converting the output dst to a PIL image with the RGBA channels.'''
    pil_image = Image.fromarray(dst).convert('RGBA')

    return pil_image

如您所见,我有这两个用于设置 RGB 的元组。无论是随机化还是 select 特定颜色,叠加层的颜色都没有区别。

# r, g, b = (random.randint(0, 255),
    #            random.randint(0, 255),
    #            random.randint(0, 255))

    r, g, b = (0, 255, 0)

编辑

您希望使用 alpha 遮罩“更改徽标的颜色”。除非您实际操作实际图像像素,否则您无法执行此操作。使用 alpha 抠图方法不是正确的答案。我建议您实际上屏蔽掉要更改的徽标区域,然后用所需的颜色替换颜色。 Alpha 抠图主要用于 对象混合在一起,而不是改变对象的颜色分布。我在下面为后代留下了答案,因为原始问题核心中提供的 alpha matting 的原始方法是不正确的。


对这种方法的核心误解来自 cv2.addWeighted 的执行方式。引用 the documentation(强调我的):

In case of multi-channel arrays, each channel is processed independently. The function can be replaced with a matrix expression:

dst = src1*alpha + src2*beta + gamma;

cv2.addWeighted 没有按照您期望的方式正确处理 alpha 通道。具体来说,它将 alpha 通道视为另一个信息通道,并单独对该通道进行加权求和以获得最终输出。它实际上并没有实现 alpha 抠图。因此,如果您想进行 alpha 抠图,您需要自己实际计算操作。

类似于:

import numpy as np
import cv2
from PIL import Image

def overlay(path):
    ### Some code from your function - comments removed for brevity
    logo_img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    alpha = logo_img[:, :, 3]

    mask = np.zeros(logo_img.shape, dtype=np.uint8)

    # r, g, b = (random.randint(0, 255),
    #            random.randint(0, 255),
    #            random.randint(0, 255))

    r, g, b = (0, 255, 0)
    a = 255
    mask[:, :] = r, g, b, a
    mask[:, :, 3] = alpha

    ### Alpha matte code here
    alp = alpha.astype(np.float32) / 255.0  # To make between [0, 1]
    alp = alp[..., None]  # For broadcasting
    dst_tmp = logo_img[..., :3].astype(np.float32) * alp + mask[..., :3].astype(np.float32) * (1.0 - alp)
    dst = np.zeros_like(logo_img)
    dst[..., :3] = dst_tmp.astype(np.uint8)
    dst[..., 3] = 255

    pil_image = Image.fromarray(dst).convert('RGBA')

    return pil_image

这个新功能的第一部分来自于您原来的功能。但是,具有 alpha 遮罩的部分在上面适当的注释后标记。该部分的第一行会将 alpha 贴图转换为 [0, 1] 范围。这是必需的,以便当您执行 alpha 抠图操作(即两个图像的加权和)时,您可以确保没有输出像素跨越其本机数据类型的范围。此外,我引入了一个单独的三维空间,以便我们可以分别在每个 RGB 通道上广播 alpha 通道以正确进行加权。之后,我们将计算 alpha 遮罩作为徽标图像和遮罩的加权和。请注意,我仅将每个通道的 RGB 通道进行子集化和拉出。你在这里不需要 alpha 通道,因为我直接在加权和中使用它。完成此操作后,创建一个新的输出图像,前三个通道是生成的 alpha 遮罩,但 alpha 通道都设置为 255,因为此时我们已经实现了混合并且您希望所有像素值现在不透明显示。