【问题标题】:10 bit YUV420 to RGB Conversion10 位 YUV420 到 RGB 转换
【发布时间】:2020-01-03 00:05:29
【问题描述】:

我正在处理从 YUV420 到 RGB 的转换,但图像颜色效果不佳。最初我自己的文件是 10 位的。最初,我从 8 位文件开始。

我正在使用下面的代码来读取 YUV420 图像并转换为 RGB。因为我有 YUV420.YUV 图像文件,但该代码是用于视频的,所以我只读取了 1 帧。然后我得到 YUV 作为 Y 的全尺寸,但 U 和 V 作为 Wikipedia 中描述的一半尺寸。然后我将图像调整为图像的全尺寸,并将 YUV 应用到 RGB 转换。但是 RGB 图像的颜色不正确。我已附加文件,以便您可以运行并查看问题所在。这是 YUV 文件tulips_yuv420_inter_planar_qcif.yuv

我还有两个问题;

首先,一帧的“流”的大小应该等于1.5*Y的大小,但是无论我使用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))

【问题讨论】:

    标签: matlab image-processing video-processing yuv


    【解决方案1】:

    示例文件是 8 位(不是 10 位),存储格式比较棘手。

    该工具允许您选择格式。
    合适的格式如下:

    帧分为两个场 - 上场和下场(隔行格式)。
    每个文件的分辨率为 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 示例相同的非标准interlace 格式。

    从 8 位样本构建 10 位 YUV420 样本文件(单帧测试):
    以下代码从 8 位样本中构建了一个 10 位样本(将范围从存储在 uint8 中的 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 位输入。


    计算文件的大小:
    你是对的:“一帧的大小等于 1.5*Y 的大小”。
    假设 10 位分量存储在 2 个字节中,Y 的大小为宽*高*2,一帧的大小为宽*高*3。

    【讨论】:

    • 请问您使用的是什么工具?
    • 您发布的link 中的工具。备注:该工具在我的机器中没有显示视频。
    • @Rotem 你是对的,上面的图像是 8bit。但我的第二个问题是如何读取 10 位 YUV420。谢谢你所做的一切。
    • 如果我不得不猜测...每个 10 位分量存储在 2 个字节中,最有可能在每个字节的下部(每个 uint16 元素的值在 [0, 1023 ])。它是相同的非标准 interlace 格式吗?能给个示例文件吗?
    • 我更新了答案。对于您的 10 位输入,解决方案很有可能无法“按原样”工作。假设太多了……
    猜你喜欢
    • 2011-09-27
    • 2014-05-16
    • 2022-12-22
    • 2020-12-18
    • 1970-01-01
    • 2021-04-13
    • 2019-11-12
    • 2016-02-06
    • 2014-04-04
    相关资源
    最近更新 更多