Implementazione di CIEDE2000 in JavaScript
| Numero di visite | 697 |
|---|---|
| Numero di file visualizzati | 766 + 321 |
Questa pagina presenta un’implementazione di riferimento della formula della differenza cromatica CIEDE2000 in JavaScript. Se si desidera ottenere una corrispondenza esatta con le implementazioni di terze parti fino a 10 cifre decimali, potrebbe essere necessario apportare alcune modifiche al codice sorgente, in particolare (de)commentando alcune righe, che possono essere applicate automaticamente tramite il link sottostante.
La funzione ΔE2000 in JavaScript
Consideriamo la più comune e accademica (Sharma, 2005) delle due formulazioni.
// 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.
// 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.
function ciede_2000(l_1, a_1, b_1, l_2, a_2, b_2, k_l, k_c, k_h, canonical) {
"use strict"
// Working in JavaScript 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...
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;
// 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 - Math.sqrt(n / (n + 6103515625.0)));
// Application of the chroma correction factor.
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);
// atan2 is preferred over atan because it accurately computes the angle of
// a point (x, y) in all quadrants, handling the signs of both coordinates.
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);
// Cross-implementation consistent rounding.
if (Math.PI - 1E-14 < n && n < Math.PI + 1E-14)
n = Math.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.
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;
// The hue rotation correction term is designed to account for the
// non-linear behavior of hue differences in the blue region.
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);
// Lightness.
var l = (l_2 - l_1) / ((k_l || 1.0) * (1.0 + 0.015 * n / Math.sqrt(20.0 + n)));
// These coefficients adjust the impact of different harmonic
// components on the hue difference calculation.
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;
// Hue.
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));
// Returning the square root ensures that dE00 accurately reflects the
// geometric distance in color space, which can range from 0 to around 185.
return Math.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 = 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, ...)
// Deviation between implementations ≈ 1.5e-5
// See the source code comments for easy switching between these two widely used ΔE*00 implementation variants.Il vostro test di performance
Precisione e affidabilità del codice sorgente
La differenza tra le formulazioni di Sharma e Lindbloom non supera mai ±0,0003 sul ΔE2000 finale, il che corrisponde alla differenza abituale misurata tra due implementazioni a 32 bit ed è impercettibile a occhio umano. Le nostre implementazioni a 64 bit, tutte coerenti tra loro, garantiscono almeno 10 cifre decimali corrette, quindi la scelta di una formulazione rispetto a un’altra dipende principalmente dall’interoperabilità desiderata. La formulazione che appare di default in questa pagina è la più comunemente utilizzata (il suo micro-vantaggio è che si basa sulla comunità ed è più leggera del suo analogo quando viene vettorializzata).
✎ Se trovate un commento nel codice sorgente che non corrisponde a un’altra lingua, informate l’autore del sito, che studierà il vostro suggerimento e lo incorporerà nel codice sorgente.
Come si convertono i colori RGB in L*a*b*?
Per la conversione è necessario utilizzare lo spazio colore intermedio XYZ; se avete bisogno di aiuto, il codice sorgente è disponibile in fondo a questa pagina (utilizzando il punto di bianco D65 formalizzato nel 1964).
Intervalli di valori in CIELAB e interpretazione del ΔE2000
Nello spazio colore CIELAB, la componente L* rappresenta la luminosità e varia tipicamente da 0 (nero) a 100 (bianco). Le componenti a* e b* definiscono gli assi cromatici: a* va dal verde al rosso, mentre b* va dal blu al giallo. In pratica, i valori di a* e b* si collocano solitamente tra -128 e +127, anche se possono superare leggermente questi limiti in base alle conversioni cromatiche.
| Colore 1 | Colore 2 | Valore di ΔE2000 |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 |
| Colore 1 | Colore 2 | Valore di ΔE2000 |
|---|---|---|
| 5 | ||
| 10 | ||
| 15 |
Parametri k_l, k_c e k_h
I parametri k_l, k_c e k_h sono fattori di ponderazione applicati ai termini di luminosità (ΔL*), croma (ΔC*) e tinta (ΔH*) nella formula CIEDE2000. Il loro valore predefinito è 1, che corrisponde alle condizioni di osservazione standard raccomandate dalla Commissione internazionale per l’illuminazione. In pratica, questi coefficienti vengono regolati per riflettere condizioni specifiche: ad esempio, k_l = 2 viene talvolta utilizzato per dare maggiore peso alle differenze di luminosità (comune nella stampa), mentre k_c o k_h possono essere ridotti per aumentare la tolleranza alle variazioni di saturazione o tinta a seconda delle esigenze del controllo qualità. A seconda del contesto, questi coefficienti variano tipicamente tra 0,5 e 2.
ΔE2000 (CIEDE2000) misura la differenza percepita tra due colori: 0 significa colori identici, e valori più alti (fino a circa 185 nei casi estremi) indicano una differenza più evidente. Per esempio, un ΔE2000 intorno a 5 indica colori vicini, mentre intorno a 15 indica colori chiaramente distinti.
Esempio di utilizzo in 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.2895721943I risultati dei test
Il driver scritto in linguaggio C99, con 250 test statici precisi, ha dimostrato che questa funzione JavaScript è interoperabile con la funzione CIEDE2000 disponibile in altri linguaggi di programmazione.
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⚡ Confronto tra colori RGB e esadecimali per il Web
Solo 3 KB – Semplice. Veloce. Questa funzione JavaScript compatta accetta due colori nei formati RGB o esadecimale e restituisce la differenza di colore CIE ΔE2000 utilizzando l’illuminante 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);};
File da scaricare
Un file qui sotto supporta i calcoli di precisione arbitraria in JavaScript (utile se si ha a che fare con il ΔE2000 in metrologia). Sentitevi liberi di utilizzare questi file messi a disposizione da Michel, anche per scopi commerciali.
| File | Dimensione | Numero di clic |
|---|---|---|
| ciede-2000.js | 4 KB | 106 |
| ciede-2000-driver.js | 7 KB | 84 |
| ciede-2000-multiprecision.js | 37 KB | 82 |
| ciede-2000-random.js | 6 KB | 75 |
| compare-hex-colors.js | 8 KB | 77 |
| compare-rgb-colors.js | 8 KB | 77 |
| test-js-multiprecision.yml | 5 KB | 68 |
| test-js.yml | 3 KB | 66 |
| vs-chroma.yml | 4 KB | 66 |
| vs-npm-delta-e.yml | 4 KB | 65 |
| reference-dataset.txt | 4 KB | 321 |
| Fai clic su js.zip per scaricare tutti i file in un archivio. | ||
Comunità
Se volete lasciare la vostra opinione su questo codice sorgente JavaScript o sul CIEDE2000 in generale, il libro degli ospiti contiene già 1 messaggi in italiano e 9 messaggi in totale, quindi fateci sapere cosa ne pensate.