全范围 YCbCr 到 RGB 并在 OpenCV 中返回不可逆
Full-range YCbCr to RGB and back in OpenCV not reversible
我正在尝试将 YCbCr 的简单变体转换为 RGB 并返回。我在 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分量对应的余域也为[0, 255]。我是否错误地实施了这个公式?如果不是,为什么它不可逆?如果它是不可逆的,是否还有另一个类似 YUV 的公式?
您的 YCbCr 元组超出了 RGB 的有效范围。
转换公式(BT.601)标准如下:
R' = 1.164*(Y' - 16) + 1.596*(Cr' - 128)
G' = 1.164*(Y' - 16) - 0.813*(Cr' - 128) - 0.392*(Cb' - 128)
B' = 1.164*(Y' - 16) + 2.017*(Cb' - 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 范围。
我正在尝试将 YCbCr 的简单变体转换为 RGB 并返回。我在 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分量对应的余域也为[0, 255]。我是否错误地实施了这个公式?如果不是,为什么它不可逆?如果它是不可逆的,是否还有另一个类似 YUV 的公式?
您的 YCbCr 元组超出了 RGB 的有效范围。
转换公式(BT.601)标准如下:
R' = 1.164*(Y' - 16) + 1.596*(Cr' - 128)
G' = 1.164*(Y' - 16) - 0.813*(Cr' - 128) - 0.392*(Cb' - 128)
B' = 1.164*(Y' - 16) + 2.017*(Cb' - 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 范围。