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