Различное преобразование цвета из RGB в YCbCr с OpenCV и Matlab

Я пытаюсь воспроизвести алгоритм jpeg с помощью openCV после того, как успешно реализовал его в MATLAB. Я заметил, что MATLAB и OpenCV дают разные результаты для преобразования цветового пространства из RGB в YCbCr.

Глядя на документацию OpenCV, кажется, что единственной используемой функцией является cv::cvtColor, но при печати первой подматрицы 8x8 Y, Cb и Cr они не совпадают.

Вот мой код как для MATLAB, так и для C++ (с OpenCV 4.0.1).

Матлаб:

% Read rgb image
imgrgb = imread('lena512color.bmp');

% Convert to ycbcr
imgycbcr = rgb2ycbcr(imgrgb);

% Extract the 3 components
luma = imgycbcr (:,:,1);
cb = imgycbcr (:,:,2);
cr = imgycbcr (:,:,3);

C++:

// Load img
cv::Mat bgrImg = imread( "lena512color.bmp", cv::IMREAD_COLOR );
assert( bgrImg.data && "No image data");

// Declare an empty Mat for dst image
cv::Mat ycrcbImg;

// Convert to ycrcb
cv::cvtColor(bgrImg, ycrcbImg, cv::COLOR_BGR2YCrCb);

// Split bgr into 3 channels
cv::Mat bgrChan[3];
cv::split(bgrImg, bgrChan);

// Split ycrcb into 3 channels
cv::Mat ycrcbChan[3];
cv::split(ycrcbImg, ycrcbChan);

// Print first block for each channel
PRINT_MAT(ycrcbChan[0](cv::Rect(0, 0, 8, 8)), "LUMA (first 8x8 block)")
PRINT_MAT(ycrcbChan[1](cv::Rect(0, 0, 8, 8)), "Cr (first 8x8 block)")
PRINT_MAT(ycrcbChan[2](cv::Rect(0, 0, 8, 8)), "Cb (first 8x8 block)")

PRINT_MAT(bgrChan[0](cv::Rect(0, 0, 8, 8)), "Blue (first 8x8 block)")
PRINT_MAT(bgrChan[1](cv::Rect(0, 0, 8, 8)), "Green (first 8x8 block)")
PRINT_MAT(bgrChan[2](cv::Rect(0, 0, 8, 8)), "Red (first 8x8 block)")

где PRINT_MAT — следующий макрос:

#define PRINT_MAT(mat, msg) std::cout<< std::endl <<msg <<":" <<std::endl <<mat <<std::endl;

Распечатывая каналы RGB, я получаю одинаковые значения (для первого блока 8x8) как для Matlab, так и для OpenCV, а для Y, Cb и Cr я получаю разные значения. Например, для компонента Luma:

Матлаб:

155 155 155 154 155 150 156 154
155 155 155 154 155 150 156 154
155 155 155 154 155 150 156 154
155 155 155 154 155 150 156 154
155 155 155 154 155 150 156 154
157 157 151 149 154 153 152 153
154 154 156 152 154 155 153 150
152 152 149 150 152 152 150 151

OpenCV:

162 162 162 161 162 157 163 161
162 162 162 161 162 157 163 161
162 162 162 161 162 157 163 161
162 162 162 161 162 157 163 161
162 162 162 161 162 157 163 161
164 164 158 155 161 159 159 160
160 160 163 158 160 162 159 156
159 159 155 157 158 159 156 157

Что такое правильное преобразование? И почему результаты разные?


person ThugMazzola    schedule 10.02.2019    source источник


Ответы (2)


Глядя на исходный код MATLAB rgb2ycbcr, преобразование выполняется в соответствии с уравнением, взятым из "Poynton's "Introduction to Digital Video" (стр. 176, уравнения 9.6)":

origT = [ ...
     65.481 128.553  24.966;...
    -37.797 -74.203 112    ;...
    112     -93.786 -18.214];
origOffset = [16; 128; 128];
ycbcr = origT * rgb + origOffset;

И еще упоминается, что:

Если введено значение uint8, то YCBCR равно uint8, где Y находится в диапазоне [16 235], а Cb и Cr — в диапазоне [16 240].

В OCV (реализация), с другой стороны, это выполняется с использованием следующих отношений:

введите здесь описание изображения

Обратите внимание, как написано: «Y, Cr и Cb охватывают весь диапазон значений.».

Если мы используем те же уравнения в MATLAB, мы получим результат, намного более близкий к OCV (возможно, я использую другое исходное изображение). Например, для Y:

OCV_Y = 0.299*imgrgb(:,:,1) + 0.587*imgrgb(:,:,2) + 0.114*imgrgb(:,:,3);

Дает это первое 8x8:

   162   162   162   162   163   157   163   161
   162   162   162   162   163   157   163   161
   162   162   162   162   163   157   163   161
   162   162   162   162   163   157   163   161
   162   162   162   162   163   157   163   161
   164   164   158   155   162   159   159   160
   161   161   163   158   160   161   158   155
   159   159   156   156   159   158   157   157

Согласно статье в Википедии о YCbCr, кажется, что OCV реализует "JPEG Conversion", тогда как MATLAB реализует вариант ITU-R BT.601 для "телевидения стандартной четкости" .

В заключение: я бы сказал, что оба определения правильны, но если вас особенно волнует правильная реализация для JPEG, я бы сказал, что способ OCV лучше. В любом случае любой другой вариант очень просто реализовать в MATLAB.

person Dev-iL    schedule 10.02.2019
comment
Это хорошее предупреждение о том, что не существует единого определения YCbCr. Но также нет единого определения ни RGB, ни HSV, ни большинства других цветовых пространств. Если только вы явно не укажете стандарт, которому следуете. - person Cris Luengo; 10.02.2019

Получается, что оба верны. В то время как OpenCV принимает для Y целый диапазон, MATLAB использует [16, 235]. Обоснование можно увидеть в этом другом вопросе/ответе.

Как вы можете прочитать в документах MATLAB:

Изображение в цветовом пространстве YCbCr, возвращенное как массив m-by-n-by-3.

  • Если вход двойной или одинарный, то Y находится в диапазоне [16/255, 235/255], а Cb и Cr находятся в диапазоне [16/255, 240/255].
  • Если введено значение uint8, то Y находится в диапазоне [16, 235], а Cb и Cr — в диапазоне [16, 240].
  • Если ввод uint16, то Y находится в диапазоне [4112, 60395], а Cb и Cr находятся в диапазоне [4112, 61680].

В документах OpenCV:

  • Y = 0.299R + 0.587G + 0.114B
  • Cr = (R - Y)⋅0,713 + \дельта
  • Cb = (B - Y)⋅0,564 + \дельта

[...]

Y, Cr и Cb охватывают весь диапазон значений.

person Berriel    schedule 10.02.2019