如何在YUV420p帧上绘制矩形

3

我想在YUV420P帧上使用以下函数绘制一个红色矩形。下面的代码会改变帧并且我能看到两条黑线(顶部和底部)保持黑点分散。有什么建议吗?

void draw_rectangle(uint8_t *frame, int x, int y, 
                    int width, int height,
                    int img_width, int img_height)
{
    cv::Mat frame_yuv;
    int size[2];
    Point pt1, pt2;
    cv::Scalar color = Scalar(255, 0, 0);

    size[0] = img_width + img_width/2;
    size[1] = img_height;
    frame_yuv = cv::Mat(2, size, CV_8UC1, frame);

    pt1.x = x;
    pt1.y = y;
    pt2.x = x + width;
    pt2.y = y + height;

    rectangle(frame_yuv, pt1, pt2, Scalar(0, 0, 255));
}

我猜问题出在frame_yuv = cv::Mat(2, size, CV_8UC1, frame);,正如你所提到的,frame是YUV格式,这意味着它是一个3通道矩阵,但你正在使用CV_8UC1,它只代表单通道矩阵,将其更改为CV_8UC3可能会有所帮助。在绘制矩形之前,请确保能够正确地将uint8_t *frame转换为cv::Mat - ZdaR
我不相信在OpenCV Mat中可以“原样”存储YUV420帧。Mats每个样本为1、2、4或8字节,通道数为1、3或4,这意味着您只能获得每像素的这些倍数作为字节数,但是YUV420每像素有1.5字节,或者更正确地说是每4个像素6字节。您需要将每像素的字节数降至1字节和3个通道,即8UC3。 - Mark Setchell
这里是布局 https://en.m.wikipedia.org/wiki/YUV#/media/File%3AYuv420.svg 基本上,Y通道可以直接复制到Mat中,但U和V通道需要在X和Y维度上扩大两倍,然后合并成RGB 8UC3。 - Mark Setchell
请提供一个数据框架并给出其尺寸。 - Mark Setchell
我通过以下额外的步骤使代码工作。 - Venkata Subbarao
3个回答

3

终于,我的代码能够正常工作了。下面是参考步骤:

frame_yuv = cv::Mat(2, size, CV_8UC3, frame);

cv::Mat C(2,2, CV_8UC3, color);
cv::Mat C_yuv;
cvtColor(C, C_yuv, cv::COLOR_BGR2YUV_I420);

// Set the R, G, B values to C_yuv
// Extract the Y, U, V components to separate Mat's
// Apply rectange first on Y component
// Devide each points pt1, pt2 by 2
// Apply the rectange on U, V

不会额外复制框架。


恭喜你成功编写代码并分享出来。请考虑给那些帮助过你的答案点赞(可以点赞多个),以激励更多人在未来提供帮助。 - Mark Setchell

2

由于您没有提供样本数据,请使用@zindarod提供的文件,尺寸为144x176。

下面是YUV数据在内存中的样子:

enter image description here

请注意底部的流...所有的Y像素首先出现。然后所有的U像素按4倍降采样。然后所有的V像素也按4倍降采样。

我没有时间写OpenCV代码,但我可以向您展示如何将其制作成常规Mat。

第1步-提取Y通道

将前144x176个字节放入一个名为Y的144x176 8UC1 Mat中。

enter image description here

第2步-提取U通道

跳过前144x176个字节,然后将接下来的72x88个字节放入另一个名为U的72x88 8UC1 Mat中。将此Mat调整大小为宽度和高度加倍,即144x176。

enter image description here

第3步-提取V通道

跳过前(144x176)+(88x72)个字节,然后将接下来的72x88个字节放入另一个名为V的72x88 8UC1 Mat中。将此Mat调整大小为宽度和高度加倍,即144x176。

enter image description here

第4步-合并

将Y、U和V Mats合并成一个8UC3 Mat:

// Now merge the 3 individual channels into 3-band bad boy
auto channels = std::vector<cv::Mat>{Y, U, V};
cv::Mat ThreeBandBoy;
cv::merge(channels, ThreeBandBoy);

在此输入图片描述


这里有一些代码(链接),几乎可以满足步骤1-3的需求。


0

我正在从文件中读取这个YUV图像,它是YUV_I420格式。

fstream file;
file.open("yuv_i420.yuv", ios::in | ios::binary);

// size of image in RGB
size_t rows = 144, cols = 176;

if (!file.is_open())
stderr<<"Error opening file"<<endl;
else {
// get total size of file
auto size = file.tellg();
file.seekg(0,ios::end);
size = file.tellg() - size;
file.seekg(0,ios::beg);
char *buffer = new char[size];

// read image from file
if (file.read(buffer, size)) {
  // create YUV Mat
  Mat yuv_I420(rows + rows / 2, cols, CV_8UC1, buffer);

  // draw a rectangle on YUV image, keep in mind that the YUV image is a
  // single channel grayscale image, size is different than the BGR image
  rectangle(yuv_I420, Point(10, 10), Point(50, 50), Scalar(255));
  // convert to BGR to check validity
  Mat bgr;
  cvtColor(yuv_I420, bgr, cv::COLOR_YUV2BGR_I420);
  cv::imshow("image", bgr);
  cv::waitKey(0);
}

file.close();
delete[] buffer;
}

1
我认为那不正确。你已经增加了50%的行来容纳U的四分之一像素(或每个2x2区域下采样的1个像素),V也是如此。我认为你只在一个通道中绘制了矩形,而不是所有3个通道。我还认为如果你在图像的下部而不是靠近顶部绘制,它会出错。我没有投反对票或做任何愚蠢的事情,我只是不确定这是否正确。 - Mark Setchell
@MarkSetchell 请使用给定的文件运行代码,您会发现它是正确的。至于在YUV图像上绘制,我真的看不出有什么用处。如果OP想要得到正确的颜色,他将不得不在YUV图像中以5个不同的位置绘制矩形。 - zindarod
1
我还是不太确定,抱歉 :-( OP似乎试图通过单个rectangle()调用来绘制任意颜色的矩形。我仍然认为你不能用这段代码做到这一点。我认为你必须将数据重新布局为8UC3才能做出任何有意义的事情。 - Mark Setchell

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