What algorithm is behind the Color in Alpha Gimp?

For those who are not familiar with the Gimp Color to Alpha feature, here is a page on it from the Gimp documentation: Color in Alpha . This is a really good job, and I really wonder how exactly Gimp does it in terms of color manipulation, depending on what color space can be in color. Thanks for the blank tips.

EDIT 1: Generating transparency information for a pixel based on its similarity to the key color (the one you select in the Color to Alpha dialog box), as some people suggested, before deleting your answer for some reason, it will sound like good insight, but I believe it is more complex than that. Suppose we evaluate the similarity of color in the range of units from 0.0 to 1.0, and we have a pixel whose color, for example, is 0.4, for example, white (for example, you would choose white in β€œColor in Alpha”), and so the pixel gets an alpha value of 0.6, how would you change the actual color of the pixel to compensate for the decrease in brightness / brightness / saturation when the resulting pixel is displayed on a white background with alpha 0.6?

EDIT 2: Actually update: the question related to the first editing was answered in How to change alpha of a pixel without changing the resulting color? , but this is probably not a complete story, because what happens in the Gimp source for the Color in Alpha function is not so simple and, apparently, is based on a specific algorithm, not a formula.

+6
source share
4 answers

You need to come up with a mechanism to compare the similarity of colors. There are many color spaces in which you can do this. RGB is often not the best for this kind of thing. But you can use HSV, YCbCr or other brightness / color space. Often the distance in one of these spaces will give you a better answer than the Euclidean distance in RGB. Once you have a distance, you can divide it by the maximum distance to get a percentage. This percentage would be the inverse of the alpha you want to use as one of the possibilities.

If you want to know how GIMP does this, you can look at the source. For example, the last code change for this plugin has changed here .

+1
source

I took a look at the source code, and meat is a colortoalpha function. Parameters * a1 - * a4 - input / output red, green, blue and alpha, respectively, and c1 - c3 - color for alpha.

When you combine two colors c1 and c2 with a specific alpha-a (0 ≀ a ≀ 1), the result

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

Here we do the opposite operation: we know the final result y and the background color c2, and we want to find out c1 and a. Since this is an underdetermined equation, there are an infinite number of solutions. However, the ranges 0 ≀ c1 ≀ 255 and 0 ≀ a ≀ 1 are added to the solution.

How the Gimp plugin works, for each pixel it minimizes the alpha value (i.e. maximizes transparency). Conversely, this means that for each resulting pixel that is not completely transparent (i.e., not quite the background color), one of the RGB components is 0 or 255.

This creates an image that, when applied over the specified color, creates the original image (in the absence of rounding errors) and has maximum transparency for each pixel.

It is worth noting that the entire process is performed in the RGB color space, but can also be performed in others if the join operation is performed in the same color space.

+8
source

So, I looked at the GIMP source code ... ew! I made it general and readable. However, pretty fast. For an explanation of math, see Sampo's answer . Here's the C # implementation (easily converted to 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; } } } 

The GIMP implementation ( here ) uses the RGB color space, uses the alpha value as a float with a range from 0 to 1, and R, G, B as a float from 0 to 255.

An RGB implementation is inefficient when the image has JPEG artifacts because they mean slight perceptible color deviations, but rather significant absolute deviations of R, G, B. Using the LAB color space should do the trick for the case.

If you just want to remove a solid background from an image, a color alpha algorithm is not the best option. I got good results when I calculated the color space for each pixel using a LAB color. The calculated distance was then applied to the alpha channel of the original image. The main difference between this and the color in alpha is that the shades of the pixels will not be changed. Background deletion simply sets the alpha (opacity) to the color difference. It works well if the background color is not displayed in the foreground. If it cannot remove the background, or use the BFS algorithm to use only external pixels (something like using the magic wand selection in GIMP and then removing the selection).

The background cannot be removed if the foreground image has both holes and pixels in color similar to the background color. Such images require some manual processing.

+1
source

I translated the colortoalpha method from gimp to C # as much as I could. The problem is that RGBA values ​​are accepted as bytes for each channel in the library, such as ImageSharp . Some conversions lose data during the conversion, but I tried my best to save as much as I could. It uses ImageSharp to mutate the image. ImageSharp is fully managed, so it will run on different platforms. It is also fast. All these methods work in approximately ~ 10 ms (less than 10 ms).

Here is the code for implementing 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); } } } 
0
source

Source: https://habr.com/ru/post/908451/


All Articles