Implémentation de CIEDE2000 en JavaScript

Version de la fonction : v1.0.0
Statistiques du site
Nombre de visites2 293
Nombre de fichiers consultés1 275 + 606

Cette page prĂ©sente une implĂ©mentation de rĂ©fĂ©rence de la formule de diffĂ©rence de couleur CIEDE2000, Ă©crite en langage de programmation JavaScript. Si vous souhaitez assurer une compatibilitĂ© parfaite (Ă  la dixiĂšme dĂ©cimale) avec certaines implĂ©mentations tierces, vous devrez peut-ĂȘtre modifier les commentaires dans le code source. Pour vous faciliter la tĂąche, le lien suivant automatise cette opĂ©ration.

Diagramme de la formule CIEDE2000 complĂšte avec les composants L*a*b* et les ajustements.

La fonction ΔE2000 en JavaScript

Considérons la plus courante et académique (Sharma, 2005) des deux formulations.

// Cette fonction écrite en JavaScript est placée dans le domaine public et
// n’est pas affiliĂ©e Ă  la CIE (Commission Internationale de l’Éclairage).

// L’implĂ©mentation CIEDE2000 classique qui accepte deux couleurs L*a*b* et renvoie leur diffĂ©rence.
// La composante "L" varie de 0 à 100. "a" et "b", non bornées, sont souvent projetées entre -128 et 127.
function ciede_2000(l_1, a_1, b_1, l_2, a_2, b_2, k_l, k_c, k_h, canonical) {
	"use strict"
	// Traite la formule de différence de couleurs CIEDE2000 en JavaScript.
	// k_l, k_c et k_h sont des facteurs paramĂ©triques qu’on ajuste
	// selon des recommandations propres au secteur industriel.
	var n = (Math.sqrt(a_1 * a_1 + b_1 * b_1) + Math.sqrt(a_2 * a_2 + b_2 * b_2)) * 0.5;
	n = n * n * n * n * n * n * n;
	// Un facteur impliquant la chroma moyenne Ă  la puissance 7,
	// conçu pour modéliser plus précisément son influence.
	n = 1.0 + 0.5 * (1.0 - Math.sqrt(n / (n + 6103515625.0)));
	// Application du facteur de correction de la chroma pour compenser sa non-linéarité.
	var c_1 = Math.sqrt(a_1 * a_1 * n * n + b_1 * b_1);
	var c_2 = Math.sqrt(a_2 * a_2 * n * n + b_2 * b_2);
	// La fonction atan2 est prĂ©fĂ©rĂ©e Ă  atan car elle calcule l’angle d’un
	// point (x, y) dans tous les quadrants, en tenant compte du signe de x et y.
	var h_1 = Math.atan2(b_1, a_1 * n), h_2 = Math.atan2(b_2, a_2 * n);
	h_1 += 2.0 * Math.PI * (h_1 < 0.0);
	h_2 += 2.0 * Math.PI * (h_2 < 0.0);
	n = Math.abs(h_2 - h_1);
	// Prévient le branchement de dépendre du RoundingMode du langage de programmation.
	if (Math.PI - 1E-14 < n && n < Math.PI + 1E-14)
		n = Math.PI;
	// Lorsque les angles de teinte sont dans différents quadrants,
	// la moyenne arithmétique simple peut donner un angle incorrect,
	// les lignes suivantes prennent en compte cette correction angulaire.
	var h_m = (h_1 + h_2) * 0.5, h_d = (h_2 - h_1) * 0.5;
	if (Math.PI < n) {
		h_d += Math.PI;
		if (canonical) // Sharma’s implementation, OpenJDK, ...
			h_m += h_m < Math.PI ? Math.PI : -Math.PI;
		else // Lindbloom’s implementation, Netflix’s VMAF, ...
			h_m += Math.PI;
	}
	var p = 36.0 * h_m - 55.0 * Math.PI;
	n = (c_1 + c_2) * 0.5;
	n = n * n * n * n * n * n * n;
	// Le terme de correction de la rotation de teinte ajuste le comportement
	// de l’algorithme, d’autant plus si la comparaison porte sur des teintes bleues.
	var r_t = -2.0 * Math.sqrt(n / (n + 6103515625.0))
		* Math.sin(Math.PI / 3.0 * Math.exp(p * p / (-25.0 * Math.PI * Math.PI)));
	n = (l_1 + l_2) * 0.5;
	n = (n - 50.0) * (n - 50.0);
	// Luminosité.
	var l = (l_2 - l_1) / ((k_l || 1.0) * (1.0 + 0.015 * n / Math.sqrt(20.0 + n)));
	// Ces coefficients modulent l’influence des composantes
	// harmoniques dans le calcul de la différence de teinte.
	var t = 1.0	+ 0.24 * Math.sin(2.0 * h_m + Math.PI * 0.5)
			+ 0.32 * Math.sin(3.0 * h_m + 8.0 * Math.PI / 15.0)
			- 0.17 * Math.sin(h_m + Math.PI / 3.0)
			- 0.20 * Math.sin(4.0 * h_m + 3.0 * Math.PI / 20.0);
	n = c_1 + c_2;
	// Teinte.
	var h = 2.0 * Math.sqrt(c_1 * c_2) * Math.sin(h_d) / ((k_h || 1.0) * (1.0 + 0.0075 * n * t));
	// Chroma.
	var c = (c_2 - c_1) / ((k_c || 1.0) * (1.0 + 0.0225 * n));
	// Retourner la racine carrée assure que dE00 représente une distance
	// gĂ©omĂ©trique (comprise entre 0 et environ 185) dans l’espace CIELAB.
	return Math.sqrt(l * l + h * h + c * c + c * h * r_t);
}

