Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

10 bit YUV420 to RGB Conversion

I am working with conversion from YUV420 to RGB but the image colours are not producing fine. Originally my own files are 10 bit. Initially, I started with 8 bit files.

I am using the code below to read YUV420 image and convert to RGB. Because I have YUV420.YUV image file but that code is for video therefore, I only read 1 frame. Then I get YUV as Y as the full size but U and V as half size as described at Wikipedia. I then resize the images to the full size of the image and apply YUV to RGB conversion. But the RGB images are not in correct colors. I have attached the files so that you can run and see what is the problem. Here is the YUV file tulips_yuv420_inter_planar_qcif.yuv.

I have two more questions;

Firstly, The size of the "stream" for one frame should be equal to 1.5*the size of the Y but it is very large whether I use uint8 or uint16 to read the file.

Secondly, If I have 10bit YUV420 file how to I to modify this code to show correct 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))
like image 293
Kanjoo Avatar asked Feb 03 '26 10:02

Kanjoo


1 Answers

The sample file is 8 bit (not 10 bit), and the storage format is tricky.

The tool allows you selecting the format.
The suitable format is as follows:
enter image description here

The frame is divided to two fields - upper filed and lower filed (interlace format).
Resolution of each filed is 176x72.
Because the format is YUV420, the size of U and V fields is 88x36.

The code sample uses the following stages:

  • Read the upper filed of Y, U and V (8 bits per element).
  • Read the lower filed of Y, U and V.
  • Interleave upper and lower filed.
  • Up-sample U and V to the size of Y.
  • Convert YUV to RGB (use existing MATLAB function ycbcr2rgb).

The following code sample reads the first frame and convert to 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)

Result:
enter image description here


Reading 10 bit YUV420:

Assumptions:

  • Each 10 bits component is stored in 2 bytes (no "bits packing").
  • Data is stored in lower part of each byte (each uint16 element holds a value in range [0, 1023]).
  • Storage format is the same non-standard interlace format as the uint8 sample.

Build 10 bits YUV420 sample file from the 8 bits sample (singe frame for testing):
The following code build a 10 bits sample out of the 8 bits sample (expands the range from 8 bits stored in uint8 to 10 bits stored in uint16).

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);

Reading 10 bit YUV420
The following code reads single frame of 10 bit YUV420 (matching list of assumptions):

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)

Note:
The code YUV = YUV/1023 converts the "10 bits" format to [0, 1] double format.
Conversion is used because ycbcr2rgb is not supporting 10 bits input.


Computing the size of the file:
You are correct: "The size of one frame equals 1.5*the size of Y".
Assuming 10 bits component is stored in 2 bytes, size of Y is width*height*2, and size of one frame is width*height*3.

like image 165
Rotem Avatar answered Feb 05 '26 01:02

Rotem