在OpenCV中将YUV转换为BGR或RGB

12

我有一张电视采集卡,它以YUV格式发送数据。我看到这里有其他帖子与这个问题类似,并尝试了所有可能的方法,但都没有提供清晰的图像。目前,使用OpenCV的cvCvtColor(scr, dst, CV_YUV2BGR)函数调用获得的结果最好。

目前我对YUV格式不太熟悉,有点困惑,因为它看起来像是存储了4个通道,但实际上只有3个?我已经包含了一张采集卡的图片,希望有人能理解可能正在发生的事情,并帮助我填补空白。

YUV转换为BGR的图像

该信号通过DeckLink Intensity Pro卡发送,并在Windows 7环境中使用OpenCV的C++应用程序进行访问。

更新

我查看了维基百科上关于此信息的文章,并尝试在我的应用程序中使用公式。下面是代码块以及从中获得的输出。非常感谢任何建议。

BYTE* pData;

    videoFrame->GetBytes((void**)&pData);

    m_nFrames++;

    printf("Num Frames executed: %d\n", m_nFrames);

    for(int i = 0; i < 1280 * 720 * 3; i=i+3)
    {
        m_RGB->imageData[i] = pData[i] + pData[i+2]*((1 - 0.299)/0.615);
        m_RGB->imageData[i+1] = pData[i] - pData[i+1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[i+2]*((0.299*(1 - 0.299))/(0.615*0.587));
        m_RGB->imageData[i+2] = pData[i] + pData[i+1]*((1 - 0.114)/0.436);
    }

尝试进行RGB转换


我目前对YUV格式不太了解。请参考维基百科上的相关内容:http://en.wikipedia.org/wiki/YUV#Conversion_to.2Ffrom_RGB - NickLH
谢谢你分享那篇文章,但我是否可以假设我将使用y、u、v和y1这些变量来生成RGB等效值?此外,我是否需要创建4个不同的数组来接收每个单独的值集,以将它们合并在一起? - Seb
你只需要3个数组,因为RGB有3个颜色分量。但是,你基本上需要单独计算每个颜色分量并将它们存储在某个地方。 - NickLH
6个回答

11
在更新版本的OPENCV中,有一个内置函数可以用于执行YUVRGB的转换。
使用 cvtColor(src,dst,CV_YUV2BGR_YUY2); 来实现。
在下划线后面指定YUV格式,例如CV_YUYV2BGR_xxxx

只有当YUV源是SD时,这才能正常工作。通过基本的视觉检查可能看起来正确,但实际上颜色并不正确。这是因为目前最新的OpenCV源码(2.4.10)只支持PAL/NTSC/SECAM的601色彩空间,而不支持HD的709或UHD的2020色彩空间。 - G Huxley

6

看起来你正在将YUV422流解码为YUV444。尝试对你提供的代码进行以下修改:

for(int i = 0, j=0; i < 1280 * 720 * 3; i+=6, j+=4)
{
    m_RGB->imageData[i] = pData[j] + pData[j+3]*((1 - 0.299)/0.615);
    m_RGB->imageData[i+1] = pData[j] - pData[j+1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[j+3]*((0.299*(1 - 0.299))/(0.615*0.587));
    m_RGB->imageData[i+2] = pData[j] + pData[j+1]*((1 - 0.114)/0.436);
    m_RGB->imageData[i+3] = pData[j+2] + pData[j+3]*((1 - 0.299)/0.615);
    m_RGB->imageData[i+4] = pData[j+2] - pData[j+1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[j+3]*((0.299*(1 - 0.299))/(0.615*0.587));
    m_RGB->imageData[i+5] = pData[j+2] + pData[j+1]*((1 - 0.114)/0.436);
}

我不确定你的常量是否正确,但最坏的情况是颜色会偏差 - 图像应该是可以识别的。


你说的常量不正确,你是对的。我现在看到了一个漂亮而清晰的图像。我需要研究YUV422常量来解决这个问题。感谢你的建议。 - Seb

5
我使用以下C++代码使用OpenCV将yuv数据(YUV_NV21)转换为rgb图像(OpenCV中的BGR):

我使用以下C++代码使用OpenCV将yuv数据(YUV_NV21)转换为rgb图像(OpenCV中的BGR)

int main()
{
  const int width  = 1280;
  const int height = 800;

  std::ifstream file_in;
  file_in.open("../image_yuv_nv21_1280_800_01.raw", std::ios::binary);
  std::filebuf *p_filebuf = file_in.rdbuf();
  size_t size = p_filebuf->pubseekoff(0, std::ios::end, std::ios::in);
  p_filebuf->pubseekpos(0, std::ios::in);

  char *buf_src = new char[size];
  p_filebuf->sgetn(buf_src, size);

  cv::Mat mat_src = cv::Mat(height*1.5, width, CV_8UC1, buf_src);
  cv::Mat mat_dst = cv::Mat(height, width, CV_8UC3);

  cv::cvtColor(mat_src, mat_dst, cv::COLOR_YUV2BGR_NV21);
  cv::imwrite("yuv.png", mat_dst);

  file_in.close();
  delete []buf_src;

  return 0;
}

转换后的结果如图yuv.png所示。

您可以从此处找到测试原始图像,以及从我的Github项目中找到整个项目。


2

也许这是错误的方法,但是很多人(我指的是工程师)会混淆YUV和YCbCr。

尝试:

cvCvtColor(src, dsc, CV_YCbCr2RGB) 

或者CV_YCrCb2RGB或者更为奇特的类型。

抱歉,应该是CV_YCbCr。OpenCV没有YUV枚举器。 - Seb
2
只有当YUV源为SD时,此方法才能正常工作。通过基本的视觉检查可能看起来正确,但实际上颜色并不正确。这是因为目前最新的OpenCV源(2.4.10)仅支持PAL / NTSC / SECAM的601色彩空间,而不支持高清的709或UHD的2020。 - G Huxley
Opencv 2.4.*确实有YUV枚举器,但没有CV_YCbCr2RGB。相反,有CV_YCrCb2RGB。 - Temak

2
BlackMagic Intensity软件以bmdFormat8BitYUV格式返回YUVY'格式,因此2个源像素被压缩成4个字节 - 我认为openCV的cvtColor无法处理这种格式。
您可以自己处理它,或者调用Intensity软件的ConvertFrame()函数。
编辑: YUV通常存储为
enter image description here 每个像素有一个亮度(Y),但每行中仅有每隔一个像素有一个色度(U和V)。
因此,如果data是指向上面所示内存起始位置的unsigned char。
像素1,Y = data[0] U = data[+1] V = data[+3]
像素2,Y = data[+2] U = data[+1] V = data[+3]
然后使用您在样本代码中使用的YUV->RGB系数。

我研究了那个函数,但它不能在IDeckLinkInputFrame类上工作。最好的方法似乎是自己做这个。谢谢你提到了“YUVY”,但你是否知道基于你发现的内容转换的公式呢?如果你不知道,请不要特意帮助我。谢谢。 - Seb
可能我把U/V和Cr/Cb搞反了,但这应该很明显! - Martin Beckett

1
也许有人会对颜色模型YCbCr和YUV感到困惑。 OpenCV不处理YCbCr,而是使用YCrCb,并且在OpenCV中实现方式与YUV相同。 从OpenCV源代码https://github.com/Itseez/opencv/blob/2.4/modules/imgproc/src/color.cpp#L3830可以看到:
case CV_BGR2YCrCb: case CV_RGB2YCrCb:
case CV_BGR2YUV: case CV_RGB2YUV:
    // ...
    // 1 if it is BGR, 0 if it is RGB
    bidx = code == CV_BGR2YCrCb || code == CV_BGR2YUV ? 0 : 2; 
    //... converting to YUV with the only difference that brings 
    //    order of Blue and Red channels (variable bidx)

但还有一件事要说。
目前在OpenCV 2.4.*分支中,转换CV_BGR2YUV和CV_RGB2YUV存在一个bug

目前实现中使用的公式为:

Y = 0.299B + 0.587G + 0.114R
U = 0.492(R-Y)
V = 0.877(B-Y)

根据wikipedia,它应该是这样的:
Y = 0.299R + 0.587G + 0.114B
U = 0.492(B-Y)
V = 0.877(R-Y)

通道红色和蓝色在实现的公式中错位了。
在修复该错误之前,可能的解决方法是将BGR转换为YUV。
  cv::Mat source = cv::imread(filename, CV_LOAD_IMAGE_COLOR);
  cv::Mat yuvSource;    
  cvtColor(source, yuvSource, cv::COLOR_BGR2RGB); // rearranges B and R in the appropriate order
  cvtColor(yuvSource, yuvSource, cv::COLOR_BGR2YUV);
  // yuvSource will contain here correct image in YUV color space

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接