13

我正在尝试将框模糊应用于透明图像,并且边缘周围出现“暗晕”。

Jerry Huxtable简短地提到了这个问题,并且一个很好的演示显示了问题的发生:

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

现在我们将对图像应用 3x3 框模糊。为简单起见,我们将只计算中心像素的新值。盒子模糊的工作方式是,因为我们有一个 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

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

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%)

在此处输入图像描述

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

在此处输入图像描述

在白色上显示时更清晰:

在此处输入图像描述


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

在此处输入图像描述

我从PixelFormat32bppARGB格式的 GDI+ 位图开始


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

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

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

给我错误的答案。


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

我将使用 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)

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

在此处输入图像描述

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

在此处输入图像描述

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

(0, 1, 0, 0.33)

在此处输入图像描述

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

也可以看看

4

3 回答 3

7

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

您在问题中显示的数学是绝对正确的,直到最后。在那里您缺少一步 - 结果仍处于预乘 alpha 模式,并且必须“未乘”回 PixelFormat32bppARGB 格式。乘法的反面是除法,因此:

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

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

于 2011-02-03T18:04:31.013 回答
2

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

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

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

于 2011-01-31T19:27:13.760 回答
0

正确的数学,错误的操作

两个答案在这里似乎都是错误的,并且您在示例中得到了正确的数学运算,而不是过度操作。

使用正确的混合配方,根据 Porter Duff 的说法是:

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

不知道其余的答案来自哪里,但是哇。

alpha 编码指示过度操作

正确编码时,RGBA 表示 Emission 和 Occlusion

必须在图像中关联 alpha 的原因是 RGB 值始终代表发射。在这种情况下,RGB 代表像素的发射,而 alpha 代表遮挡的程度。

也就是说,如果你线性插值,就像你的盒子模糊一样,这些值必须表示发射。在最简单的情况下,考虑一个 100% 发光的像素,我们在每个增量处线性向下插值 25%。

在这里,每个 RGB 的发射应该减少 25%,这样我们就会有 100%、75%、50%、25%,最后是 0%。请注意,在每次增量时,发射都会随着遮挡程度而缩放,因此 alpha 将在相同的值下保持完美同步。只需在 RGBA 上应用卷积作为发射和遮挡的一个单元。最重要的是,混合公式必须是这个答案中的第一个。

现在考虑未关联的 alpha 情况,它实际上根本没有被编码。这里发射与遮挡程度完全无关,因此过度操作将发射的缩放捆绑到第一步,其中不相关的发射乘以遮挡程度。

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

但是,如果有人愚蠢地尝试对这些完全未编码的值执行完全相同的线性插值、旋转、模糊、涂抹或任何其他操作,会发生什么?走着瞧...

在不相关的 alpha 示例中,我们的发射是 100%,但我们的遮挡程度是 75%。现在向下插值 25%。注意光传输的基本数学在这里是如何失败的。我们最终会得到与发射完全不同步的 alpha 表示。100%RGB-75%A、75%RGB-56.25%A、50%RGB-28.125%A等

但是等等,还有更多的黑暗......

最后一个难题是您正在使用非线性编码的显示参考值。也就是说,当使用为 sRGB 显示器编码的值时,它们会被非线性压缩,这反过来又被显示器的 EOTF 还原为显示器的线性光输出。这意味着我们的代码值不代表从显示器发出的类似辐射的比率。因此,发射和遮挡的线性数学将失败,就像在压缩 MP3 值上执行音频数学将失败一样。

因此,在我们简单的线性插值示例中,我们可以看到数学从根本上很快就崩溃了。

我们的第一个增量是 100% 排放,第二个增量是 75%,以此类推。但是,如果我们使用 sRGB 非线性编码值从 100% 线性插值到 25%,即使是这个简单的数学运算,实际发射的光量也会断开,并且我们的 alpha 将以非常相似的方式边缘化,与未关联的 alpha 编码如何分崩离析. 解决这个问题的唯一方法是使我们的过程分为三个步骤:

  1. 撤消像素上的非线性压缩 sRGB 传递函数。
  2. 执行我们的数学运算。
  3. 根据目标传递函数重新应用非线性 sRGB 编码,在本例中为 sRGB。

正确答案,错误的公式

尽管如此,你的盒子模糊数学在第一个实例中是足够正确的,你的半透明绿色为 33% 的发射,而 alpha 遮挡为 33%。根本问题是他过度操作,这将是上面的公式。也就是说,在您的绿色超过全发射背景的情况下:

  FG.RGB + (1.0 - FG.A) * BG.RGB
  [0.0 0.33 0.0] + ((1.0 - 0.33) * [1.0 1.0 1.0])
  [0.0 0.33 0.0] + [0.77 0.77 0.77]
  [0.77 1.0 0.77]

但是请注意,如果您要将示例合成到纯 100% sRGB 红色发射上,即使使用正确的数学,由于非辐射编码值,您会得到更暗的结果。您选择的 sRGB 绿色和红色是完美的例子,它们会加剧将发生的非线性数学变暗,特别是如果您在完全发光的绿色背景之上对红色对象执行模糊处理。以下是完全正确合成的,尽管使用非线性压缩的 sRGB 代码值进行数学运算: 坏星

附加阅读

一些相关的引用。首先来自 Autodesk 的 Zap Andersson 在臭名昭著的 Adob​​e Alpha 线程中,由 Alpha 通道的创建者 Alvy Ray Smith 证实:

这里的错误是克里斯从字面上解释“预乘”这个词。很多人做错了。实际上,我真的很讨厌这个词,因为它使您相信它的字面意思,即某物已与“之前”的某物相乘(即“Pre”某物)。那是错的。这完全歪曲了“预乘”的含义。

学院科技成就奖得主拉里·格里茨

在这一点上,我将继续进行一些宗教性的咆哮,并只是说相关的 alpha(预乘颜色)是唯一有意义的选择。这是渲染器唯一可能自然生成的东西,它是合成和任何其他图像处理数学工作的唯一方式,在我看来,它是存储任何图像的唯一合理形式。

还值得注意的是,关联 alpha 可以表示非关联 alpha(未预乘颜色)不能表示的 RGBA 值。这只是生活中的事实

(Gritz 先生在这里用他的 CANNOT 语句所指的一个例子是火焰或反射之类的情况,它没有遮挡,只有发射,或者 RGB 的 Alpha 为零。这基本上不可能用未关联的 alpha 操作。)

学院科技成就奖获得者杰里米·塞兰

首先,我是“专业”预乘法。据我所知,预乘是像素与 alpha 的自然表示。最令人困惑的是它的名字。

于 2019-06-16T20:53:31.580 回答