【问题标题】:How to use pre-multiplied during image convolution to solve alpha bleed problem?如何在图像卷积期间使用预乘来解决 alpha bleed 问题?
【发布时间】:2011-01-31 18:57:03
【问题描述】:

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

Jerry Huxtable has a short mention of the problem,一个很好的演示表明问题发生了:

但我一辈子都无法理解“pre-multiplied 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)

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

另见

【问题讨论】:

  • 我想你现在明白了。部分透明的像素根本不会改变数学。
  • 抱歉,我之前的评论是基于对公式的误读。您确实需要添加一个预乘步骤:preRed1 = red1 * alpha1 / 255; 等。

标签: image-processing alphablending convolution premultiplied-alpha


【解决方案1】:

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

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

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

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

【讨论】:

  • 我猜这是神奇的成分,这让我很困惑。不需要在开始时进行乘法(甚至可以进行)。有了最终答案,我不得不取消预乘,即使我一开始就没有乘过)。
  • @Ian,难怪你很困惑!我的错,我确信我在你的例子中看到了预乘。它之所以有效,是因为您所有的 alpha 值都是 0 或 1 (255)。
  • 3 年后,我重新发现了这个问题。谷歌搜索后,我遇到了我自己的问题。再次,我花了很多时间阅读,并在 Excel 中摆弄了一些样本,以重新了解“预乘图像”如何帮助(当没有任何东西可以预乘时)。
【解决方案2】:

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

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

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

【讨论】:

  • 您将不得不在此基础上添加更多数学知识。假设 red=255, alpha=0.5 然后是 red' = 255/0.5 = 510,这是一个无意义的值。
  • 抱歉,我假设标准化颜色值是从 0 到 1。模糊后将 RGB 值钳制在 1 以上。尽管您最好的选择是通过混合坚持预乘颜色值。
  • @Ian, red=255, alpha=0.5 不应该在现实生活中发生,因为进入总和的每个红色都将乘以相应的 alpha;最大红色为 127.5。如果我们谈论的过滤器不是简单的框模糊,则输出可能大于 255(或小于 0),但就像 tkerwin 所说的那样钳制它们。
  • @Mark Ransom:现实生活中时常发生。请参阅 PixelFormat32bppARGB GDI+ 像素格式。您正在考虑 PixelFormat32bppPARGB(预乘)像素格式。
  • 如果您有部分透明的像素,并且假设您从非预乘格式开始,只需在模糊之前对其进行预乘即可。
【解决方案3】:

数学正确,运算错误

这里的两个答案都错了,而且你的例子中的数学是正确的,而不是过度操作。

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

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

不确定其余答案来自哪里,但是哇。

alpha 编码决定了过度操作

正确编码时,RGBA 表示发射和遮挡

您必须在图像中关联 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 代码值进行数学运算:

补充阅读

一些相关的报价。 First from Zap Andersson of Autodesk in the infamous Adobe Alpha thread,由 Alpha 通道的创建者 Alvy Ray Smith 确认:

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

Academy Scientific and Technical Achievement winner Larry Gritz:

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

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

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

Academy Scientific and Technical Achievement winner Jeremy Selan:

首先,我是“专业”预乘法。尽我所能 理解,预乘是 带 alpha 的像素;最令人困惑的是它的名字。

【讨论】:

  • 我相信 Porter Duff 假设 所有 像素值使用预乘 alpha。如果您的来源或目的地没有,您需要一些额外的数学运算。没有预乘 alpha 的图像在现实世界中很常见。
  • 除非没有经过编码的 alpha 编码发射这样的东西。因此,为什么这些答案都是错误的。如果您查看未关联过度的数学运算,则第一部分会根据遮挡程度来缩放发射,以尝试复制正确编码的 alpha 编码。这也是为什么它不能表达反射/发射的原因。
  • 将 alpha 添加到 RGB 有两种常用方法 - alpha 可以独立于 RGB,也可以预乘。在这两种情况下,alpha 值保持不变,但 RGB 值不同。当您继续讨论不正确的答案时,我注意到您的答案没有包含 BG.Alpha 或为结果生成 alpha。失败。
  • 你需要阅读我写的内容。 RGB 总是 表示排放。尝试对“独立”RGB 进行操作。它不起作用。因为发射不代表发射和遮挡的程度。我发布的基础 Porter Duff 没有包含 BG,因为坦率地说,如果无法在这里掌握 alpha 的基本基本概念,我很确定用背景遮挡程度进一步混淆水域不会帮助任何人。
猜你喜欢
  • 2011-06-30
  • 2014-01-28
  • 2017-10-07
  • 1970-01-01
  • 2018-03-30
  • 2018-10-28
  • 1970-01-01
  • 1970-01-01
  • 2012-02-27
相关资源
最近更新 更多