Continuing on with a previous post where I did a C# version of the Delta-E algorithm, which is really mostly just converting RGB to CIE-L*ab. Getting the delta score at that point is really pretty simple. I’ve had a chance now to convert one of the better algorithms now as well. To use the 1994 version below, just replace the CompareTo function in my original post with the one below. You could also overload it and pass in the K_1 and K_2 values as parameters, or create an enum to pass in as a parameter. I would recommend using a couple of existing values and eyeballing them if you’re not sure if you should use the graphics or textiles ones. Dark blues, teal, and tan were the ones that I used.

There is obviously a performance hit as it’s significantly more complex than the older version. I haven’t done any performance testing to compare the two yet, but I may post those in the future if I get a chance. I may do the CMC and 2000 formulas as well when I get some more time.

 public int CompareTo(ColorFormulas oComparisionColor)
        {
            // Based upon the Delta-E (1994) formula at easyrgb.com (http://www.easyrgb.com/index.php?X=DELT&H=04#text4)
            // Also referenced the python ColorMath project for factoring in textile vs screen: https://github.com/gtaylor/python-colormath/blob/master/colormath/color_diff.py
            double CIE_L1 = CieL;                       //Color #1 CIE-L*ab values
            double CIE_a1 = CieA;
            double CIE_b1 = CieB;
            double CIE_L2 = oComparisionColor.CieL;     //Color #2 CIE-L*ab values
            double CIE_a2 = oComparisionColor.CieA;
            double CIE_b2 = oComparisionColor.CieB;

            double K_1 = 0.048;      // 0.045 graphic arts, 0.048 textiles
            double K_2 = 0.014;      // 0.015 graphic arts, 0.014 textiles

            double K_L = 2;         // 1 default, 2 textiles
            double K_C = 1;
            double K_H = 1;

            double xC1 = Math.Sqrt(Math.Pow(CIE_a1, 2) + Math.Pow(CIE_b1, 2));
            double xC2 = Math.Sqrt(Math.Pow(CIE_a2, 2) + Math.Pow(CIE_b2, 2));

            double S_L = 1;
            double S_C = 1 + K_1 * xC1;
            double S_H = 1 + K_2 * xC1;

            double delta_L = CIE_L1 - CIE_L2;
            double delta_C = xC1 - xC2;
            double delta_a = CIE_a1 - CIE_a2;
            double delta_b = CIE_b1 - CIE_b2;

            double delta_H = 0;
            double deltaHCalc = Math.Pow(delta_a, 2) + Math.Pow(delta_b, 2) - Math.Pow(delta_C, 2);

            // Can't do a sqrt of a negative num
            if (deltaHCalc < 0)
            {
                delta_H = 0;
            }
            else
            {
                delta_H = Math.Sqrt(deltaHCalc);
            }

            // Make double sure that delta_H is non-negative 
            if (Double.IsNaN(delta_H) || delta_H < 0) delta_H = 0;

            double L_group = Math.Pow(delta_L / (K_L * S_L), 2);
            double C_group = Math.Pow(delta_C / (K_C * S_C), 2);
            double H_group = Math.Pow(delta_H / (K_H * S_H), 2);

            double Delta94 = Math.Sqrt(L_group + C_group + H_group);
            return Convert.ToInt16(Math.Round(Delta94));
        }

I’ve had to do some work on color comparison recently. There are some really great algorithms out there for this, particularly at easyrgb.com. Unfortunately, I couldn’t find a good C# version of this anywhere, so I had to convert my own. So for my own future reference, and for anyone that needs it here it is.

public class ColorFormulas
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public double CieL { get; set; }
        public double CieA { get; set; }
        public double CieB { get; set; }

        public ColorFormulas(int R, int G, int B)
        {
            RGBtoLAB(R, G, B);
        }

        public void RGBtoLAB(int R, int G, int B)
        {
            RGBtoXYZ(R, G, B);
            XYZtoLAB();
        }

        public void RGBtoXYZ(int RVal, int GVal, int BVal)
        {
            double R = Convert.ToDouble(RVal) / 255.0;       //R from 0 to 255
            double G = Convert.ToDouble(GVal) / 255.0;       //G from 0 to 255
            double B = Convert.ToDouble(BVal) / 255.0;       //B from 0 to 255

            if (R > 0.04045)
            {
                R = Math.Pow(((R + 0.055) / 1.055), 2.4);
            }
            else
            {
                R = R / 12.92;
            }
            if (G > 0.04045)
            {
                G = Math.Pow(((G + 0.055) / 1.055), 2.4);
            }
            else
            {
                G = G / 12.92;
            }
            if (B > 0.04045)
            {
                B = Math.Pow(((B + 0.055) / 1.055), 2.4);
            }
            else
            {
                B = B / 12.92;
            }

            R = R * 100;
            G = G * 100;
            B = B * 100;

            //Observer. = 2°, Illuminant = D65
            X = R * 0.4124 + G * 0.3576 + B * 0.1805;
            Y = R * 0.2126 + G * 0.7152 + B * 0.0722;
            Z = R * 0.0193 + G * 0.1192 + B * 0.9505;
        }

        public void XYZtoLAB()
        {
            // based upon the XYZ - CIE-L*ab formula at easyrgb.com (http://www.easyrgb.com/index.php?X=MATH&H=07#text7)
            double ref_X = 95.047;
            double ref_Y = 100.000;
            double ref_Z = 108.883;

            double var_X = X / ref_X;         // Observer= 2°, Illuminant= D65
            double var_Y = Y / ref_Y;
            double var_Z = Z / ref_Z;

            if (var_X > 0.008856)
            {
                var_X = Math.Pow(var_X , (1 / 3.0));
            }
            else
            {
                var_X = (7.787 * var_X) + (16 / 116.0);
            }
            if (var_Y > 0.008856)
            {
                var_Y = Math.Pow(var_Y, (1 / 3.0));
            }
            else
            {
                var_Y = (7.787 * var_Y) + (16 / 116.0);
            }
            if (var_Z > 0.008856)
            {
                var_Z = Math.Pow(var_Z, (1 / 3.0));
            }
            else
            {
                var_Z = (7.787 * var_Z) + (16 / 116.0);
            }

            CieL = (116 * var_Y) - 16;
            CieA = 500 * (var_X - var_Y);
            CieB = 200 * (var_Y - var_Z);
        }

        ///
        /// The smaller the number returned by this, the closer the colors are
        ///
        ///
        /// 
        public int CompareTo(ColorFormulas oComparisionColor)
        {
            // Based upon the Delta-E (1976) formula at easyrgb.com (http://www.easyrgb.com/index.php?X=DELT&H=03#text3)
            double DeltaE = Math.Sqrt(Math.Pow((CieL - oComparisionColor.CieL), 2) + Math.Pow((CieA - oComparisionColor.CieA), 2) + Math.Pow((CieB - oComparisionColor.CieB), 2));
            return Convert.ToInt16(Math.Round(DeltaE));
        }

        public static int DoFullCompare(int R1, int G1, int B1, int R2, int G2, int B2)
        {
            ColorFormulas oColor1 = new ColorFormulas(R1, G1, B1);
            ColorFormulas oColor2 = new ColorFormulas(R2, G2, B2);
            return oColor1.CompareTo(oColor2);
        }
    }

Because of a recent project, I got to take a closer look at how various types of serialization and deserialization perform in .Net, using the built in serializers. It pitted binary, XML, and JSON versus each other. My guess going in would be that binary would be the fastest, followed by JSON and then XML, following the size of each. That was generally true for serialization, but not for deserialization. XML deserialized faster than JSON, with JSON scaling really badly. Very interesting stuff.