//    Projet GitHub : https://github.com/michel-leonard/ciede2000-color-matching
//   Tests en ligne : https://michel-leonard.github.io/ciede2000-color-matching

// L1 = 54.8   a1 = 23.5   b1 = 2.0
// L2 = 53.3   a2 = 29.5   b2 = -2.2
// CIE ΔE00 = 4.1622955128 (Bruce Lindbloom, Netflix’s VMAF, ...)
// CIE ΔE00 = 4.1622807178 (Gaurav Sharma, OpenJDK, ...)
// Écart entre les implĂ©mentations ≈ 1.5e-5

// Voir les commentaires du code source pour passer d’une de ces variantes d’implĂ©mentation de ΔE*00 Ă  l’autre.

Votre test de performance

ParamĂštres k_l, k_c et k_h

Les paramĂštres k_l, k_c et k_h de la formule CIEDE2000 sont des facteurs de pondĂ©ration appliquĂ©s respectivement aux composantes de luminositĂ© (ΔL*), de chroma (ΔC*) et de teinte (ΔH*). Dans le code source, ils sont dĂ©finis comme des constantes dont la valeur par dĂ©faut est 1, ce qui correspond aux conditions d’observation standard prĂ©vues par la Commission internationale de l’éclairage (CIE). En pratique, il peut ĂȘtre nĂ©cessaire d’ajuster ces coefficients en fonction de conditions spĂ©cifiques : par exemple, k_l = 2 est parfois utilisĂ© pour donner plus de poids aux diffĂ©rences de luminositĂ© (cas frĂ©quent dans l’industrie textile), tandis que k_c ou k_h peuvent ĂȘtre rĂ©duits pour augmenter la tolĂ©rance aux variations de saturation ou de teinte. En rĂ©sumĂ©, ces coefficients varient gĂ©nĂ©ralement entre 0,5 et 2, la valeur la plus courante Ă©tant 1.

Précision et fiabilité du code source

La diffĂ©rence entre la formulation acadĂ©mique de Sharma et la formulation simplifiĂ©e de Lindbloom ne dĂ©passe pas ±0,0003 sur le ΔE2000 final. L’implĂ©mentation prĂ©sentĂ©e ici est en 64 bits et garantit de plus de 10 dĂ©cimales correctes ; le choix d’une formulation plutĂŽt qu’une autre relĂšve donc d’un dĂ©tail technique. En haut de la page, vous pouvez choisir entre les deux formulations ; celle qui s’affiche actuellement est la formulation simplifiĂ©e.

Comment déterminer si une implémentation donnée de CIEDE2000 est de type académique ou simplifié ?

Comment convertir les couleurs RGB en L*a*b* ?

Vous devrez utiliser l’espace colorimĂ©trique intermĂ©diaire XYZ pour la conversion, et si vous avez besoin d’aide, le code source est fourni au bas de cette page (en utilisant le point blanc D65 formalisĂ© en 1964).

Plages de valeurs dans CIELAB et interprĂ©tation du ΔE2000

Dans l’espace colorimĂ©trique CIELAB, la composante L* reprĂ©sente la luminositĂ© et varie de 0 (noir) Ă  100 (blanc). Les composantes a* et b* dĂ©crivent les axes de couleur : a* s’étend du vert au rouge, tandis que b* va du bleu au jaune. Dans la pratique, les valeurs de a* et b* sont presque toujours comprises entre -128 et +127, bien que la norme ne fixe pas de limite officielle pour ces deux composantes.

