bc / ciede-2000.bc 💾

This is a 4,265-byte text/plain file, placed in the public domain, which has been consulted 143 times since the year 2026.

/* This function written in bc is not affiliated with the CIE (International Commission on Illumination),
and is released into the public domain. It is provided "as is" without any warranty, express or implied. */

m_pi = 0.0

/* The classic CIE ΔE2000 implementation, which operates on two L*a*b* colors, and returns their difference.
"l" ranges from 0 to 100, while "a" and "b" are unbounded and commonly clamped to the range of -128 to 127. */
define ciede_2000(l_1, a_1, b_1, l_2, a_2, b_2) {
	/* Working in Basic Calculator with the CIEDE2000 color-difference formula.
	k_l, k_c, k_h are parametric factors to be adjusted according to
	different viewing parameters such as textures, backgrounds... */
	k_l = 1.0
	k_c = 1.0
	k_h = 1.0
	if (m_pi == 0.0) {
		/* Computing pi ... 3.141592653589793238462643383279502884197169399375105820974945
		with arbitrary precision using Machin’s formula proposed in 1706. */
		m_pi =  16.0 * a(0.2) - 4.0 * a(1.0 / 239.0)
	}
	n = (sqrt(a_1 * a_1 + b_1 * b_1) + sqrt(a_2 * a_2 + b_2 * b_2)) * 0.5
	n = n * n * n * n * n * n * n
	/* A factor involving chroma raised to the power of 7 designed to make
	the influence of chroma on the total color difference more accurate. */
	n = 1.0 + 0.5 * (1.0 - sqrt(n / (n + 6103515625.0)))
	/* Application of the chroma correction factor. */
	c_1 = sqrt(a_1 * a_1 * n * n + b_1 * b_1)
	c_2 = sqrt(a_2 * a_2 * n * n + b_2 * b_2)
	/* Using 14 lines to simulate atan2, as bc does not have this built-in. */
	if (0.0 < a_1) {
		h_1 = a(b_1 / (a_1 * n)) + (b_1 < 0.0) * 2.0 * m_pi
	} else if (a_1 < 0.0) {
		h_1 = a(b_1 / (a_1 * n)) + m_pi
	} else {
		h_1 = m_pi + ((b_1 < 0.0) - (0.0 < b_1)) * 0.5 * m_pi
	}
	if (0.0 < a_2) {
		h_2 = a(b_2 / (a_2 * n)) + (b_2 < 0.0) * 2.0 * m_pi
	} else if (a_2 < 0.0) {
		h_2 = a(b_2 / (a_2 * n)) + m_pi
	} else {
		h_2 = m_pi + ((b_2 < 0.0) - (0.0 < b_2)) * 0.5 * m_pi
	}
	/* The atan2 polyfill (customized) is complete. */
	if (h_2 < h_1) { n = h_1 - h_2; } else { n = h_2 - h_1; }
	/* Cross-implementation consistent rounding. */
	if (m_pi - 0.00000000000001 < n && n < m_pi + 0.00000000000001) { n = m_pi; }
	/* When the hue angles lie in different quadrants, the straightforward
	average can produce a mean that incorrectly suggests a hue angle in
	the wrong quadrant, the next lines handle this issue. */
	h_m = (h_1 + h_2) * 0.5
	h_d = (h_2 - h_1) * 0.5
	if (m_pi < n) {
		h_d = h_d + m_pi
		/* 📜 Sharma’s formulation doesn’t use the next line, but the one after it,
		and these two variants differ by ±0.0003 on the final color differences. */
		h_m = h_m + m_pi
		/* h_m = h_m + ((h_m < m_pi) - (m_pi <= h_m)) * m_pi */
	}
	p = 36.0 * h_m - 55.0 * m_pi
	n = (c_1 + c_2) * 0.5
	n = n * n * n * n * n * n * n
	/* The hue rotation correction term is designed to account for the
	non-linear behavior of hue differences in the blue region. */
	r_t = -2.0 * sqrt(n / (n + 6103515625.0)) \
			* s(m_pi / 3.0 * e(p * p / (-25.0 * m_pi * m_pi)))
	n = (l_1 + l_2) * 0.5
	n = (n - 50.0) * (n - 50.0)
	/* Lightness. */
	l = (l_2 - l_1) / (k_l * (1.0 + 0.015 * n / sqrt(20.0 + n)))
	/* These coefficients adjust the impact of different harmonic
	components on the hue difference calculation. */
	t = 1.0 	+ 0.24 * s(2.0 * h_m + m_pi / 2.0) \
			+ 0.32 * s(3.0 * h_m + 8.0 * m_pi / 15.0) \
			- 0.17 * s(h_m + m_pi / 3.0) \
			- 0.20 * s(4.0 * h_m + 3.0 * m_pi / 20.0)
	n = c_1 + c_2
	/* Hue. */
	h = 2.0 * sqrt(c_1 * c_2) * s(h_d) / (k_h * (1.0 + 0.0075 * n * t))
	/* Chroma. */
	c = (c_2 - c_1) / (k_c * (1.0 + 0.0225 * n))
	/* Returning the square root ensures that dE00 accurately reflects the
	geometric distance in color space, which can range from 0 to around 185. */
	return sqrt(l * l + h * h + c * c + c * h * r_t)
}

/*
  GitHub Project : https://github.com/michel-leonard/ciede2000-color-matching
    Online Tests : https://michel-leonard.github.io/ciede2000-color-matching

   L1 = 30.8   a1 = 22.0   b1 = -4.4
   L2 = 28.1   a2 = 16.4   b2 = 4.3
   CIE ΔE00 = 7.0779305175 (Bruce Lindbloom, Netflix’s VMAF, ...)
   CIE ΔE00 = 7.0779164917 (Gaurav Sharma, OpenJDK, ...)
   Deviation between implementations ≈ 1.4e-5

   See the source code comments for easy switching between these two widely used ΔE*00 implementation variants.
*/