Gimp的“Color to Alpha”功能背后有什么算法?

时间:2012-02-14 16:56:05

标签: image-processing graphics raster-graphics

对于那些不熟悉Gimp的“Color to Alpha”功能的人来说,这里是Gimp文档中的页面:Color to Alpha。它确实做得非常好,我很清楚Gimp在颜色处理方面是如何做到的,无论颜色可能是哪个颜色空间。感谢一堆提示。

编辑1:根据像素与关键颜色(您在“颜色到Alpha”对话框中选择的颜色)的相似性生成像素的透明度信息,就像一些人在删除答案之前所建议的那样出于某种原因,听起来像是一个很好的洞察力,但我认为它比那更复杂。让我们假设我们估计单位范围内的颜色相似度从0.0到1.0,我们有一个像素的颜色,例如,0.4,类似于白色的颜色(就像你在“颜色到”中选择白色一样) Alpha“对话框”,因此像素的alpha值为0.6,那么当结果像素显示在具有0.6的alpha的白色背景时,你将如何改变像素的实际颜色以补偿亮度/亮度/饱和度的松散?

编辑2:实际上是更新:与第一次编辑相关的子问题已在How to change the alpha of a pixel without changing the resulting color?中得到解答,但这可能不是完整的故事,因为Gimp的内容正在发生“颜色到Alpha”功能的来源并不那么简单,似乎是基于特定的算法而不是公式。

5 个答案:

答案 0 :(得分:8)

我看了一下源代码,它的主要功能是colortoalpha功能。参数* a1到* a4分别是输入/输出红色,绿色,蓝色和alpha,c1到c3是制作alpha的颜色。

当您将两种颜色c1和c2与特定的alpha a(0≤a≤1)组合时,结果为

y = a * c1 + (1-a) * c2

这里我们正在进行相反的操作:我们知道最终结果y和背景颜色c2,并想知道c1和a。由于这是一个指定不足的等式,因此存在无限量的解。但是,范围0≤c1≤255且0≤a≤1会增加解的边界。

Gimp插件的工作方式是每个像素最小化alpha值(即最大化透明度)。相反,这意味着对于每个不完全透明的结果像素(即不完全是背景颜色),其中一个RGB分量为0或255.

这会生成一个图像,当叠加在指定颜色的顶部时,将生成原始图像(没有舍入误差)并且每个像素具有最大透明度。

值得注意的是,整个过程是在RGB颜色空间中完成的,但也可以在其他颜色空间中执行,只要组合操作在相同的颜色空间中完成。

答案 1 :(得分:1)

您需要提出一种比较颜色相似性的机制。您可以使用各种颜色空间。 RGB通常不是这种东西的最佳选择。但您可以使用HSV,YCbCr或其他亮度/色度空间。通常,其中一个空间中的距离将比RGB中的欧几里德距离给出更好的答案。一旦你有一个距离,你可以将它除以最大距离得到一个百分比。作为一种可能性,该百分比将是您想要使用的alpha的倒数。

如果您想知道GIMP是如何做到的,您可以查看来源。例如,here's one recent code change到该插件。

答案 2 :(得分:1)

所以我调查了GIMP source code ......哇!我把它变得通用和可读。 虽然还很快。 有关数学说明,请参阅Sampo's answer。 这是C#实现(易于转换为C / C ++):

static class PixelShaders {

    /// <summary>
    /// Generic color space color to alpha.
    /// </summary>
    /// <param name="pA">Pixel alpha.</param>
    /// <param name="p1">Pixel 1st channel.</param>
    /// <param name="p2">Pixel 2nd channel.</param>
    /// <param name="p3">Pixel 3rd channel.</param>
    /// <param name="r1">Reference 1st channel.</param>
    /// <param name="r2">Reference 2nd channel.</param>
    /// <param name="r3">Reference 3rd channel.</param>
    /// <param name="mA">Maximum alpha value.</param>
    /// <param name="mX">Maximum channel value.</param>
    static void GColorToAlpha(ref double pA, ref double p1, ref double p2, ref double p3, double r1, double r2, double r3, double mA = 1.0, double mX = 1.0) {
        double aA, a1, a2, a3;
        // a1 calculation: minimal alpha giving r1 from p1
        if (p1 > r1) a1 = mA * (p1 - r1) / (mX - r1);
        else if (p1 < r1) a1 = mA * (r1 - p1) / r1;
        else a1 = 0.0;
        // a2 calculation: minimal alpha giving r2 from p2
        if (p2 > r2) a2 = mA * (p2 - r2) / (mX - r2);
        else if (p2 < r2) a2 = mA * (r2 - p2) / r2;
        else a2 = 0.0;
        // a3 calculation: minimal alpha giving r3 from p3
        if (p3 > r3) a3 = mA * (p3 - r3) / (mX - r3);
        else if (p3 < r3) a3 = mA * (r3 - p3) / r3;
        else a3 = 0.0;
        // aA calculation: max(a1, a2, a3)
        aA = a1;
        if (a2 > aA) aA = a2;
        if (a3 > aA) aA = a3;
        // apply aA to pixel:
        if (aA >= mA / mX) {
            pA = aA * pA / mA;
            p1 = mA * (p1 - r1) / aA + r1;
            p2 = mA * (p2 - r2) / aA + r2;
            p3 = mA * (p3 - r3) / aA + r3;
        } else {
            pA = 0;
            p1 = 0;
            p2 = 0;
            p3 = 0;
        }
    }

}

