5.19. Correzioni tonali

Le correzioni tonali modificano il modo in cui luminosità e colore sono distribuiti in un’immagine catturata – gli interventi che un’applicazione applica quando un frame è troppo scuro, troppo chiaro, troppo piatto o sbilanciato verso il colore sbagliato.

Le correzioni appartengono a due famiglie: le regolazioni di luminosità e contrasto che ridistribuiscono la luminosità, e le regolazioni di colore che cambiano il colore con cui ogni pixel viene interpretato. Entrambe hanno un equivalente nell”ISP del sensore, che corregge ogni frame man mano che entra; i metodi qui descritti si applicano a un’immagine Image già catturata, a posteriori, per i casi in cui il frame necessita di una correzione maggiore di quella fornita dall’ISP.

5.19.1. Equalizzazione dell’istogramma

L’operazione di stiramento del contrasto più semplice è l”equalizzazione dell’istogramma. L’idea è rimappare i valori dei pixel in modo che l’istogramma dell’output sia il più piatto possibile – ogni valore compare con frequenza pressoché uguale. L’effetto visivo è che un’immagine a basso contrasto (il cui istogramma è concentrato in una banda stretta) diventa un’immagine ad alto contrasto i cui pixel coprono l’intero intervallo 0255.

histeq() esegue l’equalizzazione:

img.histeq()

Il meccanismo è diretto. Viene calcolata la funzione di distribuzione cumulativa (CDF) dell’istogramma della sorgente; il valore di ogni pixel di input viene mappato sulla sua posizione nella CDF, riscalato sull’intervallo di output. Dove i pixel erano già distribuiti uniformemente, la mappatura è vicina all’identità; dove i pixel erano accumulati su un’unica luminosità, la mappatura li distribuisce stirando quella luminosità su un intervallo più ampio di valori di output.

Il risultato è notevole sulle scene a basso contrasto – la differenza tra una fotografia d’interni poco illuminata e la stessa fotografia dopo histeq è spesso la differenza tra «illeggibile» e «perfettamente leggibile». Il compromesso è che l’operazione amplifica tutto, incluso il rumore del sensore. Su una scena con dettagli reali a basso contrasto da recuperare, histeq è la risposta giusta; su una scena pulita e ben esposta che semplicemente non ne ha bisogno, histeq introduce rumore visibile.

5.19.2. CLAHE: equalizzazione adattiva

L’equalizzazione dell’istogramma è globale: usa un’unica CDF calcolata dall’intera immagine e la applica ovunque. Funziona su immagini il cui intervallo di luminosità è all’incirca uniforme, ma fallisce su scene con regioni scure e chiare localizzate – la CDF viene attirata verso il lato che ha più pixel, e il lato opposto viene corretto in eccesso.

La variante adattiva è la Contrast Limited Adaptive Histogram Equalisation, comunemente nota come CLAHE. Invece di un’unica CDF globale, CLAHE calcola una CDF separata per ogni piccola tessera dell’immagine, equalizza ogni tessera rispetto alla propria CDF e fonde insieme i confini delle tessere. Il risultato è che le regolazioni di luminosità avvengono localmente – l’angolo in ombra ottiene la propria equalizzazione senza che l’angolo luminoso lo tiri nella direzione sbagliata.

Il flag adaptive=True commuta histeq() in modalità CLAHE:

img.histeq(adaptive=True, clip_limit=10)

Il parametro clip_limit è la parte di CLAHE a cui si riferisce il «contrast limited» nel nome. L’equalizzazione locale può amplificare eccessivamente il rumore nelle regioni piatte dove la CDF ha pochissimi valori distinti; il clip limit limita quanto aggressivamente un singolo bin può essere ridistribuito, il che previene l’amplificazione del rumore consentendo comunque lo stiramento del contrasto dove conta. Un valore intorno a 10 è un punto di partenza ragionevole; valori più grandi fanno lavorare CLAHE più intensamente al costo di un rumore più visibile, valori più piccoli lo rendono più delicato.

CLAHE è più costoso dell’histeq globale, ma produce risultati più puliti su scene in cui la luminosità è distribuita in modo disomogeneo – ovvero la maggior parte delle scene del mondo reale.

5.19.3. Gamma, contrasto e luminosità

L’equalizzazione dell’istogramma è il modo basato sui dati per rimappare la luminosità. Il modo indipendente dai dati consiste nell’applicare una curva scelta, parametrizzata da pochi controlli facili da regolare. gamma() ne fornisce tre:

