10位YUV420到RGB转换

3
我正在处理YUV420到RGB的转换,但图像颜色不够好。我的原始文件是10位的,但最初我使用8位的文件。我使用下面的代码读取YUV420图像并将其转换为RGB。因为我有YUV420.YUV图像文件,但该代码用于视频,因此我只读取了1个帧。然后我将YUV作为完整大小的Y读取,而U和V则按照维基百科上的描述缩小了一半。然后,我将图像调整为完整图像的大小并应用YUV到RGB的转换。但RGB图像的颜色不正确。我附上了文件,以便您可以运行并查看问题所在。这是YUV文件 tulips_yuv420_inter_planar_qcif.yuv
我还有两个问题:
首先,一个帧的“流”大小应该等于Y的大小的1.5倍,但无论我使用uint8还是uint16读取文件,它都非常大。
其次,如果我有10位的YUV420文件,我该如何修改此代码以显示正确的RGB。
fname = 'tulips_yuv420_inter_planar_qcif.yuv';
width  = 176;
height = 144;
nFrame=1;
fid = fopen(fname,'r');           % Open the video file
stream = fread(fid,'uint8');    % uint16
% stream = fread(fid);    % uint8

length = 1.5 * width * height;  % Length of a single frame

y = double(zeros(height,   width,   nFrame));
u = double(zeros(height/2, width/2, nFrame));
v = double(zeros(height/2, width/2, nFrame));

for iFrame = 1:nFrame

   frame = stream((iFrame-1)*length+1:iFrame*length);

   % Y component of the frame
   yImage = reshape(frame(1:width*height), width, height)';
   % U component of the frame
   uImage = reshape(frame(width*height+1:1.25*width*height), width/2, height/2)';
   % V component of the frame
   vImage = reshape(frame(1.25*width*height+1:1.5*width*height), width/2, height/2)';

   y(:,:,iFrame) = double(yImage);
   u(:,:,iFrame) = double(uImage);
   v(:,:,iFrame) = double(vImage);

end
u=imresize(u,size(y),'bicubic');
v=imresize(v,size(y),'bicubic');
yuv=cat(3,y,u,v);
T = [1,0,1.28033;1,-0.21482,-0.38059;1,2.12798,0];


RGB(:,:,1) = T(1)*yuv(:,:,1) + T(4)*yuv(:,:,2) + T(7)*yuv(:,:,3) ;
RGB(:,:,2) = T(2)*yuv(:,:,1) + T(5)*yuv(:,:,2) + T(8)*yuv(:,:,3) ;
RGB(:,:,3) = T(3)*yuv(:,:,1) + T(6)*yuv(:,:,2) + T(9)*yuv(:,:,3) ;

figure,imshow(uint8(RGB))
1个回答

3
示例文件是8位(而不是10位),存储格式比较棘手。 该工具允许您选择格式。 适当的格式如下所示: enter image description here 每帧被分成两个场 - 上场和下场(隔行扫描)。 每个场的分辨率为176x72。 因为格式是YUV420,所以U和V字段的大小为88x36。 代码示例使用以下阶段: 读取Y、U和V的上场(每个元素8位)。 读取Y、U和V的下场。 交错上场和下场。 将U和V上采样到Y的大小。 将YUV转换为RGB(使用现有的MATLAB函数ycbcr2rgb)。 以下代码示例读取第一帧并转换为RGB:
fname = 'tulips_yuv420_inter_planar_qcif.yuv';
width  = 176;
height = 144;
fid = fopen(fname, 'r');           % Open the video file

Y0 = (fread(fid, [width, height/2], 'uint8'))';     %Read upper field of Y plane
U0 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read lower field of Y plane
V0 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read upper field of U plane

Y1 = (fread(fid, [width, height/2], 'uint8'))';     %Read upper field of Y plane
U1 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read lower field of U plane
V1 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read lower field of V plane

fclose(fid);

%Interleave upper and lower fields
Y = zeros(height, width);
Y(1:2:end, :) = Y0;
Y(2:2:end, :) = Y1;

U = zeros(height/2, width/2);
U(1:2:end, :) = U0;
U(2:2:end, :) = U1;

V = zeros(height/2, width/2);
V(1:2:end, :) = V0;
V(2:2:end, :) = V1;

U = imresize(U, size(Y), 'bicubic');
V = imresize(V, size(Y), 'bicubic');
YUV = cat(3, Y, U, V);

%Convert YUV to RGB (MATLAB function ycbcr2rgb uses BT.601 conversion formula).
RGB = ycbcr2rgb(uint8(YUV));

figure,imshow(RGB)

结果:

结果:
在此输入图片描述