GIMP的实现(here)使用RGB颜色空间,将alpha值用作float,范围为0到1,R,G,B为float,范围为0到255。 / p> 当图像具有JPEG伪影时,RGB实现失败,因为它们意味着无法察觉的颜色偏差,但是相当显着的绝对R,G,B偏差。使用LAB色彩空间应该可以解决这个问题。

如果您只想从图像中删除纯色背景,则颜色到alpha算法不是最佳选择。当使用LAB色彩空间计算每个像素的色彩空间距离时,我得到了很好的结果。然后将计算的距离应用于原始图像的alpha通道。这与alpha的颜色之间的主要区别是像素的色调不会改变。背景删除只是将alpha(不透明度)设置为颜色空间差异。如果前景图像中没有出现背景颜色,则效果很好。如果它确实无法移除背景,或者必须使用BFS算法来遍历外部像素(例如在GIMP中使用魔棒选择,然后删除选择)。

如果前景图像同时具有与背景颜色相似的孔和像素,则无法删除背景。这些图像需要一些手动处理。

答案 3 :(得分:0)

我尽我所能将colortoalpha方法从gimp转换为C#。问题在于,像ImageSharp这样的库中,每个通道的RGBA值都以字节为单位。某些转换在转换过程中丢失了数据,但我尽力保留了尽可能多的数据。这使用ImageSharp进行图像突变。 ImageSharp受到完全管理,因此可以跨平台使用。它也很快。整个方法大约需要10毫秒(少于10毫秒)的时间。

这是C#实现的代码:

public static unsafe void ColorToAlpha(this Image<Rgba32> image, Rgba32 color)
    {
        double alpha1, alpha2, alpha3, alpha4;
        double* a1, a2, a3, a4;

        a1 = &alpha1;
        a2 = &alpha2;
        a3 = &alpha3;
        a4 = &alpha4;

        for (int j = 0; j < image.Height; j++)
        {
            var span = image.GetPixelRowSpan(j);

            for (int i = 0; i < span.Length; i++)
            {
                ref Rgba32 pixel = ref span[i];

                // Don't know what this is for
                // *a4 = pixel.A;

                if (pixel.R > color.R)
                    *a1 = (pixel.R - color.R) / (255.0 - color.R);
                else if (pixel.R < color.R)
                    *a1 = (color.R - pixel.R) / color.R;
                else
                    *a1 = 0.0;

                if (pixel.G > color.G)
                    *a2 = (pixel.G - color.G) / (255.0 - color.G);
                else if (pixel.G < color.G)
                    *a2 = (color.G - pixel.G) / color.G;
                else
                    *a2 = 0.0;

                if (pixel.B > color.B)
                    *a3 = (pixel.B - color.B) / (255.0 - color.B);
                else if (pixel.B < color.B)
                    *a3 = (color.B - pixel.B) / color.B;
                else
                    *a3 = 0.0;

                if (*a1 > *a2)
                    *a4 = *a1 > *a3 ? *a1 * 255.0 : *a3 * 255.0;
                else
                    *a4 = *a2 > *a3 ? *a2 * 255.0 : *a3 * 255.0;

                if (*a4 < 1.0)
                    return;

                pixel.R = (byte)Math.Truncate((255.0 * (*a1 - color.R) / *a4 + color.R));
                pixel.G = (byte)Math.Truncate((255.0 * (*a2 - color.G) / *a4 + color.G));
                pixel.B = (byte)Math.Truncate((255.0 * (*a3 - color.B) / *a4 + color.B));

                pixel.A = (byte)Math.Truncate(*a4);
            }
        }
    }

答案 4 :(得分:0)

我在“过滤器-其他-颜色转换为Alpha”下的编辑器www.Photopea.com中实现了此过滤器。结果与GIMP 100%相同。该算法极其简单

想法:使用透明度值A将背景色B与前景色F组合在一起,以获得新的颜色N:

N = A * F  +  (1 - A) * B;

您知道N(图像中的实际颜色)和B(滤镜的参数),并且想要恢复前景色F及其透明度A。

这样做:

A = max( abs(N.r - B.r), abs(N.g - B.g), abs(N.b - B.b)  )  

现在,您知道N,B,A。只需使用上面的公式来计算F。