0

I have already converted the jpg images from RGB to YCbCr but must now use Chroma Subsampling to make them 4:2:0. I have searched but have not found any information on how to do this (note: I am very new to Matlab)

Edit: I now have this but at the bottom where I am setting ycbcr(:,:,2) = newCb it says "Unable to perform assignment because the size of the left side is 1273-by-1910 and the size of the right side is 1273-by-955-by-0."

function f = conversion(source_image, source_name)

image = imread(source_image);

% conversion_matrix = [0.299 -0.168736 0.5;
%                      0.587 -.0331264 -.0418688;
%                      0.114  0.5 -.081312];

conversion_matrix = [0.299 0.587 0.114;
                     -0.168736 -.0331264 0.5;
                     0.5  -.0418688 -.081312];

ycbr = reshape(double(image),[],3)*conversion_matrix;

ycbr = reshape(uint8(ycbr),size(image));

Y = ycbr(:,:,1)+ 0;
Cb = ycbr(:,:,2)+ 0.5;
Cr = ycbr(:,:,3)+ 0.5;

Cb = double(Cb);
newCb = uint8(round((Cb(:,1:2:end, 1:2:end) + Cb(:,2:2:end, 1:2:end) + Cb(:,1:2:end, 2:2:end) + Cb(:,2:2:end, 2:2:end)) / 4));
Cr = double(Cr);
newCr = uint8(round((Cr(:,1:2:end, 1:2:end) + Cr(:,2:2:end, 1:2:end) + Cr(:,1:2:end, 2:2:end) + Cr(:,2:2:end, 2:2:end)) / 4));

ycbcr(:,:,1) = Y;
ycbcr(:,:,2) = newCb;
ycbcr(:,:,3) = newCr;

imshow(ycbcr);
imwrite(ycbcr, source_name);

