Elaborazione video
Transcript
Elaborazione video
Elaborazione dei Segnali Multimediali a.a. 2009/2010 Elaborazione video In questa esercitazione vengono illustrate alcune semplici elaborazioni del segnale video. I dati di ingresso sono nel cosiddetto formato grezzo, per cui si vedrà innanzitutto come manipolare tale formato. In seguito verranno illustrati degli esempio relativi al conditional replenishment e a stima e compensazione del movimento. 1 Il segnale video grezzo Nei formati grezzi i file rappresentativi di una sequenza video contengono solo i campioni dei segnali di luminanza ed eventualmente di crominanza. Tutte le informazioni supplementari, come la risoluzione, il numero di frame, il frame rate, lo spazio dei colori utilizzato, devono essere note all’utente affinché sia possibile accedere al video stesso. Nondimeno, esistono dei formati video grezzi talmente comuni da costituire un vero e proprio standard. In questa esercitazione prenderemo in esame alcuni di tali casi. Il segnale video grezzo è formato da una successione di fotogrammi, tutti con la stessa risoluzione, le più comuni delle quali sono elencate in tabella 1. I campioni del segnale sono tipicamente rappresentati su formato unsigned char. Formato QCIF CIF SIF SD Righe×Colonne 144×176 288×352 240×352 576×720 Tabella 1: Formati video e risoluzioni 1.1 Il formato Y o video monocromatico Oltre che per la risoluzione, i formati video grezzi differiscono per lo spazio di colori utilizzato. Nel caso di video monocromatico, il segnale è costituito da una sequenza di matrici di luminanza, scandite per righe. Ad esempio, se il file flower050 cif.y contiene 50 frame di un video in risoluzione CIF, la prima frame sarà rappresentata nei primi 352 · 288 = 101376 byte, ognuno dei quali rappresenta un campione di luminanza. L’ultima frame sarà rappresentata sui byte dal numero (101376 · 49 + 1) al numero 101376 · 50. Di seguito si presenta il listato di una funzione Matlab in grado di leggere la k-esima frame di un video monocromatico in risoluzione CIF. Si noti l’uso del comando fseek che permette di cambiare il punto di accesso ad un file di un numero qualsiasi di byte, a partire dall’inizio del file, dalla fine oppure dalla posizione corrente mediante l’opzione bof (beginning of file), cof (current position in file) o eof (end of file). Si noti anche che è necessario trasporre la frame dopo la lettura, esattamente come si fa nel caso di immagini fisse. 1 Il segnale video grezzo 2 function frame = get_cif_y_frame(nomefile, k); % Preleva una frame da un video monocromatico in formato CIF frame=[]; fid = fopen(nomefile,’r’); rows = 288; cols = 352; bytes_per_frame = rows * cols; byte_shift = bytes_per_frame * (k-1); fseek(fid, byte_shift, ’bof’); frame = fread(fid, [cols rows], ’uchar’); frame = frame’; fclose(fid); 1.2 Il formato YUV o video a colori Nel caso di video a colori ogni frame è costituita da tre matrici, poste l’una di seguito all’altra. I vari formati a colori differiscono nel modo in cui tali componenti sono relazionate alla rappresentazione canonica RGB. Il formato più diffuso per il video è quello YUV, in cui la prima componente è dunque la luminanza. In particolare, è comune il cosiddetto formato 4:2:0, in cui le matrici di crominanza sono sottocampionate di un fattore 2 lungo le righe e lungo le colonne, come illustrato in figura 1. In un file .yuv sono presenti 352 · 288 = 101376 byte di luminanza seguiti da 144 · 176 = 25344 byte per la componente U ed altrettanti per la componente V. Vediamo allora come va modificato il codice per prelevare la k-esima frame da un video CIF a colori. Innanzitutto è necessario creare una matrice 3D formata dalle componenti Y, U e V del fotogramma, in cui vanno inseriti i valori relativi alla luminanza e alla crominanza, come mostrato nel codice seguente. frame=[]; fid = fopen(’flower_cif_032.yuv’,’r’); k = 1; rows = 288; cols = 352; bytes_per_frame = rows * cols; byte_shift = bytes_per_frame * (k-1) * 1.5; fseek(fid, byte_shift, ’bof’); tmpY = fread(fid, [cols rows], ’uchar’); tmpU = fread(fid, [cols/2 rows/2], ’uchar’); tmpV = fread(fid, [cols/2 rows/2], ’uchar’); Quindi, le matrici di crominanza vanno interpolate per poter ricostruire la frame: Elaborazione dei Segnali Multimediali a.a. 2009-2010 Codifica video 3 frame(:,:,1) = tmpY’; [X Y] = meshgrid(2:2:rows,2:2:cols); [XI YI]= meshgrid(1:rows,1:cols); U = interp2(X,Y,tmpU,XI,YI); V = interp2(X,Y,tmpV,XI,YI); frame(:,:,2) = U’; frame(:,:,3) = V’; image(ycbcr2rgb(uint8(frame))); Si noti che per la visualizzazione è stato necessario tornare allo spazio di colori RGB. La versione monocromatica della frame può essere vista passando f(:,:,1) come argomento di image (con un’opportuna colormap). Le funzioni di lettura frame possono essere semplicemente modificate per leggere fotogrammi da video in formati differenti dal CIF. Scrivete due funzioni che prelevano un fotogramma da un video QCIF, a livelli di grigio e a colori. 1.3 Visualizzazione del segnale video I formati Y e YUV per il video grezzo sono cosı̀ diffusi che esistono programmi appositamente creati per visualizzare tale tipo di file. Un esempio è seqview. Provate a usare seqview per visualizzare i filmati Y e YUV. Dovrete fornire i parametri del video, ma il programma automaticamente sceglie dei valori di tentativo spesso corretti. 2 Codifica video Per comprimere una sequenza video si cerca di ridurre sia la ridondanza spaziale, mediante tecniche analoghe a quelle usate per la codifica di immagini fisse come JPEG, sia la ridondanza temporale. In quest’ultimo caso sono possibili diversi approcci: • codifica differenziale; • conditional replenishment; • stima e compensazione del movimento. Indicheremo con cur la frame corrente che deve essere predetta, ref la frame di riferimento e con brow, bcol le dimensioni orizzontali e verticali dei macroblocchi elaborati. Faremo, inoltre, per semplicità riferimento solo a segnali video monocromatici. 2.1 Codifica differenziale La codifica differenziale è una tecnica molto semplice e si basa sul fatto che fotogrammi consecutivi sono spesso molto simili tra loro, allora anziché trasmettere il fotogramma originale si trasmette la differenza tra quest’ultimo e il precedente. Provate allora semplicemente a visualizzare la differenza tra due fotogrammi successivi di un file video allo scopo di individuare le regioni in cui c’è stato movimento. 2.2 Conditional Replenishment Un metodo più sofisticato consiste nell’adattarsi ai cambiamenti locali della scena, suddividendo l’immagine in macroblocchi e verificando per ognuno di essi se c’è stata una variazione rispetto al fotogramma precedente. Questo approccio richiede, innanzitutto, di definire un criterio per misurare la similitudine tra due frame, per esempio attraverso la SAD (somma delle differenze assolute) o la SSD (somma delle differenze al quadrato). Elaborazione dei Segnali Multimediali a.a. 2009-2010 Codifica video 4 Quindi, se queste quantità sono minori di una fissata soglia γ allora si decide se trasmettere la differenza (refine) o non trasmettere nulla (skip). Se, invece, la soglia viene superata bisogna trasmettere il macroblocco corrente (new). Per implementare questa strategia si può scrivere la seguente funzione: function cur = cr(cur, ref, brow, bcol, gamma); [rows cols] = size(cur); % Scansione di tutti i macroblocchi for r = 1:brow:rows, for c = 1:bcol:cols, B = cur(r:r+brow-1, c:c+bcol-1); R = ref(r:r+brow-1, c:c+bcol-1); SSD = sum(sum((B-R).*(B-R))); if (SSD < gamma) cur(r:r+brow-1,c:c+bcol-1) = R; end; end; end; % Macroblocco corrente % Macroblocco di confronto % calcolo funzione di costo % modalità skip Fate degli esperimenti al variare della dimensione del macroblocco e al variare della soglia e calcolate la percentuale dei blocchi che non vengono sostituiti da quello corrente. Visualizzate, inoltre, la frame elaborata e quella originale e notate le eventuali differenze. 2.3 Stima e compensazione del movimento Vediamo ora come si può realizzare la stima di un campo di vettori di movimento (motion vector field, MVF) e poi la compensazione del movimento. La funzione che segue permette di calcolare il campo dei vettori di movimento, rappresentato con una matrice 3D in cui per ogni pixel sono memorizzate la componente verticale e orizzontale dei vettori di movimento. function mvf = me(cur, ref, brow, bcol, search); step=1; [rows cols]=size(cur); Si è inoltre considerato un parametro step che definisce la risoluzione con cui i vettori sono individuati all’interno dell’area di ricerca definita dal raggio search, (step=1 significa che si confronta il macroblocco con tutti quelli presenti nell’area di ricerca spostandosi di un pixel alla volta). % Scansione di tutti i blocchi for r=1:brow:rows, for c=1:bcol:cols, B=cur(r:r+brow-1,c:c+bcol-1); dcolmin=0; drowmin=0; SSDmin=brow*bcol*256*256; % Selezione del blocco corrente % Inizializzazione del vettore % Inizializzazione dell’errore La stima del movimento prevede innanzitutto di scandire ogni blocco nel fotogramma corrente. Per ogni Elaborazione dei Segnali Multimediali a.a. 2009-2010 Codifica video 5 blocco sono inizializzati due parametri: il vettore stimato drowmin, dcolmin ed il minimo valore di distorsione (SSDmin tra il blocco corrente ed i blocchi della frame di riferimento). Quest’ultimo è inizializzato al massimo valore possibile. A questo punto dobbiamo confrontare il blocco B con tutti i blocchi dell’area di ricerca. % Fase di Motion Estimation: individuazione del miglior vettore for drow=-search:step:search, for dcol=-search:step:search, % Check: l’area di ricerca deve essere interna all’immagine if ((r+drow>0)&(r+drow+brow-1<=rows)& ... (c+dcol>0)&(c+dcol+bcol-1<=cols)) % blocco di confronto R=ref(r+drow:r+drow+brow-1, c+dcol:c+dcol+bcol-1); SSD=sum(sum((B-R).*(B-R))); Si effettua una scansione su tutti i vettori drow, dcol nell’area di ricerca. Si noti il controllo che garantisce che il vettore corrente non punti all’esterno del fotogramma stesso. Il valore di distorsione corrente SSD viene calcolato e memorizzato. Confrontiamo tale distorsione con il minimo corrente, SSDmin. Se SSD è minore (e si noti che questo è sempre vero per il primo vettore: ciò spiega l’inizializzazione di SSDmin), vuol dire che il vettore corrente è migliore di quello finora trovato. Aggiorniamo quindi il vettore migliore e il valore di SSDmin. if (SSD<SSDmin) SSDmin=SSD; dcolmin=dcol; drowmin=drow; end; end; % vettore all’interno della frame end; % ciclo su dcol end; % ciclo su drow Alla fine della scansione di tutta la finestra, il miglior vettore risulta memorizzato in dminrow, dmincol. Tale informazione è memorizzata nella variabile d’uscita mvf, che è una matrice 3D: in mvf(i,j,1) ci deve essere la componente lungo le righe del vettore di movimento relativo al pixel (i,j), in mvf(i,j,2) ci deve essere la componente lungo le colonne. % Salviamo il MVF mvf(r:r+brow-1,c:c+bcol-1,1)=drowmin; mvf(r:r+brow-1,c:c+bcol-1,2)=dcolmin; end; % ciclo su c end; % ciclo su r Notiamo che mvf ha la struttura di un campo di vettori “denso”: è presente un vettore per pixel (identico per tutti i pixel del macroblocco). Questa struttura è ridondante, (basta un vettore per blocco), ma facilita l’implementazione. Per visualizzare il campo dei vettori di movimento utilizzate la funzione matlab displayMVF. Per quanto riguarda invece la funzione che effettua la compensazione del movimento data la frame di riferimento e il MVF calcolato, si ha: Elaborazione dei Segnali Multimediali a.a. 2009-2010 Codifica video 6 function motcomp = mc(ref,mvf); [rows cols] = size(ref); for r=1:rows, for c=1:cols, mc_r = r + mvf(r,c,1); mc_c = c + mvf(r,c,2); motcomp(r,c)= ref(mc_r,mc_c); end end 2.4 Esercizi proposti 1. Calcolo di MVF. Prelevate due fotogrammi da un file video ed effettuate la stima del movimento. Valutate come i parametri (dimensioni del blocco ed area di ricerca) influenzano il campo dei vettori. 2. Compensazione del movimento. Valutate le prestazioni della compensazione del movimento tramite il valore quadratico medio dell’errore di predizione. Che succede al variare di parametri come la dimensioni dei blocchi e l’area di ricerca? che succede usando video più o meno statici? 3. Regolarizzazione dei vettori di movimento. Implementate una funzione di stima di movimento in due stadi: il primo calcola il MVF come nella funzione me.m; il secondo regolarizza il MVF con un filtro mediano sulle due componenti. Confrontare i MVF prodotti dai due stadi in termini di MSE dell’errore di predizione. 4. Cross-search. La soluzione implementata precedentemente è di tipo Full-search, per velocizzare la ricerca è possibile adottare la strategia Cross-search. Scrivete una funzione mvf = crossSearch(cur, ref) che effettua la stima del movimento con strategia di ricerca Cross-search. Il criterio da minimizzare è la SSD. I blocchi devono avere dimensione 16 × 16 e i vettori devono avere componenti comprese tra -15 e 15. La tecnica consiste nel cercare prima la componente orizzontale e poi quella verticale del vettore di movimento. In altre parole, si valuta prima l’indice di riga rmin tale che il vettore (rmin , 0) abbia la minima SSD tra i vettori (r, 0) nella finestra di ricerca; poi si valuta cmin tale che il vettore (rmin , cmin ) minimizzi la SSD rispetto a tutti gli altri vettori (rmin , c). Elaborazione dei Segnali Multimediali a.a. 2009-2010