deltaE2000 function

double deltaE2000(
  1. Lab c1,
  2. Lab c2
)

ΔE CIE2000 between two Lab colors. Returns 0 for identical inputs and values up to ~100 for extreme contrasts.

Implementation follows Sharma et al. (2005), equations 1–10.

Implementation

double deltaE2000(Lab c1, Lab c2) {
  const kL = 1.0;
  const kC = 1.0;
  const kH = 1.0;

  final l1 = c1.l;
  final a1 = c1.a;
  final b1 = c1.b;
  final l2 = c2.l;
  final a2 = c2.a;
  final b2 = c2.b;

  final c1Star = math.sqrt(a1 * a1 + b1 * b1);
  final c2Star = math.sqrt(a2 * a2 + b2 * b2);
  final cMean = (c1Star + c2Star) / 2.0;

  final cMean7 = math.pow(cMean, 7).toDouble();
  final g = 0.5 * (1.0 - math.sqrt(cMean7 / (cMean7 + math.pow(25, 7))));

  final a1Prime = a1 * (1.0 + g);
  final a2Prime = a2 * (1.0 + g);

  final c1Prime = math.sqrt(a1Prime * a1Prime + b1 * b1);
  final c2Prime = math.sqrt(a2Prime * a2Prime + b2 * b2);

  final h1Prime = _atan2Deg(b1, a1Prime);
  final h2Prime = _atan2Deg(b2, a2Prime);

  final dlPrime = l2 - l1;
  final dcPrime = c2Prime - c1Prime;

  double dhPrime;
  if (c1Prime * c2Prime == 0) {
    dhPrime = 0;
  } else {
    var diff = h2Prime - h1Prime;
    if (diff > 180) {
      diff -= 360;
    } else if (diff < -180) {
      diff += 360;
    }
    dhPrime = diff;
  }
  final dHPrime =
      2.0 * math.sqrt(c1Prime * c2Prime) * math.sin(_deg2rad(dhPrime / 2.0));

  final lMeanPrime = (l1 + l2) / 2.0;
  final cMeanPrime = (c1Prime + c2Prime) / 2.0;

  double hMeanPrime;
  if (c1Prime * c2Prime == 0) {
    hMeanPrime = h1Prime + h2Prime;
  } else if ((h1Prime - h2Prime).abs() <= 180) {
    hMeanPrime = (h1Prime + h2Prime) / 2.0;
  } else if ((h1Prime + h2Prime) < 360) {
    hMeanPrime = (h1Prime + h2Prime + 360) / 2.0;
  } else {
    hMeanPrime = (h1Prime + h2Prime - 360) / 2.0;
  }

  final t =
      1.0 -
      0.17 * math.cos(_deg2rad(hMeanPrime - 30)) +
      0.24 * math.cos(_deg2rad(2 * hMeanPrime)) +
      0.32 * math.cos(_deg2rad(3 * hMeanPrime + 6)) -
      0.20 * math.cos(_deg2rad(4 * hMeanPrime - 63));

  final dTheta =
      30.0 * math.exp(-math.pow((hMeanPrime - 275) / 25, 2).toDouble());

  final cMeanPrime7 = math.pow(cMeanPrime, 7).toDouble();
  final rC = 2.0 * math.sqrt(cMeanPrime7 / (cMeanPrime7 + math.pow(25, 7)));

  final lMinus50Sq = (lMeanPrime - 50) * (lMeanPrime - 50);
  final sL = 1.0 + (0.015 * lMinus50Sq) / math.sqrt(20 + lMinus50Sq);
  final sC = 1.0 + 0.045 * cMeanPrime;
  final sH = 1.0 + 0.015 * cMeanPrime * t;

  final rT = -math.sin(_deg2rad(2 * dTheta)) * rC;

  final lTerm = dlPrime / (kL * sL);
  final cTerm = dcPrime / (kC * sC);
  final hTerm = dHPrime / (kH * sH);

  return math.sqrt(
    lTerm * lTerm + cTerm * cTerm + hTerm * hTerm + rT * cTerm * hTerm,
  );
}