f = ycbcr;
dcalvert
  • 73
  • 1
  • 11
  • You are using a wrong conversion formula. You can use the formula from my post: [How to convert sRGB to NV12 format using NumPy?](https://stackoverflow.com/questions/57022633/how-to-convert-srgb-to-nv12-format-using-numpy). Please read the question and the answer (ignore the NV12 ordering part). – Rotem Feb 19 '20 at 06:34
  • You are getting error message, because in MATLAB you can't create a 3D matrix when each plane has different dimensions. You can't use `imwrite` to save your output to a single file. I can recommend you using `fopen`, `fwrite` for Y, `fwrite` for newCb, `fwrite` for new Cr, and `fclose`. Name the file with `.raw` or `.yuv' extension (it's a binary file and not an image file). – Rotem Feb 19 '20 at 06:43
  • I updated my post with a complete solution. – Rotem Feb 20 '20 at 13:38
  • were you able to do this assignment? ycbcr(:,:,2) = newCb – Sanam Apr 24 '21 at 15:23

1 Answers1

2

You can simply resize Cb and Cr by a factor of 0.5 in each axis:

Assume:

YUV = rgb2ycbcr(RGB);
Y = YUV(:, :, 1);
U = YUV(:, :, 2);
V = YUV(:, :, 3);

Y channel is unmodified (same Y for 4:2:0 as in 4:4:4 format)

Down-sample U and V by a factor of 0.5 for getting 4:2:0 format:

newU = imresize(U, 0.5);
newV = imresize(V, 0.5);

In MATLAB you usually like to keep the 420 result Y, newU, newV and 3 matrices (planar format), and not merge the matrices into one matrix.

The 4:2:0 format doesn't dictates specific components ordering (like I420, or NV12...) so the three matrices are considered to be in 4:2:0 format.


Down-sampling without using imresize:

You can Down-sample U and V using the following code sample:

U = double(U);
newU = uint8(round((U(1:2:end, 1:2:end) + U(2:2:end, 1:2:end) + U(1:2:end, 2:2:end) + U(2:2:end, 2:2:end)) / 4));

Result is equivalent to resizing with bi-linear interpolation, without Anti-aliasing filter:

shrunkU = imresize(U, 0.5, 'bilinear', 'Antialiasing', false);

Update:

  • The conversion formula you have posted is incorrect (at least not the same as MATLAB builtin rgb2ycbcr conversion formula).
    MATLAB conversion formula matches BT.601 "limited range" standard.
  • It also looks like you have a mistake in the vector by matrix multiplication.
  • As I commented, I recommend you to save the 420 result to binary file.

The following code sample performs the following steps:

  • Convert RGB to YCbCr without using builtin function, and compare the result to MATLAB rgb2ycbcr result.
  • Convert from YCbCr 444 to YCbCr 420 (without using builtin function).
  • Save the 420 result to binary file im.yuv
  • Convert im.yuv to PNG format using FFmpeg command line tool, and show the result.

Here is the code:

RGB = imresize(imread('autumn.png'), [100, 170]); % Load RGB image for testing (and resize)

% Convert to YCbCr using MATLAB builtin function (used as reference)
refYUV = rgb2ycbcr(RGB);

% Conversion matrix applies BT.601 standard ("limited range").
T = [ 0.2568    0.5041    0.0979
     -0.1482   -0.2910    0.4392
      0.4392   -0.3678   -0.0714];

% Conversion offset (for "limted range" standard the offset for Y channel is 16)
offset = [16
          128
          128];

% Manual conversion from RGB to YCbCr (YUV is a shortcut name from YCbCr):
% Multiply T matrix (from the left side) by three "long rows" of RGB elements and add offsets vector.
YUV = T*(reshape(double(RGB), [], 3))' +  offset;

% Reshape YUV to the shape of RGB, and convert back to uint8.
YUV = uint8(reshape(YUV', size(RGB)));

% Verify that YUV equals refYUV (maximum difference result is 1 out of 255)
disp(['Max Diff = ', num2str(max(imabsdiff(YUV(:), refYUV(:))))]);

% Convert to YUV 420 (without builtin function):
Y = YUV(:, :, 1)
U = double(YUV(:, :, 2))
V = double(YUV(:, :, 3))
newU = uint8(round((U(1:2:end, 1:2:end) + U(2:2:end, 1:2:end) + U(1:2:end, 2:2:end) + U(2:2:end, 2:2:end)) / 4));
newV = uint8(round((V(1:2:end, 1:2:end) + V(2:2:end, 1:2:end) + V(1:2:end, 2:2:end) + V(2:2:end, 2:2:end)) / 4));

% Save result to YUV file (file format is going to be raw I420 foramt):
% Make sure to transpose the matrix before saving (becuase MATLAB is "column major", and fomrat is "row major").
f = fopen('im.yuv', 'w');
fwrite(f, Y', 'uint8');
fwrite(f, newU', 'uint8');
fwrite(f, newV', 'uint8');
fclose(f);

% Convert im.yuv to PNG format using FFmpeg (free command line tool).
% For Windows system, download stable stsatic build from https://ffmpeg.zeranoe.com/builds/
% Place ffmpeg.exe in the same path of the script (just for testing withing MATLAB)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[status, cmdout] = system('ffmpeg -y -s 170x100 -i im.yuv -pix_fmt yuv420p im.png');

% Read and show im.png for testing:  
I = imread('im.png');
imshow(I)

Result (after converting to YCbCr 420 and converting back to RGB using FFmpeg):
enter image description here

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Although this does make a lot of sense, I am directly instructed to not use the imresize function, which makes things unnecessarily more difficult for me – dcalvert Feb 17 '20 at 23:41
  • 1
    I added a solution without using `imresize`. – Rotem Feb 18 '20 at 00:09
  • Thank you so much for the detailed explanation! – dcalvert Feb 18 '20 at 00:42
  • I am actually having trouble using this. I am also not allowed to use the basic rgb2ycbcr function, so perhaps that is causing issues, but my converted image comes out to a 2D array, not a 3D like I think you are showing to access – dcalvert Feb 18 '20 at 01:51
  • Please add your conversion code (from RGB to YCbCr), and the code you are using for loading the image to MATLAB. Please also define the input and output format (define how the components are ordered in a single matrix). – Rotem Feb 18 '20 at 06:28
  • @Rotem how to sample from 4:4:4 to 4:1:1? – Sanam Apr 24 '21 at 18:33
  • 1
    @Sanam using `imresize`: Downscale by 1/4 only the horizontal axis: `newU = imresize(U, [size(U, 1), size(U, 2)/4]);` – Rotem Apr 24 '21 at 19:59