【问题标题】: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?在 Matlab 中,当图像在 YCbCr 中时,如何使用色度二次采样将 4:4:4 图像缩小到 4:2:0?
【发布时间】:2020-06-01 22:15:41
【问题描述】:

我已经将 jpg 图像从 RGB 转换为 YCbCr,但现在必须使用 Chroma Subsampling 使它们成为 4:2:0。我已经搜索但没有找到有关如何执行此操作的任何信息(注意:我对 Matlab 非常陌生)

编辑:我现在有了这个,但是在我设置 ycbcr(:,:,2) = newCb 的底部它说“无法执行分配,因为左侧的大小是 1273-by-1910 并且大小右边是 1273×955×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;

【问题讨论】:

  • 您使用了错误的转换公式。您可以使用我帖子中的公式:How to convert sRGB to NV12 format using NumPy?。请阅读问题和答案(忽略NV12订购部分)。
  • 您收到错误消息,因为在 MATLAB 中,当每个平面具有不同尺寸时,您无法创建 3D 矩阵。您不能使用imwrite 将输出保存到单个文件中。我可以推荐你使用fopenfwrite 代表 Y,fwrite 代表 newCb,fwrite 代表新 Cr,fclose。使用.raw 或“.yuv”扩展名命名文件(它是二进制文件而不是图像文件)。
  • 我用完整的解决方案更新了我的帖子。
  • 你能完成这个任务吗? ycbcr(:,:,2) = newCb

标签: matlab multimedia subsampling


【解决方案1】:

您可以在每个轴上简单地将 CbCr 的大小调整为 0.5:

假设:

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

Y 频道未修改(对于 4:2:0 与 4:4:4 格式的 Y 相同)

UV 下采样 0.5 倍以获得 4:2:0 格式:

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

在 MATLAB 中,您通常希望保留 420 结果YnewUnewV 和 3 个矩阵(平面格式),而不是将矩阵合并为一个矩阵。

4:2:0 格式不规定特定组件的顺序(如 I420 或 NV12...),因此这三个矩阵被认为是 4:2:0 格式。


不使用imresize的下采样:

您可以使用以下代码示例对UV 进行下采样:

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

结果相当于使用双线性插值调整大小,没有抗锯齿过滤器:

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

更新:

  • 您发布的转换公式不正确(至少与MATLAB内置的rgb2ycbcr转换公式不一样)。
    MATLAB 转换公式符合 BT.601“有限范围”标准。
  • 您在矩阵乘法中的向量似乎也有错误。
  • 正如我所评论的,我建议您将 420 结果保存到二进制文件中。

以下代码示例执行以下步骤:

  • 在不使用内置函数的情况下将 RGB 转换为 YCbCr,并将结果与​​ MATLAB rgb2ycbcr 结果进行比较。
  • 从 YCbCr 444 转换为 YCbCr 420(不使用内置函数)。
  • 将420结果保存到二进制文件im.yuv
  • 使用FFmpeg命令行工具将im.yuv转换为PNG格式,并显示结果。

代码如下:

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)

结果(转换为 YCbCr 420 并使用 FFmpeg 转换回 RGB 后):

【讨论】:

  • 虽然这确实很有意义,但我直接被指示不要使用 imresize 函数,这对我来说不必要地增加了难度
  • 我添加了一个没有使用imresize的解决方案。
  • 非常感谢您的详细解释!
  • 我实际上在使用它时遇到了麻烦。我也不允许使用基本的 rgb2ycbcr 函数,所以这可能会导致问题,但我转换后的图像是 2D 数组,而不是像我认为你展示的那样访问的 3D
  • @Sanam using imresize:仅将水平轴缩小 1/4:newU = imresize(U, [size(U, 1), size(U, 2)/4]);
猜你喜欢
  • 2021-07-18
  • 1970-01-01
  • 2017-05-27
  • 2018-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-04
相关资源
最近更新 更多