
Comments and Discussions



First of all, great stuff. it realy helped me on my philps hue light project
i did notice a small issue while testing,
if reg & green & blue are all the same value
the code will return new HSB(NaN, NaN, max);
since it will cause a 0/0
to fix this, you can do a
if(r == g && g == b) return new HSB(0, 0, max);





When trying to use ColorHelper.GetBlendColor, the R, G, & B values return 1. I believe your code uses values between 0 and 1, when the color object in VB.net (4.5) uses values between 0 and 255.
Here is my debug info
color1 = {Color [A=255, R=238, G=251, B=255]}
color2 = {Color [A=255, R=153, G=232, B=255]}
blended color = {Color [A=255, R=1, G=1, B=1]}





Hello Guillaume,
thank you for your comprehensive insight into the various models for displaying color(s).
You seem to be the right person to ask how to determine the average brightness of a grayscale image (sRGB)?
To begin we have a linear RAW image (Nikon NEF), 16bit, sRGB, gamma 0.454545. This image contains the same as preview image 570 x 375 pixels, 8bit, sRGB, gamma 0.454545. That preview image gets extracted and converted to grayscale (rec709luma).
How can the average or overall brightness of that preview image be determined?
Hope this request will ever reach you.
Looking forward to receiving your reply
With kind regards, Hans.





I work in the graphics & printing business with a technical job and this is the best explanation of colour models for computers!
5 out of 5!





Hi, Gr8 article & code.
I am trying to work out by using Saturation and Luminance whether an image has been taken at night time or day time. When i have a lot of white light from say a large moth close to a camera the luminance (overall) goes up making my code thinks it is daytime. Is HSL the best color model to use in this case? Should I look at another one to use?
Thanks





