В Matlab, как я могу использовать субсэмплирование цветности, чтобы уменьшить изображение 4: 4: 4 до 4: 2: 0, когда изображение находится в YCbCr? - PullRequest
0 голосов
/ 18 февраля 2020

Я уже преобразовал изображения jpg из RGB в YCbCr, но теперь должен использовать Chroma Subsampling, чтобы сделать их 4: 2: 0. Я искал, но не нашел никакой информации о том, как это сделать (примечание: я очень новичок в Matlab)

Редактировать: теперь у меня есть это, но внизу, где я устанавливаю ycbcr (:,:, 2) = newCb: «Невозможно выполнить назначение, потому что размер левой стороны равен 1273 на 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;

1 Ответ

2 голосов
/ 18 февраля 2020

Вы можете просто изменить размеры Cb и Cr с коэффициентом 0,5 по каждой оси:

Допустим:

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

Y канал не изменен (то же самое Y для 4: 2: 0, как в формате 4: 4: 4)

Снижение дискретизации U и V с коэффициентом 0,5 для получения формата 4: 2: 0:

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

В MATLAB вы обычно хотите сохранить результат 420 Y, newU, newV и 3 матрицы (в плоском формате), а не объединять матрицы в одну матрицу.

Формат 4: 2: 0 не требует определенного порядка c компонентов (например, I420 или NV12 ...), поэтому три матрицы считаются в формате 4: 2: 0.


Снижение дискретизации без использования imresize:

Вы можете сэмплировать U и V, используя следующий пример кода:

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
  • Преобразовать im.yuv в формат PNG с помощью инструмента командной строки FFmpeg и показать результат.

Вот код:

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 и преобразования обратно в RGB с использованием FFmpeg):
enter image description here

...