全范围YCbCr到RGB以及返回OpenCV是不可逆的

时间:2016-07-22 15:45:51

标签: c++ opencv rgb yuv

我试图将YCbCr的简单变体转换为RGB并返回。公式I使用this page(RGB到全范围YCbCr,反之亦然)。

inline cv::Mat rgb_to_ycrcb(cv::Mat input) {
    cv::Mat output(input.size(), input.type(), cv::Scalar(0, 0, 0));
    float rgb_to_ycrcb_data[] = {
                0.2990, 0.5870, 0.114,
                -0.169, -0.331, 0.500,
                0.5000, -0.419, -0.081
            };
    cv::Mat rgb_to_ycrcb = cv::Mat(3, 3, CV_32FC1, rgb_to_ycrcb_data);

    for (int r = 0; r < input.rows; r++) {
        for (int c = 0; c < input.cols; c++) {
            cv::Vec3f rgb_value;
            cv::Mat input_rgb = cv::Mat(input.ptr<cv::Vec3b>(r)[c], false);
            input_rgb.convertTo(rgb_value, CV_32F);
            cv::Vec3f offset(0., 128., 128.);

            cv::Mat converted = cv::Mat(offset, false) +
                                rgb_to_ycrcb * cv::Mat(rgb_value, false);
            cv::Vec3b final_vec;
            converted.convertTo(final_vec,
                                CV_8U);
            output.ptr<cv::Vec3b>(r)[c] = converted;
        }
    }
    return output;
}

inline cv::Mat ycrcb_to_rgb(cv::Mat input) {
    cv::Mat output(input.size(), input.type(), cv::Scalar(0, 0, 0));
    float ycrcb_to_rgb_data[] = {
                1.000, 0.0000, 1.4000,
                1.000, -0.343, -0.711,
                1.000, 1.7650, 0.000
            };
    cv::Mat ycrcb_to_rgb = cv::Mat(3, 3, CV_32FC1, ycrcb_to_rgb_data);
    for (int r = 0; r < input.rows; r++) {
        for (int c = 0; c < input.cols; c++) {
            cv::Vec3f ycrcb_value;
            cv::Mat(input.ptr<cv::Vec3b>(r)[c], false).
            convertTo(ycrcb_value, CV_32F);
            ycrcb_value[1] -= 128;
            ycrcb_value[2] -= 128;
            cv::Mat converted = ycrcb_to_rgb * cv::Mat(ycrcb_value, false);
            cv::Vec3b final_vec;
            converted.convertTo(final_vec,
                                CV_8U);
            output.ptr<cv::Vec3b>(r)[c] = converted;
        }
    }
    return output;
}

当我将由元组(28,113,14)组成的YCrCb颜色格式的图像转换为RGB时,我得到(0,114,2)。将该RGB值转换回YCrCb会产生(67,91,80)。

全范围YCrCb的公式指定YCrCb的所有分量的域是[0,255],并且RGB分量的相应codomain也是[0,255]。我是否错误地实施了这个公式?如果没有,为什么它不可逆?如果它不可逆,那么还有另一种类似YUV的公式吗?

1 个答案:

答案 0 :(得分:0)

您的YCbCr元组超出RGB的有效范围 转换公式(BT.601)标准如下:

  

R&#39; = 1.164 *(Y&#39; - 16)+ 1.596 *(Cr&#39; - 128)

     

G&#39; = 1.164 *(Y&#39; - 16) - 0.813 *(Cr&#39; - 128) - 0.392 *(Cb&#39; - 128)

     

B&#39; = 1.164 *(Y&#39; - 16)+ 2.017 *(Cb&#39; - 128)

请参阅https://software.intel.com/en-us/node/503907

这不是您在代码中使用的确切公式,但原理是相同的。

将YCbCr元组值放在转换公式中:

R' = 1.164*(Y' - 16) + 1.596*(Cr' - 128) = -167.9760
G' = 1.164*(Y' - 16) - 0.813*(Cr' - 128) - 0.392*(Cb' - 128) = 112.5300
B' = 1.164*(Y' - 16) + 2.017*(Cb' - 128) = -16.2870

如您所见,R和B超出范围[0,255] 在我的例子中,R和B的值被钳制为零 在您的转换过程中,只有R被钳制为零 当钳制值时,结果是不可逆的。

尝试用图表说明(不确定它是否正确) 如您所见,YCbCr的值超过sRGB范围 enter image description here