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

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

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

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

Matlab:

% 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:

Matlab:

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

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

2 ответа

Решение

Глядя на исходный код MATLAB rgb2ycbcr преобразование выполняется в соответствии с уравнением, взятым из " Введение в цифровое видео" Пойнтона (стр. 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 ", тогда как MATLAB реализует вариант МСЭ-R BT.601 для "телевидения стандартной четкости".

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

Оказывается, оба "правильные". В то время как 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,114В
  • Cr = (R - Y)⋅0,713 + \ delta
  • Cb = (B - Y).50,564 + \ delta

[...]

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

Другие вопросы по тегам