Un modello di colore è un insieme di regole e convenzioni che permettono di rappresentare i colori in una qualche maniera oggettiva, possibilmente diretta e poco costosa. Il concetto di oggettività è importante in questo contesto, perché nell’uomo la percezione del colore è soggettiva e quindi occorre un modello oggettivo per rappresentarlo. Il modello più conosciuto ed utilizzato è RGB ( Red ( rosso ), Green ( verde ) e Blue ( blu ) ), perché ha una diretta implementazione hardware nei monitor e negli scanner, inoltre risulta semplice indicare soltanto l’intensità dei tre colori basilari, che definiscono il relativo spazio su un solido a tre dimensioni ( cubo ). Se ad esempio abbiamo le intensità dei tre colori che possono andare tra 0 e 1, allora definire un colore è semplicissimo, il nero è 0.0 – 0.0 – 0.0, mentre il bianco è 1.0 – 1.0 – 1.0, i valori in questo caso devono essere rappresentati da tipi di dato float, come avviene nella creazione dei colori nei motori 3D più noti. Nei colori per il web invece si usano valori esadecimali: 000000 per il nero, ffffff per il bianco, ff0000 per il rosso, 00ff00 per il verde, 0000ff per il blu e ffff00 per il giallo.
Questo modello presenta dei lati positivi di semplicità, perché è costruito su una semplice proprietà additiva dei colori basilari, ma una scarsa usabilità, difatti non rappresenta fedelmente la maniera naturale utilizzata dalle persone per effettuare delle deduzioni sui colori, mancando così di intuitività e praticità d’uso. Per fare un esempio, dato il colore giallo ( 1.0 – 1.0 – 0.0 ) non è immediatamente comprensibile quale sia la direzione all’interno del cubo per ottenere un marroncino chiaro ( 0.7 – 0.6 – 0.3 ). Fortunatamente per applicazioni grafiche abbiamo vari sistemi di selezione dei colori dal cubo ( immagine in alto ) oppure un quadrato 2D con un altro rettangolo in fondo 2D per l’intensità del colore prescelto.
La maniera più naturale per rappresentare sulle macchine la tripletta RGB consiste nell’impiegare 1 byte ( 8 bit formati da 0 o 1 ) per ciascun componente. Avremo così 256 ( 2 alla 8 ) possibili sfumature di rosso, verde e blu. Questo numero, apparentemente basso, non deve trarre in inganno poiché in realtà permette di specificare 16,7 milioni di colori, dal momento che le tre componenti devono essere considerate unitamente. Infatti abbiamo un totale di 3 byte ( 24 bit ) per ciascun colore ( profondità a 24 bit ), 2 alla 24 sono infatti i 16,7 milioni di colori. Spesso è possibile trovare formati a 32 bit, dove in pratica vengono impiegati 4 byte al posto di 3, perché per i processori, soprattutto Intel, risulta più veloce copiare 4 byte alla volta piuttosto che 3; inoltre il byte aggiuntivo viene spesso sfruttato per definire proprietà addizionali sui pixel, come ad esempio la trasparenza. Questa abbondanza di colori rappresentabili è solo una stima ottimistica sulle potenzialità dell’occhio umano, che difatti non riesce a discernere più di 7 o 8 milioni di sfumature dall’intero spettro visibile, per questo applicazioni critiche in termini di velocità sono create utilizzando 16 bit, sfruttando solo 2 byte. Questi 2 byte vengono suddivisi in gruppetti di bit associati a ciascun componente. Ad esempio con la dicitura 5:5:6 si intende specificare che 5 bit vengono usati dal rosso e dal verde, mentre 6 dal blu. Ovviamente la rappresentazione a 16 bit fornisce un numero molto più basso di colori esprimibili, esattamente 65536 ( 2 alla 16 ), ma permette un notevole risparmio di spazio e tempo.
Il modello HLS ( Hue ( tinta ), Lightness ( luminosità ) e Saturation ( saturazione ) ) è in realtà facente parte di una famiglia di modelli sviluppata per essere user-oriented e per rappresentare in maniera più o meno fedele il naturale modo di interpretare i colori da parte dell’essere umano. Analizziamo le singole componenti di questo modello:
- Hue – letteralmente tinta ed indica il nome del colore utilizzato: ad esempio il rosso, il giallo o il nero.
- Lightness – indica la luminosità, ovvero quanto è brillante una particolare tinta. Per fare un esempio, il bianco è un colore dotato di massima luminosità, mentre il nero è un colore decisamente spento.
- Saturation – indica la saturazione, che è la purezza del colore o la quantità di bianco mescolata al colore. Ad esempio, il rosso è un colore saturo, il rosa invece è una tinta mediamente satura, un grigio ha una saturazione prossima allo zero.
Come potete capire da questa prima introduzione sul modello HLS, si parte dal colore base e poi si applica la luminosità e la saturazione per avere una particolare sfumatura, molto più intuitivo quindi del modello RGB. Anche in questo caso abbiamo una tripletta di valori ( h, l, s ) per indicare una precisa tonalità. La componente H assume valori tra 0 e 360 ( i gradi di un cerchio ); da notare che i colori complementari come il giallo ed il blu sono di 180 valori l’uno dall’altro, quindi nella circonferenza sono opposti.
La componente L e quella S assumono invece valori tra 0.0 e 1.0.
Lo spazio di colore per il modello HLS è più complicato perché non tutte le variabili hanno lo stesso range di valori, quindi per comprendere il tipo di spazio risultante, immaginate di prendere due coni e di unirli alle basi.
In questo doppio cono l’asse verticale è la componente L; all’estremità inferiore è posto il nero con luminosità pari a 0, all’opposto è situato il bianco con luminosità 1. La componente H è calcolata intorno a quest’asse, mentre la componente S è la lunghezza del raggio, ovvero la distanza dall’asse verticale rispetto al punto in cui siamo ( vedi immagine sopra ). Da notare che la scelta delle sfumature tende a diminuire dal centro agli estremi e su ogni possibile cerchio il rosso è posto sempre a 0 gradi, il blu a 120 gradi ed il verde a 240 gradi e così via. I grigi si ottengono sempre ponendo S = 0 e le tonalità maggiormente sature si ottengono con S = 1 ed L = 0.5. Per il bianco basta porre L = 1, mentre per il nero è sufficiente porre L = 0.
La difficoltà maggiore nell’implementazione del modello HLS è che i normali dipositivi hardware ragionino sul modello RGB e quindi occorre fare la conversione tra i due modelli di colore. Per questo compito si usa il noto algoritmo di Foley, che è molto veloce ( vi sono al più 3 divisioni ).
// Si assume che r, g e b siano definiti tra 0 e 1 void RGB2HLS (float r, float g, float b, float* h, float* l, float* s) { float max_rgb = max(r, g, b); float min_rgb = min(r, g, b); // Componente L *l = (max_rgb + min_rgb) / 2.0; // Componente S if (max_rgb == min_rgb) { *s = 0; *h = UNDEFINED; } else { float delta = max_rgb - min_rgb; *s = (*l <= 0.5) ? (delta / (max_rgb + min_rgb)) : (delta / (2.0 - (max_rgb + min_rgb))); // Componente H if (r == max_rgb) *h = (g - b) / delta; else if (g == max_rgb) *h = 2.0 + (b - r) / delta; else if (b == max_rgb) *h = 4.0 + (r -g) / delta; // Conversione in gradi *h *= 60.0; if (*h < 0.0) *h += 360.0; } }
In questo articolo abbiamo descritto i due modelli di colore più noti, quello RGB e quello HLS. Abbiamo visto come il primo sia caratterizzato da una particolare semplicità d’implementazione, mentre il secondo permetta una selezione dei colori decisamente più naturale ed intuitiva.