Pagina | 6
Capitolo 1: Concetto di Eye-Traking e la libreria OpenCV
1.1 Eye-Tracking
Con il termine Eye-Tracking, si definisce la tecnica di registrazione ed analisi dei movimenti
oculari, utilizzata in aree quali le scienze cognitive, la psicologia, l’interazione uomo-
computer (Human-Computer Interaction, o HCI), le ricerche di mercato, la ricerca medica, ed
altre ancora.
La maggior parte degli eye-trackers, utilizzano quale tecnologia di base la“Video-oculografia”
(VOG), cioè la registrazione della posizione dell’occhio nell’orbita e dei suoi movimenti per
il tramite di una camera digitale.
Gli eye-trackers più moderni utilizzano inoltre, un sistema d'illuminazione del volto (e dunque
anche degli occhi) basato su luce infrarossa (IR) o vicina all’infrarosso (NIR), per meglio
delineare il contorno della pupilla e per ottenere uno (o più) riflessi, noti come “riflessi
corneali” (Corneal Reflex, CR).
Il vettore risultante dalla relazione (dinamica) di questi due parametri (in seguito ad una
procedura detta di calibrazione) può essere utilizzato per ottenere la posizione dello sguardo,
rispetto ad un determinato elemento del display.
Infine, il progetto di Eye-Tracking è inquadrato nell'ottica dell'Assisted Living, cioè quello di
vita assistita per facilitare le persone che hanno handicap permanenti, come possono essere
persone paralizzate o affette da SLA (sclerosi laterale amiotrofica). [5]
1.2 Libreria OpenCV
La libreria denominata OpenCV (Open Source Computer Vision Library), contiene funzioni
apposite che permettono di eseguire operazioni di computer vision.
E’ una libreria scritta in C e C++, costituita da oltre 500 funzioni utili nel campo dell’image
processing e della computer vision.
La libreria è strutturata in cinque sottolibrerie (dette anche moduli), ciascuna con funzionalità
specifiche.
Il modulo cxcore.h è quello principale nonchè indispensabile.
Contiene le strutture dati di base con le rispettive funzioni d'inizializzazione, le funzioni
matematiche, le funzioni di lettura, scrittura e memorizzazione dati, le funzioni di sistema e di
gestione degli errori.
Pagina | 7
Il modulo cv.h, contiene le funzioni riguardanti l'image processing, le funzioni di analisi
strutturale e del moto, di object detection e ricostruzione 3D.
Il modulo highgui.h, contiene le funzioni GUI (Graphical User Interface) e quelle di
salvataggio e caricamento immagini, nonchè le funzioni di acquisizione video e di gestione
delle telecamere.
Il modulo ml.h (Machine Learning), contiene classi e funzioni relative all’implementazione e
gestione di reti neurali, in particolare di tipo Multilayer perceptrons (Mpl), di classificazione
statistica e clustering di dati.
Infine, vi è il modulo cvaux.h, che contiene, sia le funzioni basate su algoritmi ancora in fase
di sperimentazione, sia le funzioni considerate obsolete e quindi non più supportate.
Le funzionalità di tale modulo sono rivolte alla corrispondenza stereo, al tracking 3D, al
riconoscimento degli oggetti.
Queste funzioni sono impiegate nel miglioramento della qualità delle immagini, nel controllo
di prodotti industriali e più in generale come passo di pre-elaborazione nei procedimenti di
computer vision.
Punti di forza della libreria sono la completezza ed il suo essere open source.
Il fatto di essere liberamente distribuita, garantisce sicurezza al codice e la possibilità di
apportarne modifiche, assicurandone così una continua evoluzione.
La licenza di distribuzione è priva di royalty e ciò consente il suo utilizzo anche in prodotti
commerciali a condizione di mantenere le note di copyright.
La portabilità della libreria OpenCV è completa, sono infatti disponibili le versioni per i
sistemi MS-Windows, Linux, BSD, Unix e MacOSX. [1]
Pagina | 8
Capitolo 2:Trasformazioni ed operazioni applicate
all'algoritmo di Eye-Tracking
2.1 Trasformata di Haar
La trasformazione di Haar applicata per il riconoscimento di parti del volto è basata
sull'algoritmo di Viola-Jones, il cui scopo principale è quello di eseguire una scansione di una
sotto-finestra, in grado di rilevare volti relativi ad una data immagine in ingresso.
L'approccio di elaborazione standard, sarebbe quello di ridimensionare l'immagine in ingresso
e quindi eseguire il rilevamento a dimensione fissa attraverso queste immagini.
Quest'approccio risulta essere piuttosto dispendioso in termini di tempo per il calcolo delle
immagini a differenti dimensioni.
Contrariamente a quanto l'approccio standard Viola-Jones potrebbe far pensare, riscalare il
rivelatore anziché l'immagine in ingresso ed eseguirlo molte volte attraverso l'immagine ogni
volta con una dimensione diversa, produce benefici.
Viola e Jones, hanno messo a punto un rivelatore scale invariant (invariante rispetto alla
scala), che richiede lo stesso numero di calcoli qualunque sia la dimensione.
Questo rivelatore è costruito utilizzando un cosiddetto integrale immagine e delle semplici
caratteristiche rettangolari.
Il primo passo dell'algoritmo di rilevamento dei volti, è quello di trasformare l'immagine in
ingresso, in un'immagine integrale.
Tutto questo è fatto ponendo ogni pixel, pari alla somma intera di tutti i pixel collocati in alto
a sinistra rispetto al pixel in questione.
Fig. 2.1 rappresenta l'immagine in ingresso (a sinistra) e l'immagine integrale (a destra).
Pagina | 9
Ciò permette il calcolo della somma di tutti i pixel all'interno di qualsiasi rettangolo dato,
utilizzando solo quattro valori.
Questi valori sono i pixel dell'immagine integrale che coincidono con gli angoli del rettangolo
nell'immagine in ingresso.
Fig. 2.2 rappresenta il calcolo della somma dei rettangoli in scala di grigio.
La somma di A deve essere aggiunta al calcolo poichè, viene sottratta due volte considerando
entrambi i rettangoli B e C che includono il rettangolo A.
E' stato dimostrato come la somma di pixel all'interno di rettangoli di dimensioni arbitrarie,
può essere calcolata in un tempo costante.
Il rilevatore del volto Viola-Jones analizza una determinata sotto-finestra utilizzando
caratteristiche costituite da due o più rettangoli.
Le differenti tipologie di caratteristiche sono le seguenti:
Fig. 2.3 rappresenta le differenti tipologie di caratteristiche.
Ogni caratteristica si traduce in un singolo valore che è calcolato sottraendo la somma del
rettangolo bianco dalla somma del rettangolo nero.
Viola-Jones hanno scoperto empiricamente che, tramite un rilevatore con una risoluzione base
di 24 * 24 pixel si ottengono risultati soddisfacenti.
Questo permette un totale di circa 160.000 caratteristiche diverse che possono essere costruite
andando a considerare tutti i formati e le posizioni possibili.
Pagina | 10
Queste caratteristiche possono sembrare troppo semplici per eseguire un compito così
avanzato come il face detection mancando nella complessità, bensì guadagnando certamente
in efficienza computazionale.
Naturalmente, operazioni possono essere effettuate anche direttamente sui pixel, ma la
variazione dovuta ad un'espressione del viso (detta anche posa) ed a caratteristiche individuali
diverse potrebbe ostacolare questo approccio.
L'obiettivo è quello di costruire un insieme di caratteristiche in grado di rilevare i volti. [4]
2.1.1 L'algoritmo Ada-Boost Modificato
Tra tutte queste caratteristiche che possono essere calcolate utilizzando un rilevatore con
risoluzione base, solo poche permettono di ottenere valori costantemente elevati sul volto.
Al fine di trovare queste caratteristiche, Viola-Jones utilizzano una versione modificata
dell'algoritmo AdaBoost sviluppato da Freund e Schapire.
AdaBoost è una macchina che permette il potenziamento dell'algoritmo, in grado di costruire
un classificatore forte attraverso una combinazione ponderata di classificatori deboli di
apprendimento.
Un classificatore debole è matematicamente descritto come:
ove è una sotto-finestra di dimensione pari a 24x24 pixel, è la caratteristica applicata, la
polarità e è la soglia, che decide se può essere classificato come un positivo (un volto) o
un negativo (un non volto).
Dal momento che solo una piccola quantità dei 160.000 valori possibili possono essere
potenziali classificatori deboli, nell'algoritmo AdaBoost modificato si selezionano solo le
migliori caratteristiche.
L'algoritmo AdaBoost modificato consta delle seguenti fasi:
1. si prende una data immagine
dove
, a
seconda che i volti vengano etichettati come negativi (non volti) oppure
positivi (volti);
2. si inizializzano i pesi
, per
, dove m ed l sono il
numero di positivi e negativi;
3. per
Pagina | 11
si normalizzano i pesi,
;
si seleziona il miglior classificatore debole rispetto all'errore ponderato:
si definisce
, dove
,
e
sono i minimi di
;
si aggiornano i pesi
, dove
se
è classificato
correttamente, altrimenti vale
, inoltre
;
infine, il classificatore forte è il seguente:
dove
.
Una parte importante dell'algoritmo AdaBoost modificato, è la determinazione delle migliori
caratteristiche, polarità e soglia.
La determinazione di ogni nuovo classificatore debole coinvolge ogni caratteristica con cui il
classificatore è stato addestrato, al fine di trovarne la migliore.
La migliore caratteristica in termini prestazionali è scelta in base all'errore ponderato che
produce.
Questo errore è funzione dei pesi appartenenti agli esempi di training.
Il punto è che i pesi sono una parte vitale del meccanismo dell'algoritmo Ada-Boost.
Infine, l'immagine integrale e le caratteristiche computazionalmente efficienti garantiscono
che, il rivelatore del viso dell'algoritmo AdaBoost modificato sia pronto per
l'implementazione. [4]
Pagina | 12
2.1.2 Il classificatore a cascata
Il principio alla base dell'algoritmo di Viola -Jones per la rilevazione del volto è quello di fare
una scansione del rilevatore più volte attraverso la stessa immagine, ogni volta con una nuova
dimensione.
Anche se un'immagine potrebbe contenere una o più facce, è ovvio che un valore eccessivo
nelle dimensioni delle sotto-finestre valutate produrrebbe ancora negativi (non volti).
Questa realizzazione conduce ad una diversa formulazione del problema.
Infatti, invece di trovare i volti, l'algoritmo scarta i non volti.
Il pensiero alla base di questa affermazione è quello per cui è più veloce scartare un non-volto
che trovarne uno. Di conseguenza, un rivelatore costituito da un solo classificatore forte
sembra improvvisamente inefficiente. Da qui, si pone la necessità di un classificatore a
cascata.
Esso è composto da fasi contenenti ciascuno un classificatore forte.
Il lavoro di ogni fase è quello di determinare se una data sotto-finestra è certamente un non
volto o forse un volto.
Quando una sotto-finestra relativa ad una determinata fase è classificata come un non-volto, è
immediatamente scartata.
Al contrario, una sotto-finestra classificata come forse un volto è passata alla fase successiva
della cascata.
Ne consegue che, più fasi ogni sotto-finestra riuscirà a passare, maggiore sarà la possibilità
che essa possa contenere in realtà un volto.
Il concetto è illustrato in due passaggi nella Fig. 2.4.
Fig. 2.4 rappresenta le fasi per il riconoscimento di un volto nell'immagine.
In una singola fase, un classificatore accetterebbe normalmente falsi negativi, al fine di ridurre
il tasso di falsi positivi.
Tuttavia per i primi passaggi, nel classificatore non sono considerati i falsi positivi come un
problema in quanto, nelle fasi successive questo sarà risolto.
Pagina | 13
Pertanto, Viola-Jones prescrive l'accettazione di molti falsi positivi nelle fasi iniziali.
Di conseguenza, i falsi negativi nelle fasi finali del classificatore dovrebbero essere molto
piccoli.
La denominazione attentional cascade implica che maggior potenza di calcolo è diretta verso
le regioni dell'immagine che probabilmente conterranno dei volti. [4]
Il prototipo della funzione cvHaarDetectObjects che implementa la trasformata di Haar è
costituito dai seguenti parametri in ingresso:
(CvSeq*) cvHaarDetectObjects( const CvArr* image,CvHaarClassifierCascade* cascade,
CvMemStorage* storage, double scale_factor CV_DEFAULT(1.1),
int min_neighbors CV_DEFAULT(3), int flags CV_DEFAULT(0),
CvSize min_size CV_DEFAULT(cvSize(0,0)))
image identifica l'immagine su cui si rilevano oggetti;
cascade identifica il classificatore in cascata di Haar nella rappresentazione interna;
storage identifica un archivio di memoria sul quale viene memorizzata la sequenza
dei rettangoli degli oggetti candidati;
scale_factor identifica il fattore con il quale la finestra di ricerca è scalata nelle
scansioni successive, per esempio 1.1 significa incrementare la dimensione della
finestra del 10% per ogni scansione;
min_neighbors identifica il numero minimo di rettangoli vicini che creano un oggetto.
Tutti i gruppi di rettangoli sotto questo valore vengono rifiutati.
Se questa variabile assume valor nullo, la funzione non ritorna gruppi di rettangoli,
bensì ritorna tutti quelli rilevati; i quali, possono essere utili all'utente se vuole
applicare una procedura di raggruppamento personalizzata;
flags identifica la modalità di funzionamento. Ne esistono quattro diverse:
CV_HAAR_DO_CANNY_PRUNING identifica una modalità di ricerca dei
contorni utilizzando un algoritmo di edge detection detto Canny;
CV_HAAR_SCALE_IMAGE identifica una modalità di ricerca sull'immagine
scalata;
CV_HAAR_FIND_BIGGEST_OBJECT identifica una modalità di ricerca, la
quale permette di trovare l'oggetto più grande nell'immagine;
Pagina | 14
CV_HAAR_DO_ROUGH_SEARCH identifica una modalità di ricerca
grossolana all'interno dell'immagine.
L'unico flag che può essere specificato è CV_HAAR_DO_CANNY_PRUNING.
Se esso è impostato, la funzione utilizza un rilevatore di contorni denominato
Canny, che rifiuta alcune regioni dell'immagine che contengono pochi o troppi
contorni e quindi non possono contenere l'oggetto considerato. In questo caso,
il pruning accelera il processamento.
min_size identifica la minima dimensione della finestra di ricerca.
L'impostazione predefinita stabilisce le dimensioni dei campioni con cui il
classificatore è stato istruito alla dimensione di partenza [20x20] pixel per il face
detection.
Invece, come valore di ritorno della funzione abbiamo un insieme di rettangoli presenti in una
data immagine, all'interno della quale sono contenuti oggetti, per i quali il classificatore è
stato istruito.
A seconda di quale sia l'obiettivo della rilevazione dell'oggetto nell'immagine, si possono
settare i parametri in ingresso alla funzione in modo diverso.
Infatti, se sono utilizzati parametri predefiniti per il rilevamento accurato di oggetti, allora
vengono posti (scale_factor = 1.1, min_neighbors = 3, flags = 0), mentre se si vuole
un'operazione più veloce su immagini video in real time, i parametri sono settati nel seguente
modo (scale_factor = 1.2, min_neighbors = 2, flags = CV_HAAR_DO_CANNY_PRUNING,
min_size= dimensione del volto più piccola possibile). [12]
Nella Fig. 2.5, viene evidenziato il rettangolo ritornato tramite l'Haar , applicata per la ricerca
del volto sull'immagine tramite uno screenshot in real time dalla webcam.
Pagina | 15
Fig. 2.5 rappresenta il rettangolo ottenuto tramite Haar del volto.
Inoltre, nella Fig. 2.6, applichiamo l'Haar per trovare gli occhi e lo screenshot che ne
consegue è il seguente.
Fig. 2.6 rappresenta i rettangoli ottenuti tramite Haar degli occhi (sinistro e destro).
Pagina | 16
2.2 Filtraggio antirumore nel dominio spaziale
Prima di introdurre il filtro di attenuazione del rumore utilizzato, è bene comprendere quali
siano le cause principali di tale rumore.
La più comune causa d'imperfezione in un'immagine digitale è data dall’instabilità della
sorgente luminosa, cui si aggiungono in misura minore le imperfezioni nel sistema di
digitalizzazione (trasmissione elettronica dei dati).
Il problema nasce quando il rumore non ha un andamento statistico ben definito, ed è dunque
random.
E' tuttavia possibile, utilizzare alcune tecniche per ridurlo il più possibile ottenendo alcuni
vantaggi, ma anche i corrispondenti svantaggi che saranno illustrati.
Innanzitutto, si osservi che nel caso siano disponibili più copie di una stessa immagine statica,
si può ipotizzare di mediarle pixel per pixel, in modo da incrementare il rapporto segnale
rumore.
Ciò contravverrebbe con l'idea per la quale si mira a un'implementazione efficiente
dell’algoritmo, oltre che all’elaborazione d'immagini dinamiche e non statiche.
Pertanto, la soluzione migliore è quella che compie l’operazione di media direttamente sui
pixel adiacenti a quello in esame, ovvero effettuando un filtraggio locale di tipo “passa
basso”.
I rimedi possibili per ridurre il rumore da un’immagine, rientrano in due macro-categorie:
Quelli che agiscono del dominio spaziale.
Quelli che intervengono nel dominio della frequenza, tramite trasformata e
antitrasformata bidimensionale di Fourier.
Nel nostro caso, ci siamo limitati all'uso di un algoritmo operante nel dominio spaziale.
Troviamo sostanzialmente tre categorie di filtro, ciascuna delle quali si adatta ad una
determinata tipologia di rumore:
Filtri lineari (medio e media pesata).
Filtri mediani (non lineari).
Filtri gaussiani.
Il primo ed il terzo sono oggetto di analisi ed implementazione negli algoritmi, per cui
verranno analizzati, mentre la seconda tipologia è indicata per immagini che manifestano
disturbi impulsivi (come spike), che non interessano le webcam utilizzate. [2]
E' possibile utilizzare il filtro spaziale di smoothing o filtro di media, il quale è usato
principalmente per due scopi:
Pagina | 17
eliminare i dettagli irrilevanti, le cui dimensioni siano tipicamente più piccole
della maschera, attenuando ad esempio gli effetti dell'aliasing;
per la riduzione del rumore, quest'ultima applicazione è la più investigata, ma ha
lo svantaggio di ridurre oltre al rumore, anche le variazioni rapide di livello di
grigio associate ai contorni degli oggetti (edge) e non al rumore. In generale, è
possibile limitare questo effetto.
Il filtro più semplice in questo contesto è il cosiddetto Box Filter, avente una maschera h del
tipo illustrato qui di seguito, dove D è la dimensione della maschera:
Fig. 2.7 rappresenta il filtro di media, detto Box filter.
In questo modo si ha che:
Questa proprietà è anche nota come proprietà di preservare la media.
Essa indica la capacità di questi filtri di preservare il valore di luminanza medio nello spazio,
nel caso di segnali bidimensionali (cioè la media). Ci sono due modi di giustificare questo
legame.
Il primo è il seguente:
dove H( x, y) è la trasformata di Fourier della maschera h(i, j).
In tal caso si ha che, il valore della funzione di trasferimento del filtro è pari ad uno a
frequenza nulla, ossia che la componente continua risulta inalterata ( caratteristica tipica di un
filtraggio passa basso).
Ricordando che, la componente continua di un segnale è la sua media (temporale o spaziale),
si ha la tesi citata sopra.
Un altro tipo di filtro di smoothing è il cossidetto filtro weighted average, ossia a media
pesata.
Pagina | 18
In tal caso, la maschera del filtro avrà valori che decrescono lentamente, man mano che ci si
allontana dal centro della maschera e in modo proporzionale alla distanza euclidea.
I valori presenti sulla diagonale principale saranno più piccoli rispetto a quelli presenti sia
sulla seconda colonna, sia sulla seconda riga passante per il valore centrale della matrice. [8]
Una maschera di esempio per questo tipo è riportata in Fig. 2.8:
Fig. 2.8 rappresenta il filtro a media pesata detto weighted average.
Dopodichè, analizziamo il prototipo della funzione di smooth costituito dai seguenti parametri
in ingresso:
(void) cvSmooth( const CvArr* src, CvArr* dst,
int smoothtype CV_DEFAULT(CV_GAUSSIAN),
int size1 CV_DEFAULT(3),
int size2 CV_DEFAULT(0),
double sigma1 CV_DEFAULT(0),
double sigma2 CV_DEFAULT(0))
src rappresenta l'immagine sorgente;
dst rappresenta l'immagine di destinazione;
smoothtype identifica la tipologia di filtro di smoothing utilizzato;
size1 identifica il primo parametro dell'operazione di smoothing;
size2 identifica il secondo parametro nell'operazione di smooth ("smussatura"), per
esempio nel caso di semplice scalatura/non scalatura e sfocatura gaussiana
(Gaussian Blur), se size2 è zero, esso è impostato al valore di size1;
sigma1 identifica il parametro che, nel caso del kernel gaussiano, può specificare
Gaussian sigma ossia la (deviazione standard). Se è zero, viene calcolato dalle
dimensioni del kernel:
Ove n=size1 per kernel orizzontale, n=size2 per kernel verticale.
Con il sigma standard per i piccoli kernel (da [3x3] a [7x7]), le prestazioni sono
migliori. Se sigma1 non è zero, mentre size1 e size2 sono pari a zero, la dimensione
Pagina | 19
del kernel è calcolata dal sigma (per fornire abbastanza accuratezza
nell'operazione);
sigma2 identifica il parametro che, nel caso di kernel non quadrati gaussiani, può
essere utilizzato per specificare un sigma differente (da sigma1) in direzione
verticale.
Sia size1, che size2, sia sigma1, che sigma2, dipendono dal filtro scelto, ossia dal valore
passato al parametro smoothtype.
I valori che possono essere passati allo smoothtype, sono i seguenti:
CV BLUR, ove la funzione applica un filtro di media di dimensione
[size1×size2]. Il parametro size1 può assumere solo valori dispari, in questo
modo ogni pixel dell’immagine d'input coinciderà con il centro del filtro. Se non
assegniamo nessun valore al parametro size2, questo assume lo stesso valore di
size1. La funzione applica il filtro di media alle immagini con uno o tre canali e
con profondità pari a 8 bit, 16 bit e 32 bit floating point;
CV BLUR NO SCALE, ove la funzione applica un filtro, la cui risposta è data
dalla somma dei valori compresi nell’intorno [size1×size2].
In questo caso, l’assenza del fattore di normalizzazione
impone che
l’immagine in uscita debba avere differente profondità rispetto a quella in
ingresso, in modo da evitare l’overflow dei valori d'intensità.
In particolare, per un’immagine in ingresso con un solo canale e con profondità
pari a 8 bit unsigned, quella in uscita deve essere a 16 bit signed oppure a 32 bit
floating point;
CV MEDIAN, ove la funzione applica il filtro mediano di dimensione [size1 ×
size1].
Il filtro lavora solo con immagini a uno o tre canali e con profondità pari a otto
bit unsigned. Questo è il tipo di smooth che viene applicato nell'algoritmo
implementato;
CV GAUSSIAN, ove l’immagine è elaborata mediante un filtro gaussiano di
dimensione [size1 × size2], il quale si basa sull’approssimazione discreta della
funzione gaussiana bidimensionale a valor medio nullo, descritta dalla seguente
formula:
Pagina | 20
La deviazione standard , è il parametro che modella la funzione e definisce
l’area d'influenza del filtro, dove il peso dei coefficienti è inversamente
proporzionale alla distanza del pixel rispetto a quello centrale.
Tra le proprietà vi è la simmetria circolare, ossia il filtro esegue l’operazione di
smoothing in modo identico in tutte le direzioni.
Il filtro gaussiano può operare con immagini a uno o tre canali, con profondità
pari a otto bit, sedici bit o trentadue bit floating point;
CV BILATERAL, ove la funzione applica un filtro non lineare denominato filtro
bilaterale. A differenza di quello gaussiano, il filtro bilaterale è in grado di
eseguire lo smoothing dell’immagine senza attenuare i contorni (edge preserving
smoothing), basandosi sull’azione congiunta di un filtro lineare e di uno non
lineare. Quello lineare, detto domain filter è un filtro gaussiano che si ottiene
dall’equazione seguente:
Il filtro non lineare, detto range filter è definito dalla seguente funzione:
dove è l’intensità luminosa,
è il pixel i-esimo in un’intorno di dimensione
[n×n], e
il pixel centrale. Il range filter si ottiene dalla differenza di intensità
tra i pixel dell’intorno e il pixel centrale, mentre quello bilaterale si ottiene dalla
moltiplicazione punto per punto di entrambi i filtri.
Il nuovo valore del pixel centrale
dell’intorno è dato da:
con
come costante di normalizzazione.
In particolare, il filtro implementato dalla funzione, ha dimensione [3×3] e può
agire su immagini che hanno uno o tre canali, con profondità a otto bit oppure a
32 bit floating point. [12]