对于那些不熟悉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”功能的来源并不那么简单,似乎是基于特定的算法而不是公式。
答案 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。