首先聲明一點,這里的YUV其實不是YUV,嚴格來說是YCbCr。這里就先這樣稱呼YUV吧。本文是關於YUV420格式的視頻轉成圖片序列的。
關於YUV格式的圖片,存儲如下圖所示:
舉個例子,一個640*480的420的圖片,讀入matlab中的數據量為460800個數據。
460800/640*480=3/2。相當於每二個像素有3個數據。
如果想要把一個YUV文件轉成RGB數據,那么必須要知道這個YUV文件是怎么以文件格式存儲的。舉個例子,假如一張4*4的大小的圖片,它的YUV420格式的存儲是這樣的,如下圖:
這只是一個示意圖,假如一個圖片是100*100像素的,那么它的存儲文件中,前100*100=10000個字節是用來存儲Y的。接下來2500個字節是用來存儲U的,然后的2500個字節是用來存儲V的。
當然這種格式也不是唯一的,我發現大多數的圖片都是這種格式。當然還有其他的方式來驗證一個圖片是不是這樣的格式。對於一個YUV420格式的圖片來說,它一幀畫面的數據量是它像素數的1.5倍,當然,我們肯定是知道一幀畫面的分辨率的,然后可以讀入YUV420視頻文件中的1.5*X*Y個數據量,然后查看它對應有圖片。如圖所示:
紅框中的是Y數據,藍框中的是U數據,黃框中的是V數據。
現在應該可以得到一個YUV幀的YUV分量啦,接下來要做的就是把這三個分量加權求和,生成R,G,B分量啦。加權公式如下:
R=1.164*(Y-16)+1.596*(VV-128);
G=1.164*(Y-16)-0.813*(UU-128)-0.392*(VV-128);
B=1.164*(Y-16)+2.017*(UU-128);
這里要注意一下,這里的YUV最好是double型的,我剛才始生成RGB的時候,YUV都是uint8型的,最后生成的圖片顏色總是不對,換成double型的話,就沒有問題啦,注意一下,double型的數據還要再歸一化或者強制轉換成uint8型的數據,才可以用imshow()顯示出來。還有RGB三個分量矩陣中,大於255的量要設置成255,小於0的元素要設置為0。
本實驗中用的圖片恢復出來,如下圖所示:
大功告成。
本程序實現代碼如下圖所示:
%函數功能:這個函數用於加載一個UYV序列文件
%入口參數: path是包含要讀寫YUV序列的地址的字符串
% x,y是這個圖片的高與寬
% start表示要從第幾幀開始讀取畫面
% count表示要讀出的圖片的張數
%出口參數: picout(x,y,,3,count)表示出口參數
function [picout]=loadYUV(path,x,y,start,count);
% clear;
% clc
% x=288;
% y=352;
% count=10;
% path='e:\test-pic\flower.yuv';
fid=fopen(path,'r');
for ii=1:count
fseek(fid,1.5*x*y*(ii-1+start-1),-1);
pic=uint8(fread(fid,[y,1.5*x],'uint8'));
pic=pic';
Y=double(pic(1:x,:));
u=double(pic(x+1:1.25*x,:));
v=double(pic(1.25*x+1:1.5*x,:));
for i=1:0.25*x
U(2*i-1,1:y/2)=u(i,1:y/2);
U(2*i,1:y/2)=u(i,y/2+1:y);
V(2*i-1,1:y/2)=v(i,1:y/2);
V(2*i,1:y/2)=v(i,y/2+1:y);
end
UU=imresize(U,2);
VV=imresize(V,2);
% R=Y+1.140*(VV-128);
% G=Y-0.395*(UU-128)-0.581*(VV-128);
% B=Y+2.032*(UU-128);
R=1.164*(Y-16)+1.596*(VV-128);
G=1.164*(Y-16)-0.813*(UU-128)-0.392*(VV-128);
B=1.164*(Y-16)+2.017*(UU-128);
for i=1:x
for j=1:y
if R(i,j)<0
R(i,j)=0;
end
if R(i,j)>255
R(i,j)=255;
end
if G(i,j)<0
G(i,j)=0;
end
if G(i,j)>255
G(i,j)=255;
end
if B(i,j)<0
B(i,j)=0;
end
if B(i,j)>255
B(i,j)=255;
end
end
end
R=uint8(R);
G=uint8(G);
B=uint8(B);
%R=R/255;G=G/255;B=B/255;
picout(:,:,:,ii)=cat(3,R,G,B);
end
%imshow(picout(:,:,:,1));
上文只是一個子程序,但是絕對可用。