在OpenCV中把YUV转换为BGR或RGB

12 人关注

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

我目前还不知道YUV格式,说实话,它让我有点困惑,因为它看起来像是存储了4个通道,但其实只有3个?我附上了一张来自采集卡的图像,希望有人能明白可能发生了什么,我可以用它来填补空白。

饲料是通过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);
    
3 个评论
"我目前不知道YUV格式" --> 维基百科。阅读一下: en.wikipedia.org/wiki/YUV#Conversion_to.2from_RGB
Seb
谢谢你链接那篇文章,但我是否可以假设我将使用y、u、v和y1,并使用这些变量来产生RGB等值?另外,我是否必须创建4个不同的数组来接收每一组单独的数值,以便将它们全部合并在一起?
你只需要3个数组,因为在RGB中有3个颜色成分。但是,是的,你基本上需要单独计算颜色成分,并将它们存储在某个地方。
c++
windows
opencv
video-streaming
Seb
Seb
发布于 2011-10-31
6 个回答
kyencn
kyencn
发布于 2015-04-27
0 人赞同

在较新版本的 OPENCV ,有一个内置的函数可以用来做 YUV RGB 的转换。

cvtColor(src,dst,CV_YUV2BGR_YUY2);

在下划线后面指定 YUV 的格式,像这样 CV_YUYV2BGR_xxxx

这只有在YUV源为标清时才能正常工作。通过基本的视觉检查,它可能看起来是正确的,但实际上颜色并不正确。这是因为现在最新的OpenCV源(2.4.10)的YUV转换只支持PAL/NTSC/SECAM的601色彩空间,不支持HD的709或UHD的2020。
Mark Ransom
Mark Ransom
发布于 2015-04-27
已采纳
0 人赞同

在我看来,你正在将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);

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

Seb
你说常数不正确是对的。我看到了一个漂亮而清晰的图像。我只得研究YUV422常数来纠正这个问题。谢谢你的建议。
Gavin Gao
Gavin Gao
发布于 2015-04-27
0 人赞同

我使用下面的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项目中找到。

Sam
Sam
发布于 2015-04-27
0 人赞同

这可能是一条错误的道路,但许多人(我是说工程师)确实将YUV与YCbCr混合在一起。

cvCvtColor(src, dsc, CV_YCbCr2RGB) 

或CV_YCrCb2RGB或也许更奇特的类型。

Seb
我很抱歉,它是CV_YCbCr。OpenCV没有一个YUV的枚举器。
这只有在YUV源为标清时才能正常工作。通过基本的视觉检查,它可能看起来是正确的,但实际上颜色并不正确。这是因为现在最新的OpenCV源(2.4.10)的YUV转换只支持PAL/NTSC/SECAM的601色彩空间,不支持HD的709或UHD的2020。
Opencv 2.4.*确实有YUV枚举器,但没有CV_YCbCr2RGB。而是有CV_YCrCb2RGB。
Martin Beckett
Martin Beckett
发布于 2015-04-27
0 人赞同

BlackMagic Intensity软件在bmdFormat8BitYUV中返回YUVY'格式,所以2个源像素被压缩成4bytes - 我认为openCV的cvtColor不能处理这个问题。

你可以自己做,或者直接调用Intensity软件的ConvertFrame()函数

编辑:Y U V通常存储为

每个像素都有一个Y(亮度),但在行中的每个交替的像素只有一个U和V(颜色)。

因此,如果数据是一个无符号的char,指向内存的开始,如上图所示。

像素1,Y = data[0] U = data[+1] V = data[+3)
像素2,Y = data[+2] U = data[+1] V = data[+3] 。

然后使用你在示例代码中使用的YUV->RGB系数。

Seb
我研究了那个函数,但它对IDeckLinkInputFrame类不起作用。最好的方法似乎是自己来做这个。谢谢你提到什么是YUVY',但你是否碰巧知道基于你偶然发现的转换公式?如果你不知道,请不要不顾一切地帮助我。谢谢
ps可能把U/V Cr/Cb弄错了--但这应该是很明显的!
Temak
Temak
发布于 2015-04-27
0 人赞同

也许有人对颜色模型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_BGR2YUVCV_RGB2YUV的转换存在一个错误

目前,这个公式在执行中被使用。

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

它应该是什么(根据维基百科)。

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);