我正在尝试将图像从RBG(OpenCV中的BGR)手动转换为YCbCr颜色空间。
我的图像是png
彩色图像,宽度为800,高度为600,具有3个通道,16位深度。
这是我尝试解决此问题的方式。
cv::Mat convertToYCbCr(cv::Mat image) {
// converts an RGB image to YCbCr
// cv::Mat: B-G-R
std::cout << "Converting image to YCbCr color space." << std::endl;
int i, j;
for (i = 0; i <= image.cols; i++) {
for (j = 0; j <= image.rows; j++) {
// R, G, B values
auto R = image.at<cv::Vec3d>(j, i)[2];
auto G = image.at<cv::Vec3d>(j, i)[1];
auto B = image.at<cv::Vec3d>(j, i)[0];
// Y'
auto Y = image.at<cv::Vec3d>(j,i)[0] = 0.299 * R + 0.587 * G + 0.114 * B + 16;
// Cb
auto Cb = image.at<cv::Vec3d>(j,i)[1] = 128 + (-0.169 * R -0.331 * G + 0.5 * B);
// Cr
auto Cr = image.at<cv::Vec3d>(j,i)[2] = 128 + (0.5 * R -0.419 * G -0.081 * B);
std::cout << "At conversion: Y = " << Y << ", Cb = " << Cb << ", "
<< Cr << std::endl;
}
}
std::cout << "Converting finished." << std::endl;
return image;
}
我收到的图像如下:
我期望的是这种情况(使用OpenCV方法):
垂直线可能暗示了什么?我的循环错了吗?我什至可以只将RGB值替换为YCbCr值,并期望图像看起来像示例吗? typeid()
对于两个图像N2cv3MatE
返回相同的值。
答案 0 :(得分:1)
您应该使用cv::cvtColor
cvtColor(src, target_image, cv::COLOR_RGB2YCrCb);
然后只需翻转第二和第三频道。
尽管您可能会收到该错误,因为您没有将结果值转换为整数。
答案 1 :(得分:1)
观察到不正确结果的主要原因是用于访问图像的数据类型不正确。用于访问16位无符号像素的正确类型是cv::Vec3w
(而不是cv::Vec3d
)。
下一个问题是,用于转换的系数是为模拟信号(YPbPr)设计的。对于数字图像,我们必须使用为数字图像设计的系数(YCbCr)。您可以在 ITU-R BT.601转换部分的Wikipedia article on YCbCr中找到更多详细信息。
文章缺少的一条信息是,如果图像具有16位无符号深度或32位浮点深度,系数将如何变化?答案是必须根据图像的位深度缩放系数。
对于具有16位无符号深度的图像,应按以下步骤进行缩放:
auto Y = (R * 65.481f * scale) + (G * 128.553f * scale) + (B * 24.966f * scale) + (16.0f * offset);
auto Cb = (R * -37.797f * scale) + (G * -74.203f * scale) + (B * 112.0f * scale) + (128.0f * offset);
auto Cr = (R * 112.0f * scale) + (G * -93.786f * scale) + (B * -18.214f * scale) + (128.0f * offset);
其中scale
等于257.0/65535.0
,而offset
等于257.0
。
此转换技术已从rgb2ycbcr
函数的MATLAB源代码中采用,该函数引用了以下有关缩放的书籍:
C.A。 Poynton,“数字视频技术简介”,John Wiley &Sons,Inc.,1996,第9章,第175页`
现在转换已经完成,我们面临的第三个问题是类似于OpenCV的图像可视化。当我们使用OpenCV执行颜色转换时,输出图像以YCrCb
的顺序存储,而不是通常的YCbCr
。因此,要使用我们的自定义转换逻辑获得相同的图像,我们必须按相关顺序存储值。
示例转换代码可能如下所示:
if(image.type() == CV_16UC3)
{
const float scale = 257.0f / 65535.0f;
const float offset = 257.0f;
for (int i = 0; i < image.cols; i++)
{
for (int j = 0; j < image.rows; j++)
{
auto R = image.at<cv::Vec3w>(j, i)[2];
auto G = image.at<cv::Vec3w>(j, i)[1];
auto B = image.at<cv::Vec3w>(j, i)[0];
auto Y = (R * 65.481f * scale) + (G * 128.553f * scale) + (B * 24.966f * scale) + (16.0f * offset);
auto Cb = (R * -37.797f * scale) + (G * -74.203f * scale) + (B * 112.0f * scale) + (128.0f * offset);
auto Cr = (R * 112.0f * scale) + (G * -93.786f * scale) + (B * -18.214f * scale) + (128.0f * offset);
image.at<cv::Vec3w>(j, i)[0] = (unsigned short)Y;
image.at<cv::Vec3w>(j, i)[1] = (unsigned short)Cr;
image.at<cv::Vec3w>(j, i)[2] = (unsigned short)Cb;
}
}
}