How do I find the RGBA value of a color from two RGB values? - colors

I have a hunch this has been done before but I am a total layman at this and don't know how to begin to ask the right question. So I will describe what I am trying to do...
I have an unknown ARGB color. I only know its absolute RGB value as displayed over two known opaque background colors, for example black 0x000000 and white 0xFFFFFF. So, to continue the example, if I know that the ARGB color is RGB 0x000080 equivalent when displayed over 0x000000 and I know that the same ARGB color is RGB 0x7F7FFF equivalent when displayed over 0xFFFFFF, is there a way to compute what the original ARGB color is?
Or is this even possible???

So, you know that putting (a,r,g,b) over (r1,g1,b1) gives you (R1,G1,B1) and that putting it over (r2,g2,b2) gives you (R2,G2,B2). In other words -- incidentally I'm going to work here in units where a ranges from 0 to 1 -- you know (1-a)r1+ar=R1, (1-a)r2+ar=R2, etc. Take those two and subtract: you get (1-a)(r1-r2)=R1-R2 and hence a=1-(R1-R2)/(r1-r2). Once you know a, you can work everything else out.
You should actually compute the values of a you get from doing that calculation on all three of {R,G,B} and average them or something, to reduce the effects of roundoff error. In fact I'd recommend that you take a = 1 - [(R1-R2)sign(r1-r2) + (G1-G2)sign(g1-g2) + (B1-B2)sign(b1-b2)] / (|r1-r2|+|g1-g2|+|b1-b2), which amounts to weighting the more reliable colours more highly.
Now you have, e.g., r = (R1-(1-a)r1)/a = (R2-(1-a)r2)/a. These two would be equal if you had infinite-precision values for a,r,g,b, but of course in practice they may differ slightly. Average them: r = [(R1+R2)-(1-a)(r1+r2)]/2a.
If your value of a happens to be very small then you'll get only rather unreliable information about r,g,b. (In the limit where a=0 you'll get no information at all, and there's obviously nothing you can do about that.) It's possible that you may get numbers outside the range 0..255, in which case I don't think you can do better than just clipping.
Here's how it works out for your particular example. (r1,g1,b1)=(0,0,0); (r2,g2,b2)=(255,255,255); (R1,G1,B1)=(0,0,128); (R2,G2,B2)=(127,127,255). So a = 1 - [127+127+127]/[255+255+255] = 128/255, which happens to be one of the 256 actually-possible values of a. (If it weren't, we should probably round it at this stage.)
Now r = (127-255*127/255)*255/256 = 0; likewise g = 0; and b = (383-255*127/255)*255/256 = 255.
So our ARGB colour was 80,00,00,FF.

Choosing black and white as the background colors is the best choice, both for ease of calculation and accuracy of result. With lots of abuse of notation....
a(RGB) + (1-a)0xFFFFFF = 0x7F7FFF
a(RGB) + (1-a)0x000000 = 0x000080
Subtracting the second from the first...
(1-a)0xFFFFFF = 0x7F7FFF-0x000080 = 0x7F7F7F
So
(1-a) = 0x7F/0xFF
a = (0xFF-0x7F)/0xFF = 0x80/0xFF
A = 0x80
and RGB = (a(RGB))/a = 0x000080/a = 0x0000FF
You can do something very similar with other choices of background color. The smaller a is and the closer the two background colors are the less accurately you will be able to determine the RGBA value. Consider the extreme cases where A=0 or where the two background colors are the same.

Related

Same come different Pixels in Processing