public static RGB HSBtoRGB(double h, double s, double b)
{ ^
double r = 0; 
double g = 0; 
double b = 0; <one variable for two different parameters
.......
and so on
modified 17Mar13 8:13am.





Great article!
Of course when converting to CMYK, there is always an extra degree of freedom, mathematically speaking. In your RGB to CMYK formula you show the normal assumption that K is min(r,g,b), but people should be aware that there is "more than one right answer". The mathematical result may not correspond to the values from some software used to make the color separation plates used in offset printing press setup, for example. Some graphic designers may prefer always having "more K" to get what they perceive to be a "better" result.






I love this project, as it has saved me a lot of time.
I apologize if someone else has already posted this but I had a need to see the other values with the HEX as the source so I added a button and the following code:
private void button1_Click(object sender, EventArgs e)
{
Color ConvertedColor = ColorTranslator.FromHtml(hexBox.Text);
redUD.Value = ConvertedColor.R;
greenUD.Value = ConvertedColor.G;
blueUD.Value = ConvertedColor.B;
using (StreamWriter sw = File.AppendText("ColorConversion.txt"))
{
sw.WriteLine("Color Conversion  " + DateTime.Now.ToString());
sw.WriteLine("HEX: " + hexBox.Text);
sw.WriteLine("RGB: " + redUD.Value.ToString() + "," + greenUD.Value.ToString() + "," + blueUD.Value.ToString());
sw.WriteLine("HSL: " + hueUD.Value.ToString() + "," + satUD.Value.ToString() + "," + lumUD.Value.ToString());
sw.WriteLine("HSB: " + hUD.Value.ToString() + "," + sUD.Value.ToString() + "," + bUD.Value.ToString());
sw.WriteLine("CMYK: " + cyanUD.Value.ToString() + "," + magentaUD.Value.ToString() + "," + yellowUD.Value.ToString() + "," + blackUD.Value.ToString());
sw.WriteLine("YUV: " + yUD.Value.ToString() + "," + uUD.Value.ToString() + "," + vUD.Value.ToString());
}
}
The content of the text file looks like this
Color Conversion  10/30/2012 1:32:11 PM
HEX: #c47233
RGB: 196,114,51
HSL: 26,59,48
HSB: 26,74,77
CMYK: 4,44,75,20
YUV: 52,28,84
As the RGB has the update process setup based on changed values this will update the rest of the values as if I manually set the RGB.
Please feel free to use/improve my code.
David Kittell
Kittell.net





If you need to convert from standard aRGB Color class in .net, you should notice that Alpha component from this is not an alpha component from sRGB standard.
Killed a whole evening to realize this
To deal with Alpha in Color class you should convert it to simple RGB removing background "alpha" component first.
public static Color RemoveAlpha(Color argbColor, Color background)
{
if (argbColor.A == 255)
return argbColor;
var alpha = argbColor.A / 255.0;
var diff = 1.0  alpha;
return Color.FromArgb(255,
(byte)(argbColor.R * alpha + background.R * diff),
(byte)(argbColor.G * alpha + background.G * diff),
(byte)(argbColor.B * alpha + background.B * diff));
}
modified 6Aug12 13:10pm.





Fantastic work. Thank you thank you!





I could never have figured out CIELAB by myself.





For a research topic I need to do conversations between some of these color spaces. I'm glad I had your code available to me so I didn't have to retread ground already covered. This saved me a ton of time. Thank you so much.





Very useful. Thanks a million.






He has celarly explained a great number of very complex color systems converions.





Clinear[1] = x*0.9692 + y*1.8760  z*0.0416; // green
should be Clinear[1] = x*0.9692 + y*1.8760 + z*0.0416; // green





Hello,
I'm trying to create a color generator for a graphic control where there could be any number a plots drawn.
For each new plot, I want to have the most different color possible while keeping existing plot color.
I took a look at ColorSpaceHelper:GetColorDistance and see that you do a simple calc. But my readings give me the impression that it is better to use CIELab to get a good color space of human perception of color (and a more accurate distance between colors). I wonder if it is not better to calc distance based on CIELab color and if so, how I can achieved that with you library if possible ?
Thanks,
Eric





Here is the code for CIE 2000 color distance:
public static float ColorDifference(CIELab lab1, CIELab lab2)
{
double p25 = Math.Pow(25, 7);
double C1 = Math.Sqrt(lab1.A * lab1.A + lab1.B * lab1.B);
double C2 = Math.Sqrt(lab2.A * lab2.A + lab2.B * lab2.B);
double avgC = (C1 + C2) / 2F;
double powAvgC = Math.Pow(avgC, 7);
double G = (1  Math.Sqrt(powAvgC / (powAvgC + p25))) / 2D;
double a_1 = lab1.A * (1 + G);
double a_2 = lab2.A * (1 + G);
double C_1 = Math.Sqrt(a_1 * a_1 + lab1.B * lab1.B);
double C_2 = Math.Sqrt(a_2 * a_2 + lab2.B * lab2.B);
double avgC_ = (C_1 + C_2) / 2D;
double h1 = (Atan(lab1.B, a_1) >= 0 ? Atan(lab1.B, a_1) : Atan(lab1.B, a_1) + 360F);
double h2 = (Atan(lab2.B, a_2) >= 0 ? Atan(lab2.B, a_2) : Atan(lab2.B, a_2) + 360F);
double H = (h1  h2 > 180D ? (h1 + h2 + 360F) / 2D : (h1 + h2) / 2D);
double T = 1;
T = 0.17 * Cos(H  30);
T += 0.24 * Cos(2 * H);
T += 0.32 * Cos(3 * H + 6);
T = 0.20 * Cos(4 * H  63);
double deltah = 0;
if (h2  h1 <= 180)
deltah = h2  h1;
else if (h2 <= h1)
deltah = h2  h1 + 360;
else
deltah = h2  h1  360;
double avgL = (lab1.L + lab2.L) / 2F;
double deltaL_ = lab2.L  lab1.L;
double deltaC_ = C_2  C_1;
double deltaH_ = 2 * Math.Sqrt(C_1 * C_2) * Sin(deltah / 2);
double SL = 1 + (0.015 * Math.Pow(avgL  50, 2)) / Math.Sqrt(20 + Math.Pow(avgL  50, 2));
double SC = 1 + 0.045 * avgC_;
double SH = 1 + 0.015 * avgC_ * T;
double exp = Math.Pow((H  275) / 25, 2);
double teta = Math.Pow(30, exp);
double RC = 2D * Math.Sqrt(Math.Pow(avgC_, 7) / (Math.Pow(avgC_, 7) + p25));
double RT = RC * Sin(2 * teta);
double deltaE = 0;
deltaE = Math.Pow(deltaL_ / SL, 2);
deltaE += Math.Pow(deltaC_ / SC, 2);
deltaE += Math.Pow(deltaH_ / SH, 2);
deltaE += RT * (deltaC_ / SC) * (deltaH_ / SH);
deltaE = Math.Sqrt(deltaE);
return (float)deltaE;
}
private static double Atan(double y, double x)
{
return Math.Atan2(y, x) * 180D / Math.PI;
}
private static double Cos(double d)
{
return Math.Cos(d * Math.PI / 180);
}
private static double Sin(double d)
{
return Math.Sin(d * Math.PI / 180);
}





Thanks a lot Rob,
In fact I found an algorithm and translated in c# but I think is is based on "Delta E 1994".
Are you aware if your algorithm is better and if the changes are significant ?
This is the algo I have found/used in my post:
Random Color Generator
public static double GetDistanceBetween(CIELab lab1, CIELab lab2)
{
const double whtL = 1;
const double whtC = 1;
const double whtH = 1;
double xC1 = Math.Sqrt(Math.Pow(lab1.a, 2.0) + Math.Pow(lab1.b, 2.0));
double xC2 = Math.Sqrt(Math.Pow(lab2.a, 2.0) + Math.Pow(lab2.b, 2.0));
double xDL = lab2.l  lab1.l;
double xDC = xC2  xC1;
double xDE = Math.Sqrt(
((lab1.l  lab2.l) * (lab1.l  lab2.l))
+ ((lab1.a  lab2.a) * (lab1.a  lab2.a))
+ ((lab1.b  lab2.b) * (lab1.b  lab2.b)));
double xDH;
if (Math.Sqrt( xDE ) > ( Math.Sqrt( Math.Abs( xDL ) ) + Math.Sqrt( Math.Abs( xDC ) ) ) )
{
xDH = Math.Sqrt((xDE*xDE)  (xDL*xDL)  (xDC*xDC));
}
else
{
xDH = 0;
}
double xSC = 1 + (0.045*xC1);
double xSH = 1 + (0.015*xC1);
xDL /= whtL;
xDC /= whtC*xSC;
xDH /= whtH*xSH;
double deltaE94 = Math.Sqrt(Math.Pow(xDL, 2.0) + Math.Pow(xDC, 2.0) + Math.Pow(xDH, 2.0));
return deltaE94;
}





I implemented it and tested it in my little "Tips and tricks" (Random Color Generator[^]).
It seems to work very fine. I suspect that it is better than what I have used Cie1994. I will update my article to use you algorithm.
Thanks a lot !!!
Éric





Eric,
I have not tested the "older" CIE standards, just read in Wikipedia about corrections from one version to the other and coded using the step by step formulas from:
http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
Somewhere on Bruce webpage is a color distance calculator, there you can test the different CIE standards.
I just saw in my code that there are some float identifiers. Just change 'F' to 'D' then all is consistent.
Rob





Thanks a lot.
It's already updated on my "Tips and Tricks".
I updated the code to use your function (and also changed float to double).
I kept my old code as reference if anybody would want to use it or test with it.
Thanks a lot !
Eric





For any color newbz like myself, the difference between black and white, which I assume is the greatest distance between colors, comes out to 100.0 with this code. Thanks again Rob!






Hi  I have a quick question. Is there a way we can load an image and do a color separation dynamically?
e.g. I might have check boxes for CMYK on the left and the image on the right and choosing any combination of colors, the respective image shows on the right. Is this possible?
Also, we come across Pantone colors. How are these handled?





When I execute the following code the RGB values come back different than the original, why?
CIELab lab = ColorSpaceHelper.RGBtoLab(120, 193, 231);
RGB rgb = ColorSpaceHelper.LabtoRGB(lab);
The r g b values returned are r=128 g=186 b=233. I don't understand why they're not 120,193,231. Is this a rounding issue? or just something funky with converting color spaces?





Hi Darren,
Actually this issue is up to a conversion error in ColorSpaceHelper, in XYZtoRGB() method.
I was using a different gamma correction when I convert from and to CIE XYZ (to convert to a CIE Lab, we first convert to CIE XYZ).
To fix this issue, even if it's not perfect, you'll need to use the same gamma correction.
Change line 1379 in ColorSpaceHelper with :
Clinear[i] = (Clinear[i]<=0.0031308)? 12.92*Clinear[i] : (1+0.055)* Math.Pow(Clinear[i], (1.0/2.2))  0.055;
The correct correction is the one in bold, which is 2.2.
It won't fix rounding issues, but will let you have a more consistant conversion.
I'm currently working on a update of this lib. It's not for tomorrow but, it'll fix every weird things you may have found (+ many new things)
Cheers,
Guillaume





Hi Guillaume,
I noted that you are using D65 illuminant tristimulus and the corresponding matrix to convert CIE Lab <> CIE XYZ <> RGB.
Your formulas are correct, apart from method RGBtoXYZ(). It should read Math.Pow((... + 0.055)/(1 + 0.055), 2.4) !!
Please see here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
I have programmed a similar library to read Adobe color book (acb) files. Adobe color books contain CIE Lab color spaces.
After a bit playing a D50 illuminant tristimulus and a Bradfordadapted D50 matrix provide for a spot on conversion from Lab to sRGB.
For your convenience here are the matrices:
D50 Bradfordadapted RGB to XYZ [M]
0.4360747 0.3850649 0.1430804
0.2225045 0.7168786 0.0606169
0.0139322 0.0971045 0.7141733
D50 Bradfordadapted XYZ to RGB [M]1
3.1338561 1.6168667 0.4906146
0.9787684 1.9161415 0.0334540
0.0719453 0.2289914 1.4052427
D50 tristimulus:
X 0.96422
Y 1.00000
Z 0.82521
You might want to consider these enhancements for an update.
Rob





Thank you Rob2412!
These D50 numbers give a much closer conversion from lab to RGB and back. Also, for anyone else wanting to convert lab to RGB there is a bug pointed out by EvgenyTr above with a flipped sign in the XYZtoRGB function. I will repeat it here:
Clinear[1] = x*0.9692 + y*1.8760  z*0.0416;
should be Clinear[1] = x*0.9692 + y*1.8760 + z*0.0416;
Fixing this bug plus Rob's numbers gives good results for flipping Lab to Rgb and back.





Hi, The preview for part 2 looks great did you ever get around to finishing it ?
It would be great to see the next part
Thanks.






hello
would you please help me to find the right formules to convert image from rgb to hsi?.
thank you





Great Work! Thank you for the Article!





Your article on colour (I live in the UK so colour = color) is a really great contribution. Warms the hert that the internet can host the considered efforts of people like you. Well done!





It's really awesome article.
I have been looking around, however i cannot find the part 2, and part 3.
Where could i find them?
I love freecode





I am inpatient in waiting to continue this serie of articles and fix to this article. If I can mention some idea of more color models let me say "CMYKOG" and "CMYKOGxy" which becames more and more popular for hexachrome (and more) printing. With obvious expirience of author I belive that would be possible to programm in C# or VBasic.
Once more thank you and keep working this great job.
If I can help  I would like, but unfortunatelly can't. I dont know to program and also I dont know much about separations. This is why I am here. To learn something.





One of the most complete articles I have read!!!
THANK YOU!!





Salute Guillaume,
Excellent article! Have the 5.
Can you provide me with the references to Part II and III. Have you published them?
Best regards,
Oleg V. Polikarpotchkin
ovp





Great article. Very helpful.
But I think it would be better to use the Round method from the Math class instead of converting the number to string then parsing it back to a number.
I am converting HSL to Color (RGB) and back, and round HSL doubles to 10 digit precision and integerround before casting RGB values to bytes. When converting from Color to HSL and back to Color I get the same RGB values I started with.
Using the Round method is probably faster as well.





Right, those roundings are strange:
double r;
Double.Parse(String.Format("{0:0.00}", r*255.0))





After scouring the internet for hours, I find this artical right under my nose.
It contains the perfect information that I needed to learn about colour spaces.
Thanks heaps for putting in so much time to teach us.
 Scotley





It is very useful. Cheers. it is very clear.
I only have to modify a little bit to provide LAB to RGB service.
Cheers.





I have not had a chance to thoroughly read through the article, but just from my research into "this question" I can tell you have much more here than I've seen in similar types of articles.
Anyway, what I am trying to do is convert between RYB and RGB. Do you have any details on that, or can you point me in the direction of where I can see how it is done?
Thanks...





Is there any research published that allows me to:
(1) Select any group of colors automatically, given a certain lightness or darkness, so that when the colors are drawn on a map, they do not conflict? One imagines that complementary colors would work, but what happens when we want 100 colors, all different enough so they do not appear similar? The application here is geographical mapping.
(2) I have been trying to lighten and darken colors programmatically, but the process is much more complex than one can imagine, for instance, this color:
R = 50
G = 100
B = 0
... if we lighten by increasing the numbers proportionally, we might get this color:
R = 100
G = 200
B = 0
... the Blue is zero, so doubling it is still zero. The resulting color does not look like a lighter version of the original color.
There are many other cases where the numbers are all midrange (between 50 and 100), but increasing their size tends to drift the color off course anyway.
The same dilemmas arise when darkening colors by reducing their values.
This appears to be a real science. Any ideas as to how to solve this problem?
Thank you!





Best Color Space article for .Net around!





A detailed, informative and easy to understand article.
Thanks,
Bill W






Very good and thorough explanation of color mixing, as a suggestion for upcoming revision a conversion between wavelenght and RGB would be usefull.
Source code to acomplish this is compliments of Miguel Moreno
http://www.miguelmoreno.net/sandbox/wavelengthtoRGB/default.aspx[^]
private Color getColorFromWaveLength(int Wavelength)
{
double Gamma = 1.00;
int IntensityMax = 255;
double Blue;
double Green;
double Red;
double Factor;
if (Wavelength >= 350 && Wavelength <= 439)
{
Red = (Wavelength  440d) / (440d  350d);
Green = 0.0;
Blue = 1.0;
}
else if (Wavelength >= 440 && Wavelength <= 489)
{
Red = 0.0;
Green = (Wavelength  440d) / (490d  440d);
Blue = 1.0;
}
else if (Wavelength >= 490 && Wavelength <= 509)
{
Red = 0.0;
Green = 1.0;
Blue = (Wavelength  510d) / (510d  490d);
}
else if (Wavelength >= 510 && Wavelength <= 579)
{
Red = (Wavelength  510d) / (580d  510d);
Green = 1.0;
Blue = 0.0;
}
else if (Wavelength >= 580 && Wavelength <= 644)
{
Red = 1.0;
Green = (Wavelength  645d) / (645d  580d);
Blue = 0.0;
}
else if (Wavelength >= 645 && Wavelength <= 780)
{
Red = 1.0;
Green = 0.0;
Blue = 0.0;
}
else
{
Red = 0.0;
Green = 0.0;
Blue = 0.0;
}
if (Wavelength >= 350 && Wavelength <= 419)
{
Factor = 0.3 + 0.7 * (Wavelength  350d) / (420d  350d);
}
else if (Wavelength >= 420 && Wavelength <= 700)
{
Factor = 1.0;
}
else if (Wavelength >= 701 && Wavelength <= 780)
{
Factor = 0.3 + 0.7 * (780d  Wavelength) / (780d  700d);
}
else
{
Factor = 0.0;
}
int R = this.factorAdjust(Red, Factor, IntensityMax, Gamma);
int G = this.factorAdjust(Green, Factor, IntensityMax, Gamma);
int B = this.factorAdjust(Blue, Factor, IntensityMax, Gamma);
return Color.FromArgb(R, G, B);
}
private int factorAdjust(double Color,
double Factor,
int IntensityMax,
double Gamma)
{
if (Color == 0.0)
{
return 0;
}
else
{
return (int)Math.Round(IntensityMax * Math.Pow(Color * Factor, Gamma));
}
}
Greetings from Mexico





I am looking into a project and need someone who knows c, c++ and also color  do you freelance and how can I get in touch?







General News Suggestion Question Bug Answer Joke Rant Admin Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Understand and use color models in .NET
Type  Article 
Licence  CPOL 
First Posted  3 Jun 2007 
Views  258,889 
Downloads  7,155 
Bookmarked  390 times 

