【问题标题】:How to convert a YV12 image to BGRA using opencv?如何使用 opencv 将 YV12 图像转换为 BGRA?
【发布时间】:2022-01-12 01:53:28
【问题描述】:

要将 YV12 转换为 BGRA,我需要执行以下操作:

 cv::Mat yuv(height * 3/2, width, CV_8UC1, src);
 cv::Mat dst;
 cv::cvtColor(yuv, dst, CV_YUV2BGRA_YV12);

当“src”是一个连续的缓冲区时,该代码将起作用。

如果我有一个 YV12 缓冲区,其 Y、U、V 分量存储在 3 个单独的平面中并且它们不连续,我如何使用 cvtColor() 进行转换?

【问题讨论】:

  • 将组件复制到一个 Mat 中,可能使用 reshapes 和 copyTo。对不起,就是这样。 -- 或者改用一些驱动程序/硬件设施,这可能比在主机端做更快
  • 感谢@ChristophRackwitz。如果想将图像的平面复制到一个 cv::Mat 我不会在这里问。
  • 您可以等待“答案”,但不会有其他答案。 OpenCV 有这个过程,没有其他的。不要怪我。
  • 我只是想出了我的问题的目的。为什么你认为我“责备”了你?

标签: c++ opencv


【解决方案1】:

要使用 cv::cvtColor,您必须将单独的组件复制到连续的图像缓冲区中,如 Christoph Rackwitz 所述。

我不认为复制组件是微不足道的,我认为你应该得到答案。


连续的 YV12 图像采用以下结构:

  • Y(列 x 行)- 顶部图像。
  • V(列/2 x 行/2)- 低于 Y
  • U (cols/2 x rows/2) - 低于 V

我们可以分配一个连续的缓冲区,并将组件复制到分配的缓冲区中。

由于 OpenCV “隐藏”了 data 成员中的缓冲区,我认为使用 new 分配缓冲区并创建一个“包装”缓冲区的 cv:Mat 会更简单。

例子:

unsigned char *YV12 = new unsigned char[cols*rows*3/2]; //Allocate continuous memory buffer for storing YV12 in as single buffer.

cv::Mat inYV12 = cv::Mat(rows*3/2, cols, CV_8UC1, YV12); //cv::Mat wrapper of YV12 buffer.

将 cv:Mat 包装器定义为 Y、U 和 V,共享连续缓冲区:

cv::Mat inY0 = cv::Mat(rows, cols, CV_8UC1, YV12); //Copy Y color channel.
cv::Mat inU0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows); //U color channel (after Y).
cv::Mat inV0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows + cols*rows/4); //V color channel (after U).

将三个单独的图像复制到连续图像:

inY.copyTo(inY0);
inU.copyTo(inU0);
inV.copyTo(inV0);

完整的代码示例:

#include "opencv2/opencv.hpp"

int main()
{
    const int cols = 192;
    const int rows = 108;

    //Read Y, U and V into 3 separate buffers for testing. 
    ////////////////////////////////////////////////////////////////////////////
    unsigned char *Y = new unsigned char[cols*rows];
    unsigned char *U = new unsigned char[cols*rows / 4];
    unsigned char *V = new unsigned char[cols*rows / 4];

    FILE *f;
    f = fopen("test.yv12", "rb");   //Note: In Visual Studio, you may have to use fopen_s or define _CRT_SECURE_NO_WARNINGS
    fread(Y, 1, cols*rows, f); //Read Y
    fread(U, 1, cols*rows/4, f); //Read U
    fread(V, 1, cols*rows/4, f); //Read V
    fclose(f);

    //Define cv::Mat "wrappers".
    cv::Mat inY = cv::Mat(rows, cols, CV_8UC1, Y);
    cv::Mat inU = cv::Mat(rows/2, cols/2, CV_8UC1, U);
    cv::Mat inV = cv::Mat(rows/2, cols/2, CV_8UC1, V);
    ////////////////////////////////////////////////////////////////////////////

    unsigned char *YV12 = new unsigned char[cols*rows*3/2]; //Allocate continuous memory buffer for storing YV12 in as single buffer.

    cv::Mat inYV12 = cv::Mat(rows*3/2, cols, CV_8UC1, YV12); //cv::Mat wrapper of YV12 buffer.

    //Define cv::Mat "wrappers" to the continuous memory buffer.
    cv::Mat inY0 = cv::Mat(rows, cols, CV_8UC1, YV12); //Copy Y color channel.
    cv::Mat inU0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows); //U color channel (after Y).
    cv::Mat inV0 = cv::Mat(rows/2, cols/2, CV_8UC1, YV12 + cols*rows + cols*rows/4); //V color channel (after U).

    //Copy the three separate images to the continuous image.
    inY.copyTo(inY0);
    inU.copyTo(inU0);
    inV.copyTo(inV0);

    //Convert YV12 to BGRA:
    cv::Mat dstBGRA;
    //cv::cvtColor(inYV12, dstBGRA, cv::CV_YUV2BGRA_YV12);
    cv::cvtColor(inYV12, dstBGRA, cv::COLOR_YUV2BGRA_YV12);

    //Release memory
    delete[] Y;
    delete[] U;
    delete[] V;
    delete[] YV12;

    cv::imwrite("dstBGRA.png", dstBGRA);    //Save output for testing

    //Show result (for testing):
    cv::imshow("dstBGRA", dstBGRA);
    cv::waitKey();
    cv::destroyAllWindows();

    return 0;
}

输入(转换为连续灰度图):

输出:

【讨论】:

  • 感谢@Rotem 的详尽回答。您的解决方案可能会以某种方式工作,但是它使用了太多会损害性能的深层复制操作。我会投票给你,但不会接受你的回答。
  • 使用深拷贝是使用 OpenCV 解决问题的代价。你想让我试着用swscale解决它吗?
  • 我明白你的意思,感谢你的热情。我会尝试使用 IPP 或 swscale。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多