Exemple de deux couleurs présentant une différence à peine perceptible (JND) selon CIEDE2000
Couleur 1Couleur 2Valeur de ΔE2000
1
2
3
Exemples de valeurs CIEDE2000 calculées entre deux couleurs distinctes
Couleur 1Couleur 2Valeur de ΔE2000
5
10
15

Le ΔE2000 (CIEDE2000) quantifie la différence perceptuelle entre deux couleurs : 0 signifie deux couleurs identiques, et des valeurs plus élevées (jusqu’à 185 et plus) indiquent une différence plus marquée. Par exemple, une valeur ΔE2000 autour de 5 correspond à des couleurs proches, tandis qu’une valeur autour de 15 correspond à des couleurs clairement distinctes. Lorsque la valeur ΔE2000 dépasse 40, les couleurs comparées n’ont pratiquement plus rien en commun, et nous ne pouvons plus en tirer d’informations précises.

Exemple d’utilisation en JavaScript

// Define color 1 in L*a*b* space
const l1 = 43.2, a1 = 31.2, b1 = 4.1;

// Define color 2 in L*a*b* space
const l2 = 43.0, a2 = 35.5, b2 = -4.3;

// Calculate ΔE2000 color difference
const deltaE = ciede_2000(l1, a1, b1, l2, a2, b2);
console.log(deltaE);

// .................................................. This shows a ΔE2000 of 5.2895865658
// As explained in the comments, compliance with Gaurav Sharma would display 5.2895721943

Résultats des tests

Notre programme de tests, Ă©crit en C99, comprend 250 tests statiques prĂ©cis. Ces tests assurent que vos calculs s’effectueront sans erreur, mĂȘme dans des cas limites critiques, par exemple lorsque la fonction arc tangente renvoie une valeur mathĂ©matiquement indĂ©finie. Les rĂ©sultats montrent que cette fonction CIEDE2000 en JavaScript est interopĂ©rable avec les 41 autres langages de programmation que nous proposons.

CIEDE2000 Verification Summary :
  First Verified Line : 19.8,86.4,126,87,48.32,-70.5,86.66139766452334
             Duration : 36.48 s
            Successes : 10000000
               Errors : 0
      Average Delta E : 62.9463
    Average Deviation : 6.0717893379802488e-15
    Maximum Deviation : 2.2737367544323206e-13

⚡ Comparaison des couleurs RVB et hexadĂ©cimales pour le web

Seulement 3 Ko – Simple. Rapide. Cette fonction JavaScript compacte accepte deux couleurs au format RVB ou hexadĂ©cimal et retourne la diffĂ©rence de couleur CIE ΔE2000 en utilisant l’illuminant standard D65.

// This function written in JavaScript 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.

// This function accepts both RGB and hexadecimal color formats and computes the color difference using the CIE ΔE2000 formula.
// Examples of use :
// - ciede_2000('#00F', '#483D8B')
// - ciede_2000('#483d8b', 75,0,130)
// - ciede_2000(75, 0, 130, '#00008b')
// - ciede_2000(0, 0, 139, 0, 0, 128)

// GitHub : https://github.com/michel-leonard/ciede2000-color-matching

