I know there is formula for going RGB -> Luminance, but I need given a brightness parameter to modify the RGB values of an image. How do I do that?
Thanks
My recommendation would be the same as ChrisA.'s answer, with one difference:
Use the HSP color space instead, as it is an approximation of Photoshop's algorithm and has better results.
For the sake of not just linking to HSP's site (which frankly should be more than enough; it's just that I don't like to answer without examples), here is my C# implementation, which follows the site's:
#region Definitions
//Perceived brightness to Red ratio.
private const double Pr = .299;
//Perceived brightness to Green ratio.
private const double Pg = .587;
//Perceived brightness to Blue ratio.
private const double Pb = .114;
#endregion
//Expected ranges: Hue = 0-359... Other values = 0-1
public static ColorRGB ToRGB(double hue, double saturation, double perceivedBrightness, double alpha) {
//Check values within expected range
hue = hue < 0 ? 0 : hue > 359 ? 359 : hue;
saturation = saturation < 0 ? 0 : saturation > 1 ? 1 : saturation;
perceivedBrightness = perceivedBrightness < 0 ? 0 : perceivedBrightness > 1 ? 1 : perceivedBrightness;
alpha = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha;
//Conversion
var minOverMax = 1 - saturation;
double r, g, b;
if (minOverMax > 0) {
double part;
if (hue < 0.166666666666667D) { //R>G>B
hue = 6 * (hue - 0); part = 1 + hue * (1 / minOverMax - 1);
b = perceivedBrightness / Math.Sqrt(Pr / minOverMax / minOverMax + Pg * part * part + Pb);
r = b / minOverMax; g = b + hue * (r - b);
}
else if (hue < 0.333333333333333D) { //G>R>B
hue = 6 * (-hue + 0.333333333333333D); part = 1 + hue * (1 / minOverMax - 1);
b = perceivedBrightness / Math.Sqrt(Pg / minOverMax / minOverMax + Pr * part * part + Pb);
g = b / minOverMax; r = b + hue * (g - b);
}
else if (hue < 0.5D) { // G>B>R
hue = 6 * (hue - 0.333333333333333D); part = 1 + hue * (1 / minOverMax - 1);
r = perceivedBrightness / Math.Sqrt(Pg / minOverMax / minOverMax + Pb * part * part + Pr);
g = r / minOverMax; b = r + hue * (g - r);
}
else if (hue < 0.666666666666667D) { //B>G>R
hue = 6 * (-hue + 0.666666666666667D); part = 1 + hue * (1 / minOverMax - 1);
r = perceivedBrightness / Math.Sqrt(Pb / minOverMax / minOverMax + Pg * part * part + Pr);
b = r / minOverMax; g = r + hue * (b - r);
}
else if (hue < 0.833333333333333D) { //B>R>G
hue = 6 * (hue - 0.666666666666667D); part = 1 + hue * (1 / minOverMax - 1);
g = perceivedBrightness / Math.Sqrt(Pb / minOverMax / minOverMax + Pr * part * part + Pg);
b = g / minOverMax; r = g + hue * (b - g);
}
else { //R>B>G
hue = 6 * (-hue + 1D); part = 1 + hue * (1 / minOverMax - 1);
g = perceivedBrightness / Math.Sqrt(Pr / minOverMax / minOverMax + Pb * part * part + Pg);
r = g / minOverMax; b = g + hue * (r - g);
}
}
else {
if (hue < 0.166666666666667D) { //R>G>B
hue = 6 * (hue - 0D); r = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pr + Pg * hue * hue)); g = r * hue; b = 0;
}
else if (hue < 0.333333333333333D) { //G>R>B
hue = 6 * (-hue + 0.333333333333333D); g = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pg + Pr * hue * hue)); r = g * hue; b = 0;
}
else if (hue < 0.5D) { //G>B>R
hue = 6 * (hue - 0.333333333333333D); g = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pg + Pb * hue * hue)); b = g * hue; r = 0;
}
else if (hue < 0.666666666666667D) { //B>G>R
hue = 6 * (-hue + 0.666666666666667D); b = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pb + Pg * hue * hue)); g = b * hue; r = 0;
}
else if (hue < 0.833333333333333D) { //B>R>G
hue = 6 * (hue - 0.666666666666667D); b = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pb + Pr * hue * hue)); r = b * hue; g = 0;
}
else { //R>B>G
hue = 6 * (-hue + 1D); r = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pr + Pb * hue * hue)); b = r * hue; g = 0;
}
}
return new ColorRGB(r, g, b, alpha);
}
//Expected ranges: 0-1 on all components
public static ColorHSP FromRGB(double red, double green, double blue, double alpha) {
//Guarantee RGB values are in the correct ranges
red = red < 0 ? 0 : red > 1 ? 1 : red;
green = green < 0 ? 0 : green > 1 ? 1 : green;
blue = blue < 0 ? 0 : blue > 1 ? 1 : blue;
alpha = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha;
//Prepare & cache values for conversion
var max = MathExtensions.Max(red, green, blue);
var min = MathExtensions.Min(red, green, blue);
var delta = max - min;
double h, s, p = Math.Sqrt(0.299 * red + 0.587 * green + 0.114 * blue);
//Conversion
if (delta.Equals(0)) h = 0;
else if (max.Equals(red)) {
h = (green - blue) / delta % 6;
}
else if (max.Equals(green)) h = (blue - red) / delta + 2;
else h = (red - green) / delta + 4;
h *= 60;
if (h < 0) h += 360;
if (p.Equals(0))
s = 0;
else
s = delta / p;
//Result
return new ColorHSP(h, s, p, alpha);
}