matlabmultimediasubsampling

In Matlab, how can I use chroma subsampling to downscale a 4:4:4 image to 4:2:0 when the image is in YCbCr?


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;

Solution

  • 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 following code sample performs the following steps:

    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