img.gamma(gamma=1.0, contrast=1.0, brightness=0.0)

Ogni parametro applica una specifica trasformazione a ciascun pixel:

gamma fa passare il valore di ogni pixel attraverso la funzione di potenza output = input ** (1 / gamma). Il significato standard: valori maggiori di 1.0 schiariscono l’immagine e alzano i mezzitoni (la classica correzione «gamma del monitor»); valori minori di 1.0 la scuriscono. Il parametro è non lineare – preserva i punti di nero e di bianco e rimodella solo la distribuzione intermedia, il che è il comportamento giusto quando l’obiettivo è recuperare dettagli nelle regioni di ombra o di alteluci senza schiacciare gli estremi esistenti.

contrast fa passare ogni pixel attraverso una semplice moltiplicazione attorno al punto di grigio medio. Valori maggiori di 1.0 aumentano il contrasto (lo scuro diventa più scuro, il chiaro più chiaro, il grigio medio resta invariato); valori minori di 1.0 riducono il contrasto.

brightness aggiunge una costante al valore di ogni pixel. Valori positivi schiariscono, valori negativi scuriscono. Lo spostamento è uniforme – nulla viene preservato – il che raramente è ciò che un’applicazione desidera da solo, ma si abbina bene a un passaggio di contrasto per ricentrare il risultato.

I tre parametri si compongono: una singola chiamata a gamma() può applicare una curva gamma, poi una moltiplicazione di contrasto, poi uno spostamento di luminosità, tutto in un’unica passata. L’ordine è prima gamma, poi contrasto, poi luminosità, che corrisponde all’ordine che dà i risultati più intuitivi quando tutti e tre non sono ai valori predefiniti.

5.19.4. Bilanciamento automatico del bianco

La famiglia delle correzioni tonali relative al colore inizia con il bilanciamento automatico del bianco. Lo stesso meccanismo che l’ISP del sensore esegue come parte della pipeline di imaging – regolare i guadagni relativi dei canali rosso, verde e blu in modo che una zona di colore grigio medio venga interpretata come grigio effettivo – è disponibile anche come operazione post-cattura su un’immagine Image finita:

img.awb()

L’impostazione predefinita usa l’algoritmo gray-world: si assume che il colore medio dell’intera immagine sia grigio neutro, e i guadagni per canale vengono regolati per renderlo tale. La forma alternativa max=True usa l’algoritmo white-patch: si assume che i pixel più luminosi siano bianco neutro, e i guadagni vengono regolati per renderli tali. Entrambi funzionano su RGB565 e su Bayer grezzo; nessuno dei due funziona sulla scala di grigi (dove non c’è colore da bilanciare) o su YUV (dove la rappresentazione del colore non è quella su cui operano questi algoritmi).

Quando ricorrere alla forma post-cattura invece del bilanciamento automatico del bianco dell’ISP: quando la scelta dell’ISP era poco adatta alla particolare scena, quando l’applicazione carica frame di riferimento da disco che sono stati catturati in condizioni diverse, o quando la valutazione del colore è abbastanza importante da indurre l’applicazione a rieseguirla con il proprio algoritmo a scelta.

5.19.5. La matrice di correzione del colore

Quando la correzione del colore di cui l’immagine ha bisogno non è il ridimensionamento per canale fornito dal bilanciamento del bianco, ma un mescolamento dei canali più generale, l’operazione a cui ricorrere è la matrice di correzione del colore. Il metodo ccm() applica una matrice 3 per 3 (o 3 per 4 con offset) che moltiplica il vettore (r, g, b) di ogni pixel per produrre un nuovo vettore (r, g, b):

img.ccm([[1.1, -0.05, -0.05],
        [-0.05, 1.1, -0.05],
        [-0.05, -0.05, 1.1]])

La matrice consente all’applicazione di correggere il crosstalk tra i canali di colore – dove la risposta del sensore del rosso include una parte di luce verde, ad esempio, la matrice può sottrarre una frazione del canale verde dall’output del rosso per compensare. Combinata con un offset per canale, la forma 3 per 4 consente all’applicazione anche di riazzerare ogni canale.

Il materiale sulla pipeline ISP tratta il perché delle matrici di correzione del colore. La forma post-cattura sull’immagine Image è semplicemente la stessa operazione, applicata a posteriori.