123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- using System.Drawing;
- namespace Masuit.Tools.Media;
- public static class ColorDeltaE
- {
- // CIE1976 (ΔE*ab)
- public static double CIE1976(this Color color1, Color color2) => CalculateDeltaE1976(color1.ToLab(), color2.ToLab());
- public static double CIE1976(this CMYColor color1, CMYColor color2) => CalculateDeltaE1976(color1.ToLab(), color2.ToLab());
- public static double CIE1976(this CMYKColor color1, CMYKColor color2) => CalculateDeltaE1976(color1.ToLab(), color2.ToLab());
- public static double CIE1976(this HSLColor color1, HSLColor color2) => CalculateDeltaE1976(color1.ToLab(), color2.ToLab());
- public static double CIE1976(this LabColor color1, LabColor color2) => CalculateDeltaE1976(color1, color2);
- public static double CIE1976(this LCHColor color1, LCHColor color2) => CalculateDeltaE1976(color1.ToLab(), color2.ToLab());
- public static double CIE1976(this XYZColor color1, XYZColor color2) => CalculateDeltaE1976(color1.ToLab(), color2.ToLab());
- public static double CIE1976(this YXZColor color1, YXZColor color2) => CalculateDeltaE1976(color1.YxzToLab(), color2.YxzToLab());
- // CIE1994 (ΔE94)
- public static double CIE1994(this Color color1, Color color2, bool textile = false) => CalculateDeltaE1994(color1.ToLab(), color2.ToLab(), textile);
- public static double CIE1994(this CMYColor color1, CMYColor color2, bool textile = false) => CalculateDeltaE1994(color1.ToLab(), color2.ToLab(), textile);
- public static double CIE1994(this CMYKColor color1, CMYKColor color2, bool textile = false) => CalculateDeltaE1994(color1.ToLab(), color2.ToLab(), textile);
- public static double CIE1994(this HSLColor color1, HSLColor color2, bool textile = false) => CalculateDeltaE1994(color1.ToLab(), color2.ToLab(), textile);
- public static double CIE1994(this LabColor color1, LabColor color2, bool textile = false) => CalculateDeltaE1994(color1, color2, textile);
- public static double CIE1994(this LCHColor color1, LCHColor color2, bool textile = false) => CalculateDeltaE1994(color1.ToLab(), color2.ToLab(), textile);
- public static double CIE1994(this XYZColor color1, XYZColor color2, bool textile = false) => CalculateDeltaE1994(color1.ToLab(), color2.ToLab(), textile);
- public static double CIE1994(this YXZColor color1, YXZColor color2, bool textile = false) => CalculateDeltaE1994(color1.YxzToLab(), color2.YxzToLab(), textile);
- // CIE2000 (ΔE00)
- public static double CIE2000(this Color color1, Color color2) => CalculateDeltaE2000(color1.ToLab(), color2.ToLab());
- public static double CIE2000(this CMYColor color1, CMYColor color2) => CalculateDeltaE2000(color1.ToLab(), color2.ToLab());
- public static double CIE2000(this CMYKColor color1, CMYKColor color2) => CalculateDeltaE2000(color1.ToLab(), color2.ToLab());
- public static double CIE2000(this HSLColor color1, HSLColor color2) => CalculateDeltaE2000(color1.ToLab(), color2.ToLab());
- public static double CIE2000(this LabColor color1, LabColor color2) => CalculateDeltaE2000(color1, color2);
- public static double CIE2000(this LCHColor color1, LCHColor color2) => CalculateDeltaE2000(color1.ToLab(), color2.ToLab());
- public static double CIE2000(this XYZColor color1, XYZColor color2) => CalculateDeltaE2000(color1.ToLab(), color2.ToLab());
- public static double CIE2000(this YXZColor color1, YXZColor color2) => CalculateDeltaE2000(color1.YxzToLab(), color2.YxzToLab());
- // CMC (ΔEcmc)
- public static double CMC(this Color color1, Color color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.ToLab(), color2.ToLab(), l, c);
- public static double CMC(this CMYColor color1, CMYColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.ToLab(), color2.ToLab(), l, c);
- public static double CMC(this CMYKColor color1, CMYKColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.ToLab(), color2.ToLab(), l, c);
- public static double CMC(this HSLColor color1, HSLColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.ToLab(), color2.ToLab(), l, c);
- public static double CMC(this LabColor color1, LabColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1, color2, l, c);
- public static double CMC(this LCHColor color1, LCHColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.ToLab(), color2.ToLab(), l, c);
- public static double CMC(this XYZColor color1, XYZColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.ToLab(), color2.ToLab(), l, c);
- public static double CMC(this YXZColor color1, YXZColor color2, double l = 2.0, double c = 1.0) => CalculateDeltaECMC(color1.YxzToLab(), color2.YxzToLab(), l, c);
- #region Core Calculation Methods
- private static double CalculateDeltaE1976(LabColor lab1, LabColor lab2)
- {
- double deltaL = lab1.L - lab2.L;
- double deltaA = lab1.a - lab2.a;
- double deltaB = lab1.b - lab2.b;
- return Math.Sqrt(deltaL * deltaL + deltaA * deltaA + deltaB * deltaB);
- }
- private static double CalculateDeltaE1994(LabColor lab1, LabColor lab2, bool textile)
- {
- double kL = textile ? 2.0 : 1.0;
- double kC = 1.0;
- double kH = 1.0;
- double k1 = textile ? 0.048 : 0.045;
- double k2 = textile ? 0.014 : 0.015;
- double deltaL = lab1.L - lab2.L;
- 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 deltaC = c1 - c2;
- double deltaA = lab1.a - lab2.a;
- double deltaB = lab1.b - lab2.b;
- double deltaH = Math.Sqrt(Math.Max(0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC));
- double sl = 1.0;
- double sc = 1.0 + k1 * c1;
- double sh = 1.0 + k2 * c1;
- double termL = deltaL / (kL * sl);
- double termC = deltaC / (kC * sc);
- double termH = deltaH / (kH * sh);
- return Math.Sqrt(termL * termL + termC * termC + termH * termH);
- }
- private static double CalculateDeltaE2000(LabColor lab1, LabColor lab2)
- {
- const double kL = 1.0;
- const double kC = 1.0;
- const double kH = 1.0;
- double l1 = lab1.L, a1 = lab1.a, b1 = lab1.b;
- double l2 = lab2.L, a2 = lab2.a, b2 = lab2.b;
- double c1 = Math.Sqrt(a1 * a1 + b1 * b1);
- double c2 = Math.Sqrt(a2 * a2 + b2 * b2);
- double meanC = (c1 + c2) / 2.0;
- double meanC7 = Math.Pow(meanC, 7);
- double g = 0.5 * (1 - Math.Sqrt(meanC7 / (meanC7 + 6103515625.0)));
- double a1Prime = a1 * (1 + g);
- double a2Prime = a2 * (1 + g);
- double c1Prime = Math.Sqrt(a1Prime * a1Prime + b1 * b1);
- double c2Prime = Math.Sqrt(a2Prime * a2Prime + b2 * b2);
- double h1Prime = (Math.Atan2(b1, a1Prime) * 180.0 / Math.PI + 360) % 360;
- double h2Prime = (Math.Atan2(b2, a2Prime) * 180.0 / Math.PI + 360) % 360;
- double deltaLPrime = l2 - l1;
- double deltaCPrime = c2Prime - c1Prime;
- double deltaHPrime;
- if (Math.Abs(h2Prime - h1Prime) <= 180)
- {
- deltaHPrime = h2Prime - h1Prime;
- }
- else if (h2Prime <= h1Prime)
- {
- deltaHPrime = h2Prime - h1Prime + 360;
- }
- else
- {
- deltaHPrime = h2Prime - h1Prime - 360;
- }
- double deltaH = 2 * Math.Sqrt(c1Prime * c2Prime) * Math.Sin(deltaHPrime * Math.PI / 360.0);
- double meanLPrime = (l1 + l2) / 2.0;
- double meanCPrime = (c1Prime + c2Prime) / 2.0;
- double meanHPrime;
- if (Math.Abs(h1Prime - h2Prime) > 180)
- {
- meanHPrime = (h1Prime + h2Prime + 360) / 2.0;
- }
- else
- {
- meanHPrime = (h1Prime + h2Prime) / 2.0;
- }
- meanHPrime %= 360;
- double t = 1 - 0.17 * Math.Cos((meanHPrime - 30) * Math.PI / 180) + 0.24 * Math.Cos(2 * meanHPrime * Math.PI / 180) + 0.32 * Math.Cos((3 * meanHPrime + 6) * Math.PI / 180) - 0.20 * Math.Cos((4 * meanHPrime - 63) * Math.PI / 180);
- double sl = 1 + (0.015 * Math.Pow(meanLPrime - 50, 2)) / Math.Sqrt(20 + Math.Pow(meanLPrime - 50, 2));
- double sc = 1 + 0.045 * meanCPrime;
- double sh = 1 + 0.015 * meanCPrime * t;
- double meanCPrime7 = Math.Pow(meanCPrime, 7);
- double rc = 2 * Math.Sqrt(meanCPrime7 / (meanCPrime7 + 6103515625.0));
- double deltaTheta = 30 * Math.Exp(-Math.Pow((meanHPrime - 275) / 25, 2));
- double rt = -Math.Sin(2 * deltaTheta * Math.PI / 180) * rc;
- double term1 = deltaLPrime / (kL * sl);
- double term2 = deltaCPrime / (kC * sc);
- double term3 = deltaH / (kH * sh);
- return Math.Sqrt(term1 * term1 + term2 * term2 + term3 * term3 + rt * term2 * term3);
- }
- private static double CalculateDeltaECMC(LabColor lab1, LabColor lab2, double l, double c)
- {
- double deltaL = lab1.L - lab2.L;
- 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 deltaC = c1 - c2;
- double deltaA = lab1.a - lab2.a;
- double deltaB = lab1.b - lab2.b;
- double deltaH = Math.Sqrt(Math.Max(0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC));
- double h1 = (Math.Atan2(lab1.b, lab1.a) * 180.0 / Math.PI + 360) % 360;
- double f = Math.Sqrt(Math.Pow(c1, 4) / (Math.Pow(c1, 4) + 1900));
- double t;
- if (h1 is >= 164 and <= 345)
- {
- t = 0.56 + Math.Abs(0.2 * Math.Cos((h1 + 168) * Math.PI / 180));
- }
- else
- {
- t = 0.36 + Math.Abs(0.4 * Math.Cos((h1 + 35) * Math.PI / 180));
- }
- double sl = (lab1.L < 16) ? 0.511 : (0.040975 * lab1.L) / (1 + 0.01765 * lab1.L);
- double sc = (0.0638 * c1) / (1 + 0.0131 * c1) + 0.638;
- double sh = sc * (t * f + 1 - f);
- double termL = deltaL / (l * sl);
- double termC = deltaC / (c * sc);
- double termH = deltaH / sh;
- return Math.Sqrt(termL * termL + termC * termC + termH * termH);
- }
- #endregion Core Calculation Methods
- }
|