Supponiamo che tu stia lavorando con i colori RGB: ogni colore è rappresentato con tre intensità o luminosità. Devi scegliere tra "RGB lineare" e "sRGB". Per ora, semplificheremo le cose ignorando le tre diverse intensità e assumendo che tu abbia solo un'intensità: cioè, hai a che fare solo con sfumature di grigio.
In uno spazio colore lineare, la relazione tra i numeri memorizzati e le intensità che rappresentano è lineare. In pratica, questo significa che se raddoppi il numero, raddoppi l'intensità (la luminosità del grigio). Se vuoi aggiungere due intensità insieme (perché stai calcolando un'intensità in base ai contributi di due sorgenti luminose, o perché stai aggiungendo un oggetto trasparente sopra un oggetto opaco), puoi farlo semplicemente aggiungendo il due numeri insieme. Se stai eseguendo qualsiasi tipo di fusione 2D o ombreggiatura 3D, o quasi qualsiasi elaborazione delle immagini, allora vuoi le tue intensità in uno spazio colore lineare, quindi puoi semplicemente aggiungere, sottrarre, moltiplicare e dividere i numeri per avere lo stesso effetto sulle intensità. La maggior parte degli algoritmi di elaborazione e rendering del colore fornisce risultati corretti solo con RGB lineare, a meno che non si aggiungano pesi extra a tutto.
Sembra davvero facile, ma c'è un problema. La sensibilità dell'occhio umano alla luce è più fine a basse intensità rispetto alle alte intensità. Vale a dire, se fai un elenco di tutte le intensità che puoi distinguere, ce ne sono più di quelle scure di quelle chiare. Per dirla in un altro modo, puoi distinguere le sfumature scure del grigio meglio di quanto puoi fare con le tonalità chiare del grigio. In particolare, se stai usando 8 bit per rappresentare la tua intensità, e lo fai in uno spazio colore lineare, ti ritroverai con troppe tonalità chiare e non abbastanza tonalità scure. Ottieni bande nelle tue aree scure, mentre nelle tue aree chiare, stai sprecando bit su diverse tonalità di quasi bianco che l'utente non può distinguere.
Per evitare questo problema e fare il miglior uso di questi 8 bit, tendiamo a usare sRGB . Lo standard sRGB ti dice una curva da usare per rendere i tuoi colori non lineari. La curva è meno profonda nella parte inferiore, quindi puoi avere più grigi scuri e più ripida nella parte superiore, in modo da avere meno grigi chiari. Se raddoppi il numero, raddoppi più che l'intensità. Ciò significa che se aggiungi i colori sRGB insieme, ottieni un risultato più chiaro di quanto dovrebbe essere. Oggigiorno, la maggior parte dei monitor interpreta i colori di input come sRGB. Quindi, quando metti un colore sullo schermo o lo memorizzi in una texture a 8 bit per canale, memorizzalo come sRGB , in modo da sfruttare al meglio quegli 8 bit.
Noterai che ora abbiamo un problema: vogliamo che i nostri colori vengano elaborati in uno spazio lineare, ma memorizzati in sRGB. Ciò significa che si finisce per eseguire la conversione da sRGB a lineare in lettura e la conversione da lineare a sRGB in scrittura. Come abbiamo già detto, le intensità lineari a 8 bit non hanno abbastanza scuri, questo causerebbe problemi, quindi c'è un'altra regola pratica: non usare colori lineari a 8 bit se puoi evitarlo. Sta diventando convenzionale seguire la regola secondo cui i colori a 8 bit sono sempre sRGB, quindi esegui la conversione da sRGB a lineare contemporaneamente allargando l'intensità da 8 a 16 bit o da intero a virgola mobile; allo stesso modo, quando hai terminato l'elaborazione in virgola mobile, riduci a 8 bit contemporaneamente alla conversione in sRGB. Se segui queste regole,
Quando leggi un'immagine sRGB e desideri intensità lineari, applica questa formula a ciascuna intensità:
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
Andando dall'altra parte, quando vuoi scrivere un'immagine come sRGB, applica questa formula a ciascuna intensità lineare:
float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )
In entrambi i casi, il valore in virgola mobile va da 0 a 1, quindi se stai leggendo interi a 8 bit devi prima dividere per 255 e se stai scrivendo interi a 8 bit vuoi moltiplicare per 255 Infine, come faresti normalmente. Questo è tutto ciò che devi sapere per lavorare con sRGB.
Finora ho affrontato una sola intensità, ma ci sono cose più intelligenti da fare con i colori. L'occhio umano può distinguere le diverse luminosità meglio di diverse tinte (più tecnicamente, ha una risoluzione della luminanza migliore della crominanza), quindi puoi fare un uso ancora migliore dei tuoi 24 bit memorizzando la luminosità separatamente dalla tinta. Questo è ciò che cercano di fare le rappresentazioni YUV, YCrCb, ecc. Il canale Y è la luminosità complessiva del colore e utilizza più bit (o ha una risoluzione spaziale maggiore) rispetto agli altri due canali. In questo modo, non è (sempre) necessario applicare una curva come si fa con le intensità RGB. YUV è uno spazio colore lineare, quindi se raddoppi il numero nel canale Y, raddoppi la luminosità del colore, ma non puoi aggiungere o moltiplicare i colori YUV insieme come puoi con i colori RGB, quindi '
Penso che questo risponda alla tua domanda, quindi concludo con una breve nota storica. Prima di sRGB, i vecchi CRT avevano una non linearità incorporata. Se raddoppi la tensione di un pixel, raddoppi più che l'intensità. Quanto di più era diverso per ogni monitor e questo parametro era chiamato gamma . Questo comportamento era utile perché significava che si potevano ottenere più ombre che luci, ma significava anche che non si poteva dire quanto sarebbero brillanti i colori sul CRT dell'utente, a meno che non lo si calibrasse prima. Correzione gammasignifica trasformare i colori con cui si inizia (probabilmente lineare) e trasformarli per la gamma del CRT dell'utente. OpenGL proviene da questa era, motivo per cui il suo comportamento sRGB a volte è un po 'confuso. Ma i fornitori di GPU ora tendono a lavorare con la convenzione che ho descritto sopra: che quando memorizzi un'intensità a 8 bit in una texture o framebuffer, è sRGB e quando stai elaborando i colori, è lineare. Ad esempio, un OpenGL ES 3.0, ogni framebuffer e texture ha un "flag sRGB" che puoi attivare per abilitare la conversione automatica durante la lettura e la scrittura. Non è necessario eseguire esplicitamente la conversione sRGB o la correzione gamma.