使用OpenCV和Matlab从RGB到YCbCr进行不同的颜色转换

时间:2019-02-10 10:38:07

标签: c++ matlab opencv rgb color-conversion

在MATLAB中成功实现jpeg算法后,我正在尝试使用openCV复制jpeg算法。我注意到MATLAB和OpenCV对于从RGB到YCbCr的色彩空间转换给出了不同的结果。

查看OpenCV的文档,似乎唯一使用的功能是cv :: cvtColor,但是打印Y,Cb和Cr的第一个8x8子矩阵,它们并不相同。

这是我用于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通道,对于Matlab和OpenCV,我获得相同的值(对于第一个8x8块),而对于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 个答案:

答案 0 :(得分:2)

事实证明两者都是“正确的”。尽管OpenCV对Y采取了整个范围,但MATLAB却适用于[16,235]。可以在另一个question/answer中看到其推理。

您可以在MATLAB docs中阅读:

  

YCbCr颜色空间中的图像,以m×n×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 docs中:

  
      
  • Y = 0.299R + 0.587G + 0.114B
  •   
  • Cr =(R-Y)⋅0.713+ \ delta
  •   
  • Cb =(B-Y)⋅0.564+ \ delta
  •   
     

[...]

     

Y,Cr和Cb覆盖整个值范围

答案 1 :(得分:2)

通过查看MATLAB rgb2ycbcr的源代码,可以根据“ Poynton的“数字视频简介”(第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]范围内,并且CbCr为在[16 240]范围内。

另一方面,

In OCV implementation使用以下关系执行:

enter image description here

请注意它是怎么说的:“ 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

根据wikipedia article on YCbCr,似乎OCV实现了“ JPEG Conversion”变体,而MATLAB的实现了“标准清晰度电视”的ITU-R BT.601变体。

总结:我会说这两个定义都是正确,但是如果您特别关心JPEG的正确实现,我会说 OCV的方法更好。无论如何,在MATLAB中实现任何其他变体都非常容易。

相关问题