如何在图像卷积过程中使用预乘以解决alpha出血问题?

Ian*_*oyd 13 alphablending image-processing convolution premultiplied-alpha

我试图将一个框模糊应用于透明图像,并且我在边缘周围得到一个"暗晕".

Jerry Huxtable 简要提到了这个问题,并且展示了问题的一个非常好的演示:

在此输入图像描述

但是,对于我的生活,我无法理解" 预乘alpha "是如何解决这个问题的.现在举一个非常简单的例子.我有一个3x3图像,包含一个红色和一个绿色像素:

在此输入图像描述

实际上剩下的像素是透明的:

在此输入图像描述

现在我们将对图像应用3x3 Box Blur.为简单起见,我们只计算中心像素的新值.盒子模糊的工作方式是,由于我们有一个9个位置的正方形(3x3,称为内核),我们占用内核中每个像素的1/9,并将其加起来:

在此输入图像描述

所以

finalRed =   1/9 * red1 + 1/9 * red2 + 1/9 * red3+ ... + 1/9 * red9
finalGreen = 1/9*green1 + 1/9*green2 + 1/9*green3+ ... + 1/9*green9
finalBlue =  1/9* blue1 + 1/9* blue2 + 1/9* blue3+ ... + 1/9* blue9
finalAlpha = 1/9*alpha1 + 1/9*alpha2 + 1/9*alpha3+ ... + 1/9*alpha9
Run Code Online (Sandbox Code Playgroud)

在这个非常简单的例子中,计算变得非常简单:

finalRed =   1/9 * 255
finalGreen = 1/9 * 255
finalBlue =  0
finalAlpha = 1/9*255 + 1/9*255
Run Code Online (Sandbox Code Playgroud)

这给了我一个最终的颜色值:

finalRed =   28
finalGreen = 28
finalBlue =  0
finalAlpha = 56 (22.2%)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

这种颜色太暗了.当我在Photoshop中对同一个3x3像素图像执行3px Box模糊时,我得到了我所期望的:

在此输入图像描述

显示白色时更清晰:

在此输入图像描述


实际上,我在包含透明文本的位图上执行框模糊,文本在边缘周围变暗:

在此输入图像描述

我开始使用PixelFormat32bppARGB格式化的GDI +位图


在应用3x3卷积内核时如何使用"预乘alpha"?

任何答案都必须包括新论坛,因为:

final = 1/9*(pixel1+pixel2+pixel3...+pixel9)
Run Code Online (Sandbox Code Playgroud)

给我错误的答案.


编辑:一个更简单的例子是:

我将使用0..1范围内的颜色和alpha值执行此数学运算:

在此输入图像描述

我要将框模糊卷积滤镜应用于中间像素:

ARGB'
      = 1/9 * (0,1,0,1) + 1/9 * (0,0,0,0) + 1/9 * (0,0,0,0) + 
        1/9 * (0,1,0,1) + 1/9 * (0,0,0,0) + 1/9 * (0,0,0,0) + 
        1/9 * (0,1,0,1) + 1/9 * (0,0,0,0) + 1/9 * (0,0,0,0);

      = (0, 0.11, 0, 0.11) + (0,0,0,0) + (0,0,0,0) +
        (0, 0.11, 0, 0.11) + (0,0,0,0) + (0,0,0,0) +
        (0, 0.11, 0, 0.11) + (0,0,0,0) + (0,0,0,0)

      = (0, 0.33, 0, 0.33)
Run Code Online (Sandbox Code Playgroud)

这给出了相当透明的深绿色.

在此输入图像描述

这不是我期望看到的.相比之下,Photoshop的Box Blur是:

在此输入图像描述

如果我假设(0, 0.33, 0, 0.33)是预先乘以alpha,并且非乘以它,我得到:

(0, 1, 0, 0.33)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

这看起来适合我所有不透明的例子; 但是当我开始涉及部分透明的像素时,我不知道该怎么办.

也可以看看

Mar*_*som 7

tkerwin 已经提供了正确答案,但似乎需要进一步解释.

你在问题中展示的数学是绝对正确的,直到最后.在那里你错过了一个步骤 - 结果仍处于预乘的alpha模式,并且必须"unmultiplied"回到PixelFormat32bppARGB格式.乘法的反面是一个除法,因此:

finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;
Run Code Online (Sandbox Code Playgroud)

你已经表达了一种担忧,即分歧可能会产生一个远远超出范围的结果,但这种情况不会发生.如果跟踪数学,您会注意到红色,绿色和蓝色值不能大于alpha值,因为预乘法步骤.如果你使用的是比简单的盒子模糊更复杂的过滤器,那么它可能是有可能的,但即使你没有使用alpha也是如此!正确的反应是钳制结果,将负数转换为0,将任何大于255的值转换为255.

  • @Ian,难怪你感到困惑!我的坏,我确信我在你的例子中看到了一个预乘.它只能起作用,因为你所有的alpha都是0或1(255). (2认同)