读取10位YUV420:

假设:

  • 每个10位分量存储在2个字节中(没有“位打包”)。
  • 数据存储在每个字节的低位(每个uint16元素保存值的范围为[0, 1023])。
  • 存储格式与uint8样本相同,都是非标准的交错格式。

从8位样本构建10位YUV420样本文件(用于测试的单帧):
以下代码将从8位样本中构建出一个10位样本(将范围从8位扩展到存储在uint16中的10位)。

fname = 'tulips_yuv420_inter_planar_qcif.yuv';
width  = 176;
height = 144;
fid = fopen(fname, 'r');           % Open the video file
Y0 = (fread(fid, [width, height/2], 'uint8'))';     %Read upper field of Y plane
U0 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read lower field of Y plane
V0 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read upper field of U plane
Y1 = (fread(fid, [width, height/2], 'uint8'))';     %Read upper field of Y plane
U1 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read lower field of U plane
V1 = (fread(fid, [width/2, height/4], 'uint8'))';   %Read lower field of V plane
fclose(fid);

fid = fopen('10bits__tulips_yuv420_inter_planar_qcif.yuv', 'w');           % Open for writing
fwrite(fid, uint16(Y0'*(1023/255)), 'uint16'); %1023 = 2^10-1, and 255 = 2^8-1
fwrite(fid, uint16(U0'*(1023/255)), 'uint16');
fwrite(fid, uint16(V0'*(1023/255)), 'uint16');
fwrite(fid, uint16(Y1'*(1023/255)), 'uint16');
fwrite(fid, uint16(U1'*(1023/255)), 'uint16');
fwrite(fid, uint16(V1'*(1023/255)), 'uint16');
fclose(fid);

读取10位YUV420
以下代码读取一帧10位YUV420(符合假设列表):

fname = '10bits__tulips_yuv420_inter_planar_qcif.yuv';
width  = 176;
height = 144;
fid = fopen(fname, 'r');           % Open the video file

Y0 = (fread(fid, [width, height/2], 'uint16'))';     %Read upper field of Y plane
U0 = (fread(fid, [width/2, height/4], 'uint16'))';   %Read lower field of Y plane
V0 = (fread(fid, [width/2, height/4], 'uint16'))';   %Read upper field of U plane

Y1 = (fread(fid, [width, height/2], 'uint16'))';     %Read upper field of Y plane
U1 = (fread(fid, [width/2, height/4], 'uint16'))';   %Read lower field of U plane
V1 = (fread(fid, [width/2, height/4], 'uint16'))';   %Read lower field of V plane

fclose(fid);

%Interleave upper and lower fields
Y = zeros(height, width);
Y(1:2:end, :) = Y0;
Y(2:2:end, :) = Y1;

U = zeros(height/2, width/2);
U(1:2:end, :) = U0;
U(2:2:end, :) = U1;

V = zeros(height/2, width/2);
V(1:2:end, :) = V0;
V(2:2:end, :) = V1;

U = imresize(U, size(Y), 'bicubic');
V = imresize(V, size(Y), 'bicubic');
YUV = cat(3, Y, U, V);

%Convert elements range from [0, 1023] to range [0, 1] (MATLAB function ycbcr2rgb supports doubles in range [0, 1]).
YUV = YUV/1023; %1023 applies 10 bits range. 2^10-1 = 1023

%Convet YUV to RGB (MATLAB function ycbcr2rgb uses BT.601 conversion formula).
RGB = ycbcr2rgb(YUV);

%Convert from double to uint8 (from range [0, 1] to range [0, 255]).
RGB = im2uint8(RGB);

figure,imshow(RGB)

注意:
代码YUV = YUV/1023将“10位”格式转换为[0,1]double格式。
进行转换是因为ycbcr2rgb不支持10位输入。

计算文件大小:
您是正确的:“一个帧的大小等于Y的大小的1.5倍”
假设10位元件存储在2字节中,Y的大小为宽度*高度*2,一个帧的大小为宽度*高度*3。


你正在使用什么工具? - Mark Setchell
你发布的链接中的工具。备注:该工具在我的机器上无法显示视频。 - Rotem
@Rotem,你是对的,上面的图像是8位。但我的第二个问题是如何读取10位YUV420。非常感谢你的帮助。 - Kanjoo
如果我要猜的话...每个10位组件存储在2个字节中,很可能在每个字节的低部(每个uint16元素保存在[0, 1023]范围内)。这是相同的非标准交错格式吗?你能给一个样本文件吗? - Rotem
我更新了答案。对于你的10位输入,解决方案很可能不能“原封不动”地工作。有太多的假设... - Rotem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接