Различные преобразования цветов из 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
тогда YCBCRuint8
где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 охватывают весь диапазон значений.