;function ciede_2000(a,b,c,d,e,f){"use strict";var k_l=1.0,k_c=1.0,k_h=1.0,g,h,i,j,k,l,m,n,o,p,q,r,s=0.040448236277105097;if(typeof a=='string'){g=parseInt((a.length===4?a[0]+a[1]+a[1]+a[2]+a[2]+a[3]+a[3]:a).substring(1),16);if(typeof b=='string'){h=parseInt((b.length===4?b[0]+b[1]+b[1]+b[2]+b[2]+b[3]+b[3]:b).substring(1),16);d=h>>16&0xff;e=h>>8&0xff;f=h&0xff;}else{f=d;e=c;d=b;}a=g>>16&0xff;b=g>>8&0xff;c=g&0xff}else if(typeof d=='string'){g=parseInt((d.length===4?d[0]+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]:d).substring(1),16);d=g>>16&0xff;e=g>>8&0xff;f=g&0xff;}a/=255.0;b/=255.0;c/=255.0;a=a<s?a/12.92:Math.pow((a+0.055)/1.055,2.4);b=b<s?b/12.92:Math.pow((b+0.055)/1.055,2.4);c=c<s?c/12.92:Math.pow((c+0.055)/1.055,2.4);g=a*41.24564390896921145+b*35.75760776439090507+c*18.04374830853290341;h=a*21.26728514056222474+b*71.51521552878181013+c*7.21749933075596513;i=a*1.93338955823293176+b*11.91919550818385936+c*95.03040770337479886;a=g/95.047;b=h/100.0;c=i/108.883;a=a<216.0/24389.0?((841.0/108.0)*a)+(4.0/29.0):Math.cbrt(a);b=b<216.0/24389.0?((841.0/108.0)*b)+(4.0/29.0):Math.cbrt(b);c=c<216.0/24389.0?((841.0/108.0)*c)+(4.0/29.0):Math.cbrt(c);g=(116.0*b)-16.0;h=500.0*(a-b);i=200.0*(b-c);d/=255.0;e/=255.0;f/=255.0;d=d<s?d/12.92:Math.pow((d+0.055)/1.055,2.4);e=e<s?e/12.92:Math.pow((e+0.055)/1.055,2.4);f=f<s?f/12.92:Math.pow((f+0.055)/1.055,2.4);j=d*41.24564390896921145+e*35.75760776439090507+f*18.04374830853290341;k=d*21.26728514056222474+e*71.51521552878181013+f*7.21749933075596513;l=d*1.93338955823293176+e*11.91919550818385936+f*95.03040770337479886;d=j/95.047;e=k/100.0;f=l/108.883;d=d<216.0/24389.0?((841.0/108.0)*d)+(4.0/29.0):Math.cbrt(d);e=e<216.0/24389.0?((841.0/108.0)*e)+(4.0/29.0):Math.cbrt(e);f=f<216.0/24389.0?((841.0/108.0)*f)+(4.0/29.0):Math.cbrt(f);j=(116.0*e)-16.0;k=500.0*(d-e);l=200.0*(e-f);d=(Math.sqrt(h*h+i*i)+Math.sqrt(k*k+l*l))*0.5;d=d*d*d*d*d*d*d;d=1.0+0.5*(1.0-Math.sqrt(d/(d+6103515625.0)));m=Math.sqrt(h*h*d*d+i*i);n=Math.sqrt(k*k*d*d+l*l);o=Math.atan2(i,h*d);p=Math.atan2(l,k*d);o+=2.0*Math.PI*(o<0.0);p+=2.0*Math.PI*(p<0.0);d=Math.abs(p-o);if(Math.PI-1E-14<d&&d<Math.PI+1E-14)d=Math.PI;q=(o+p)*0.5;r=(p-o)*0.5;if(Math.PI<d){r+=Math.PI;q+=Math.PI;}e=36.0*q-55.0*Math.PI;d=(m+n)*0.5;d=d*d*d*d*d*d*d;s=-2.0*Math.sqrt(d/(d+6103515625.0))*Math.sin(Math.PI/3.0*Math.exp(e*e/(-25.0*Math.PI*Math.PI)));d=(g+j)*0.5;d=(d-50.0)*(d-50.0);f=(j-g)/(k_l*(1.0+0.015*d/Math.sqrt(20.0+d)));a=1.0+0.24*Math.sin(2.0*q+Math.PI*0.5)+0.32*Math.sin(3.0*q+8.0*Math.PI/15.0)-0.17*Math.sin(q+Math.PI/3.0)-0.20*Math.sin(4.0*q+3.0*Math.PI/20.0);d=m+n;b=2.0*Math.sqrt(m*n)*Math.sin(r)/(k_h*(1.0+0.0075*d*a));c=(n-m)/(k_c*(1.0+0.0225*d));return Math.sqrt(f*f+b*b+c*c+c*b*s);};

Fichiers à télécharger

Un fichier ci-dessous prend en charge les calculs en prĂ©cision arbitraire en JavaScript (utile si vous traitez ΔE2000 en mĂ©trologie). Utilisez librement ces fichiers mis Ă  disposition par Michel, mĂȘme Ă  des fins commerciales.

Statistiques du site : téléchargements
FichierTailleNombre de clics
ciede-2000.js4 KB204
ciede-2000-driver.js7 KB144
ciede-2000-multiprecision.js37 KB163
ciede-2000-random.js6 KB131
compare-hex-colors.js8 KB132
compare-rgb-colors.js8 KB135
test-js-multiprecision.yml7 KB104
test-js.yml3 KB88
vs-chroma.yml4 KB88
vs-npm-delta-e.yml4 KB86
reference-dataset.txt4 KB606
Cliquez sur js.zip pour télécharger tous ces fichiers dans une archive.

Communauté

Que pensez-vous de ce code source ou de CIEDE2000 ? Votre avis nous intĂ©resse ! Ce site web spĂ©cialisĂ© comporte un livre d’or qui inclut dĂ©jĂ  9 messages dont 1 en français. Faites-y un tour et partagez votre avis.