I am working in Processing and I would like to compare the color of 2 the pixels of 2 different images.
let's say we comparing the pixel in position 10
color c1= image1.pixels[10]; color c2= image2.pixels[10];
if(c1==c2) { //so something }
Firstly I was playing with brightnsess
if(brightness(c1)==brightness(c2))
Generally it was working but not exactly as I wanted as the pixels were a little bit similar but not exactly the same color.
if you want to compare colours you are probably better off comparing the three basic ones instead of the actual number that "color" is. Thus instead of
if(c1 == c2)
where you compare two large numbers like 13314249 you can go
if(red(c1) == red(c2) && green(c1) == green(c2) && blue(c1) == blue(c2))
where you compare numbers from 0 - 255, the possible values of red or green or blue you can get from a colour. As for the "little bit similar" colours, you can set a threshold and any difference below that threshold will be considered negligible thus the colours are the same. Something like this:
int threshold = 5
if(abs(red(c1) red(c2)) < threshold && abs(green(c1) - green(c2)) < threshold && abs(blue(c1) == blue(c2)) < threshold)
Remember, you have to take the absolute difference! This way, if you decrease the threshold only very similar colours are considered the same while is you increase it different colours can be considered the same. That threshold number depends on your likings!
This would also work with your brightness example...
int threshold = 5
if(abs(brightness(c1) - brightness(c2)) < threshold)
To extend on Petros's answer. Generally, when I am comparing image pixels, I normalize, so that the code will work with images that are not in standard range 0-255. It also is good when you are doing many operations on the images to keep in mind the range you are currently working with for scaling purposes.
MAX_PIXEL=255 //maybe range is different for some reason
MIN_PIXEL=0
pixel_difference = 10
threshold = pixel_difference/(MAX_PIXEL-MIN_PIXEL)
if ( abs( (brightness(c1)-brightness(c2))/(MAX_PIXEL-MIN_PIXEL))< threshold ) {
//then the pixels are similar.
}
Sometimes you can gain more ground by transforming to a difference color space.
And depending on your task at hand you can build a background model that can adapt over time or compare higher level global features such as histograms or local features such as Scale Invariant Feature Transform (SIFT), or Corners, Edges.

Given the RGB components of a color, how can I decide if it is perceived as gray by humans?

One simple way is to say that when the RGB components are equal, they form a gray color.
However, this is not the whole story, because if they only have a slight difference, they will still look gray.
Assuming the viewer has a healthy vision of color, how can I decide if the given values would be perceived as gray (presumably with an adjustable threshold level for "grayness")?
A relatively straightforward method would be to convert RGB value to HSV color space and use threshold on the saturation component, e.g. "if saturation < 0.05 then 'almost grey', else not grey".
Saturation is actually the "grayness/colorfulness" by definition.
This method is much more accurate than using differences between R, G and B channels (since human eye perceives saturation differently on light and dark colors). On the other hand, converting RGB to HSV is computationally intensive. It is up to you to decide what is of more value - precise answer (grey/not grey) or performance.
If you need an even more precise method, you may use L*a*b* color space and compute chroma as sqrt(a*a + b*b) (see here), and then apply thresholding to this value. However, this would be even more computationally intensive.
You can also combine multiple methods:
Calculate simple differences between R, G, B components. If the color can be identified as definitely desaturated (e.g. max(abs(R-G), abs(R-B), abs(G-B)) <= 5) or definitely saturated (e.g. max(abs(R-G), abs(R-B), abs(G-B)) > 100), then stop.
Otherwise, convert to L*a*b*, compute chroma as sqrt(a*a + b*b) and use thresholding on this value.
r = 160;
g = 179;
b = 151;
tolerance = 20;
if (Math.abs(r-g) < 20 && Math.abs(r-b) < 20) {
#then perceived as gray
}

How to calculate Hue, Saturation and Lightness values from a chosen color by input distance

Given a starting hex code, I would like to know the maths to calculate the linear values of lightness in ascending and descending order. Same for Hue and Saturation.
It's kinda difficult for me to describe exactly what i want, forutnately i've found this page which make use of the exact algorithms i need:
http://www.workwithcolor.com/hsl-color-schemer-01.htm
If you checked the page you noticed that the last 3 redio buttons read: Linear by Hue, Linear by Saturation, Linear by Lightness. Each, gives you a list of hex codes in ascending order that correspond to the original hex code.
For example, for the lightness they give the following list (from color FFCE2E):
FFCE2E FFDA61 FFE694 FFF2C7 FFFEFA
I need the formulas, please.
Thanks in advance.
You can mash this up from multiple places. In a nutshell you need:
The HSL value of your picked color. Maybe this is obtained by converting an RGB to HSL (How do you get the hue of a #xxxxxx colour?) or on the website you just pick it on a palette
Now you have the 3 component (H, S, and L) and depending on which checkbox you choose, you start to decrement the component by the % value given in the edit box.
You'll obtain a list of values during this decrement and you'll now do a reverse conversion from the HSL value to the RGB (HSL to RGB color conversion).
// I gonna use rgbToHsl and hslToRgb from https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
var initialRGB = [ir, ig, ib];
var initialHSL = rgbToHsl(initialRGB[0], initialRGB[1], initialRGB[2]);
var howManyVariants = 4;
var decrementPercent = 0.1; // 10%
// This example is for hue change
var decrement = initialHSL[0] * decrementPercent;
for (var i = 0; i < howManyVariants; i++) {
// Linear decrementation
var nextHue = initialHSL[0] - i * decrement;
var nextColor = hslToRgb(nextHue, initialHSL[1], initialHSL[2]);
// visualize somehow
}
Similarly, if you want to have a set of variation by saturation then you decrement only the second parameter/component, and if you want vary luminescence, you vary the 3rd parameter.
Hope this is clear.

How to alter brightness of a single rgb color simply and easily via php?

A quesion about RGB color and finding the simplest, tiniest, php conversion code for manipulating the lightness/darkness of a given RGB hue.
Imagine a variable $colorA containning a valid six char RGB color, like F7A100 which we want to make a bit lighter and/or darker:
$color = B1B100; // original RGB color manually set.
Then, at any page have that color bit darker/lighter on the fly:
$colorX = someFunction($color, +10); // original color 10 steps lighter
$colorY = someFunction($color, -25); // original color 25 steps darker
What would be YOUR way of solving this? Keep the RGB as is or first change it to HSL? Hints and suggestions are welcome. Your sample/code is welcome too.
This really focuses to the TINIES / SIMPLES / SHORTEST possible code to just make the same hue bit darker/lighter.
I deliberately do not suggest my code, as I want to keep possibilities open in here.
The absolutely simplest solution is to add some constant (like 1) to each part of the color representation: [R, G, B]. This is due to the fact that max values of all [R, G, B] represent white, while min values - black. In pseudo-code (assuming 255 is max, sorry, I don't know PHP):
lighter(R, G, B) = [
min(255, R + 1),
min(255, G + 1),
min(255, B + 1)
]
You must keep in mind though that this transformation is way too simplistic and the proper implementation would be to convert to HSL/HSB, increase H and transform back to RGB.
For slight alteration of brightness you can convert the hexadecimal values to decimal, manipulate them and convert back to hexadecimal like this:
function alterBrightness($color, $amount) {
$rgb = hexdec($color); // convert color to decimal value
//extract color values:
$red = $rgb >> 16;
$green = ($rgb >> 8) & 0xFF;
$blue = $rgb & 0xFF;
//manipulate and convert back to hexadecimal
return dechex(($red + $amount) << 16 | ($green + $amount) << 8 | ($blue + $amount));
}
echo alterColor('eeeeee', -10); //outputs e4e4e4
Beware that this code does not handle overflow for one color - if one color value becomes less than 0 or more than 255 you will get an invalid color value. This should be easy enough to add.
For drastic changes in brightness, convert to HSL and manipulate the lightness.
Using the functions from the Drupal code, this can be done like this:
$hsl = _color_rgb2hsl(_color_unpack('eeeeee'));
$hsl[2] -= 10;
$rgb = _color_pack(_color_hsl2rgb($hsl));
echo $rgb; //outputs e4e4e4

Distance measure between HSL colours

I am coding a program that allows a user to choose various foreground and background colours in RGB. I want to not allow them to chose foreground and backgrounds that are too similar and decided to convert to HSL and use HSL euclidean distance as a way to check for similarity.
Is there a good weighting to use for HSL space (rather than equal weighting for H, S and L)? I've looked at various sites and not found the exact thing I need; just things saying that HSL or HSB is better than RGB.
first convert the colors to Lab. This colorspace is designed so that the vectorial difference between any two colors closely approximate a 'subjective distance'.
In color management, a 'delta E' value is given as a measure of how perceptually faithful a given color transformation is. it's just the magnitude of the vector difference between original and final colors as expressed in Lab space.
My advice would be to skip HSL/HSB entirely, and go directly from RGB to LAB. Once you've done that, you can do a standard delta E computation.
I don't have exact figures for you, but I'd use a much higher weight for L than H or S. The eye is bad at discriminating between equal colors of different saturation, and nearly as bad at distinguishing different hues - expecially if it's fine detail you're trying to see, like text.
I just concluded an interesting study into color spaces. As others mentioned here, converting RGB to CIE-Lab and doing a Delta E computation will give you perceptual color distance. It produces okay results.
My goal was to find the closest index in a limited color palette. However, I found using CIE-Lab Delta E calculations ended up with "wrong" colors. Particularly grayscale would wind up getting too much saturation and select a red instead of a gray from the palette but other colors had issues too (I don't remember which ones). For better or worse, I wound up weighting hues at a 1.2x multiplier, saturation at 1.5x, and B values at either 1.0x or 2.0x depending on the direction. The results more or less work out better than just Delta E alone.
Calculating the distance of Hue is a bit tricky since it is a circle. For example, Hue 0 and Hue 359 are a distance of 1. The solution is to select the minimum of two different distances.
Here's my code based on the above:
// Finds the nearest color index in a RGB palette that matches the requested color.
// This function uses HSB instead of CIE-Lab since this function is intended to be called after GetReadableTextForegroundColors() and results in more consistent color accuracy.
public static function FindNearestPaletteColorIndex($palette, $r, $g, $b)
{
$hsb1 = self::ConvertRGBToHSB($r, $g, $b);
$result = false;
$founddist = false;
foreach ($palette as $key => $rgb)
{
$rgb = array_values($rgb);
$r = $rgb[0];
$g = $rgb[1];
$b = $rgb[2];
$hsb2 = self::ConvertRGBToHSB($r, $g, $b);
$hdiff = min(abs($hsb1["h"] - $hsb2["h"]), abs($hsb1["h"] - $hsb2["h"] + ($hsb1["h"] < $hsb2["h"] ? -360.0 : 360.0))) * 1.2;
$sdiff = ($hsb1["s"] - $hsb2["s"]) * 1.5;
$bdiff = $hsb1["b"] - $hsb2["b"];
if ($hsb1["b"] < $hsb2["b"]) $bdiff *= 2.0;
$hdiff *= $hdiff;
$sdiff *= $sdiff;
$bdiff *= $bdiff;
$dist = $hdiff + $sdiff + $bdiff;
if ($result === false || $founddist >= $dist)
{
$result = $key;
$founddist = $dist;
}
}
return $result;
}
Source: https://github.com/cubiclesoft/php-misc/blob/master/support/color_tools.php
Converting the above to use HSL instead of HSB/HSV shouldn't be too difficult. I prefer the HSB color space since it mirrors Photoshop, which allows me to confirm the numbers I'm looking for in software.

Resources