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

时间:2011-01-31 18:57:03

标签: image-processing alphablending convolution premultiplied-alpha

我正在尝试将一个框模糊应用于透明图像,并且我在边缘处得到一个“暗晕”。

Jerry Huxtable has a short mention of the problem,以及展示问题的非常好的演示:

enter image description here

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

enter image description here

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

enter image description here

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

enter image description here

所以

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

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

finalRed =   1/9 * 255
finalGreen = 1/9 * 255
finalBlue =  0
finalAlpha = 1/9*255 + 1/9*255

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

finalRed =   28
finalGreen = 28
finalBlue =  0
finalAlpha = 56 (22.2%)

enter image description here

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

enter image description here

白色显示时更清晰:

enter image description here


实际上我正在对包含透明文本的位图执行框模糊处理,并且文本会在条纹周围变暗:

enter image description here

我开始使用PixelFormat32bppARGB格式

的GDI +位图

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

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

final = 1/9*(pixel1+pixel2+pixel3...+pixel9)

给我错误的答案。


编辑:更简单的例子是:

我将使用0..1:

范围内的颜色和alpha值执行此数学运算

enter image description here

我要将盒子模糊卷积滤镜应用到中间像素:

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)

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

enter image description here

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

enter image description here

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

(0, 1, 0, 0.33)

enter image description here

哪个看起来适合我的全不透明的例子;但是当我开始涉及部分透明像素时,我不知道该怎么做。

另见

3 个答案:

答案 0 :(得分:6)

tkerwin有already provided the correct answer,但似乎需要进一步解释。

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

finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;

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

答案 1 :(得分:2)

根据您的链接中的建议,您在模糊之前预先乘法,并在模糊之后进行非预乘。在您的示例中,预乘实际上什么都不做,因为没有半透明像素。你做了模糊,然后你需要你做前期乘法(假设从0到1的标准化颜色值):

RGB' = RGB/A  (if A is > 0)
A' = A

这将为您提供非预先模糊的最终图像。

答案 2 :(得分:0)

这两个答案在这里似乎都是错误的。

使用适当的混合模式,根据波特·达夫的说法,该模式是:

FG.RGB + (1.0 - FG.Alpha)*BG.RGB

不确定其余答案的来源,但哇。

alpha编码指示over操作。