如何在 OpenCV 中从 YUV 文件读取帧?

发布于 2024-08-21 15:40:25 字数 31 浏览 3 评论 0原文

如何在 OpenCV 中从 YUV 文件读取帧?

How to read a frame from YUV file in OpenCV?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

药祭#氼 2024-08-28 15:40:25

我编写了一个非常简单的 python 代码来从二进制文件读取 YUV NV21 流。

import cv2
import numpy as np

class VideoCaptureYUV:
    def __init__(self, filename, size):
        self.height, self.width = size
        self.frame_len = self.width * self.height * 3 / 2
        self.f = open(filename, 'rb')
        self.shape = (int(self.height*1.5), self.width)

    def read_raw(self):
        try:
            raw = self.f.read(self.frame_len)
            yuv = np.frombuffer(raw, dtype=np.uint8)
            yuv = yuv.reshape(self.shape)
        except Exception as e:
            print str(e)
            return False, None
        return True, yuv

    def read(self):
        ret, yuv = self.read_raw()
        if not ret:
            return ret, yuv
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_NV21)
        return ret, bgr


if __name__ == "__main__":
    #filename = "data/20171214180916RGB.yuv"
    filename = "data/20171214180916IR.yuv"
    size = (480, 640)
    cap = VideoCaptureYUV(filename, size)

    while 1:
        ret, frame = cap.read()
        if ret:
            cv2.imshow("frame", frame)
            cv2.waitKey(30)
        else:
            break

I wrote a very simple python code to read YUV NV21 stream from binary file.

import cv2
import numpy as np

class VideoCaptureYUV:
    def __init__(self, filename, size):
        self.height, self.width = size
        self.frame_len = self.width * self.height * 3 / 2
        self.f = open(filename, 'rb')
        self.shape = (int(self.height*1.5), self.width)

    def read_raw(self):
        try:
            raw = self.f.read(self.frame_len)
            yuv = np.frombuffer(raw, dtype=np.uint8)
            yuv = yuv.reshape(self.shape)
        except Exception as e:
            print str(e)
            return False, None
        return True, yuv

    def read(self):
        ret, yuv = self.read_raw()
        if not ret:
            return ret, yuv
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_NV21)
        return ret, bgr


if __name__ == "__main__":
    #filename = "data/20171214180916RGB.yuv"
    filename = "data/20171214180916IR.yuv"
    size = (480, 640)
    cap = VideoCaptureYUV(filename, size)

    while 1:
        ret, frame = cap.read()
        if ret:
            cv2.imshow("frame", frame)
            cv2.waitKey(30)
        else:
            break
狼性发作 2024-08-28 15:40:25

如前所述,YUV 格式有多种类型:

http://www.fourcc.org/yuv.php< /a>

在 OpenCV 中从 YUV 格式转换为 RGB 非常简单:

  1. 为该帧数据创建一个适当大小的一维 OpenCV Mat
  2. 为具有所需尺寸和 3 个通道的 RGB 数据创建一个空 Mat
  3. 最后使用cvtColor 使用正确的转换标志枚举在两个 Mat 之间进行转换

以下是 YV12 格式的 YUV 缓冲区的示例:

Mat mYUV(height + height/2, width, CV_8UC1, (void*) frameData);
Mat mRGB(height, width, CV_8UC3);
cvtColor(mYUV, mRGB, CV_YUV2RGB_YV12, 3);

关键技巧是在之前定义 RGB Mat 的尺寸/em> 你转换。

As mentioned, there are MANY types of YUV formats:

http://www.fourcc.org/yuv.php

To convert to RGB from a YUV format in OpenCV is very simple:

  1. Create a one-dimensional OpenCV Mat of the appropriate size for that frame data
  2. Create an empty Mat for the RGB data with the desired dimension AND with 3 channels
  3. Finally use cvtColor to convert between the two Mats, using the correct conversion flag enum

Here is an example for a YUV buffer in YV12 format:

Mat mYUV(height + height/2, width, CV_8UC1, (void*) frameData);
Mat mRGB(height, width, CV_8UC3);
cvtColor(mYUV, mRGB, CV_YUV2RGB_YV12, 3);

The key trick is to define the dimensions of your RGB Mat before you convert.

优雅的叶子 2024-08-28 15:40:25

更新这里有更新版本的代码:https://github.com/ chelyaev/opencv-yuv

我发布了一些代码,它将读取单个 YUV 4:2:0 平面图像文件。您可以直接将其应用于大多数 YUV 文件(只需继续从同一 FILE 对象读取)。 例外是在处理具有标头的 YUV 文件时 (通常,它们具有 *.y4m 扩展名)。如果您想处理此类文件,您有两个选择:

  1. 在使用下面的代码之前编写自己的函数来使用 FILE 对象中的标头数据
  2. 从 *.y4m 图像中剥离标头(使用 < code>ffmpeg 或类似工具)。这是我更喜欢的选项,因为它是最简单的。

它也不适用于任何其他形式的 YUV 格式(非平面、不同色度抽取)。正如 @Stephane 指出的那样,有很多这样的格式(其中大多数没有任何标识标头),这可能就是 OpenCV 不支持它们的原因。

但使用它们相当简单:

  • 从图像及其尺寸开始(读取 YUV 文件时需要这样做)
  • 将亮度和色度读入 3 个单独的图像
  • 将色度图像升级 2 倍以补偿色度抽取。 请注意,实际上有几种方法来补偿色度抽取。上采样只是最简单的
  • 合并成YUV图像。如果您想要 RGB,可以使用cvCvtColor

最后是代码:

IplImage * 
cvLoadImageYUV(FILE *fin, int w, int h)
{
    assert(fin);

    IplImage *py      = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *pu      = cvCreateImage(cvSize(w/2,h/2), IPL_DEPTH_8U, 1);
    IplImage *pv      = cvCreateImage(cvSize(w/2,h/2), IPL_DEPTH_8U, 1);
    IplImage *pu_big  = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *pv_big  = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *image   = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 3);
    IplImage *result  = NULL;

    assert(py);
    assert(pu);
    assert(pv);
    assert(pu_big);
    assert(pv_big);
    assert(image);

    for (int i = 0; i < w*h; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        py->imageData[i] = (unsigned char) j;
    }

    for (int i = 0; i < w*h/4; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        pu->imageData[i] = (unsigned char) j;
    }

    for (int i = 0; i < w*h/4; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        pv->imageData[i] = (unsigned char) j;
    }

    cvResize(pu, pu_big, CV_INTER_NN);
    cvResize(pv, pv_big, CV_INTER_NN);
    cvMerge(py, pu_big, pv_big, NULL, image);

    result = image;

cleanup:
    cvReleaseImage(&pu);
    cvReleaseImage(&pv);

    cvReleaseImage(&py);
    cvReleaseImage(&pu_big);
    cvReleaseImage(&pv_big);

    if (result == NULL)
        cvReleaseImage(&image);

    return result;
}

UPDATE there's a newer version of the code here: https://github.com/chelyaev/opencv-yuv

I'm posting some code that will read a single YUV 4:2:0 planar image file. You can directly apply this to most YUV files (just keep reading from the same FILE object). The exception to this is when dealing with YUV files that have a header (typically, they have a *.y4m extension). If you want to deal with such files, you have two options:

  1. Write your own function to consume the header data from the FILE object before using the code below
  2. Strip the headers from *.y4m images (using ffmpeg or similar tool). This is the option I prefer since it's the simplest.

It also will not work for any other form of YUV format (non-planar, different chroma decimation). As @Stephane pointed out, there are many such formats (and most of them don't have any identifying headers), which is probably why OpenCV doesn't support them out of the box.

But working with them is fairly simple:

  • Start with an image and it's dimensions (this is required when reading a YUV file)
  • Read luma and chroma into 3 separate images
  • Upscale chroma images by a factor of 2 to compensation for chroma decimation. Note that there are actually several ways to compensate for chroma decimation. Upsampling is just the simplest
  • Combine into YUV image. If you want RGB, you can use cvCvtColor.

Finally, the code:

IplImage * 
cvLoadImageYUV(FILE *fin, int w, int h)
{
    assert(fin);

    IplImage *py      = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *pu      = cvCreateImage(cvSize(w/2,h/2), IPL_DEPTH_8U, 1);
    IplImage *pv      = cvCreateImage(cvSize(w/2,h/2), IPL_DEPTH_8U, 1);
    IplImage *pu_big  = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *pv_big  = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *image   = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 3);
    IplImage *result  = NULL;

    assert(py);
    assert(pu);
    assert(pv);
    assert(pu_big);
    assert(pv_big);
    assert(image);

    for (int i = 0; i < w*h; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        py->imageData[i] = (unsigned char) j;
    }

    for (int i = 0; i < w*h/4; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        pu->imageData[i] = (unsigned char) j;
    }

    for (int i = 0; i < w*h/4; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        pv->imageData[i] = (unsigned char) j;
    }

    cvResize(pu, pu_big, CV_INTER_NN);
    cvResize(pv, pv_big, CV_INTER_NN);
    cvMerge(py, pu_big, pv_big, NULL, image);

    result = image;

cleanup:
    cvReleaseImage(&pu);
    cvReleaseImage(&pv);

    cvReleaseImage(&py);
    cvReleaseImage(&pu_big);
    cvReleaseImage(&pv_big);

    if (result == NULL)
        cvReleaseImage(&image);

    return result;
}
药祭#氼 2024-08-28 15:40:25

我认为至少在当前版本中这是不可能的。当然,这并不难做到,但这并不是一个有趣的功能,因为:

  • OpenCV通常适用于RGB格式的网络摄像头流,或者直接解码为RGB进行显示的编码文件目的;
  • OpenCV 致力于计算机视觉,例如,YUV 是一种不如编码社区常见的格式;
  • 有很多不同的 YUV 格式,这意味着实现它们需要大量的工作。

不过,使用 cvCvtColor() 仍然可以进行转换,这意味着无论如何它还是有一定意义的。

I don't think it is possible to do, at least with the current version. Of course, it wouldn't be that difficult to do, but it is not such an interesting feature, as:

  • OpenCV usually works on webcam stream, which are in RGB format, or on coded files, which are directly decoded into RGB for display purposes ;
  • OpenCV is dedicated to Computer Vision, where YUV is a less common format than in the Coding community for example ;
  • there are a lot of different YUV formats, which would imply a lot of work to implement them.

Conversions are still possible though, using cvCvtColor(), which means that it is of some interest anyway.

孤蝉 2024-08-28 15:40:25

我遇到了同样的问题。我的解决方案是
1.读取一个yuv帧(如I420)到一个字符串对象“yuv”。
2.将yuv帧转换为BGR24格式。我使用 libyuv 来做到这一点。为 libyuv 函数编写 python 包装器很容易。现在你得到另一个 BGR24 格式的字符串对象“bgr”。
3.使用numpy.fromstring从“bgr”字符串对象中获取图像对象。您需要更改图像对象的形状。

下面是一个简单的yuv查看器供大家参考。

import cv2
# below is the extension wrapper for libyuv
import yuvtorgb
import numpy as np

f = open('i420_cif.yuv', 'rb')

w = 352
h = 288
size = 352*288*3/2

while True:
    try:
        yuv = f.read(size)
    except:
        break
    if len(yuv) != size:
        f.seek(0, 0)
        continue

    bgr = yuvtorgb.i420_to_bgr24(yuv, w, h)

    img = np.fromstring(bgr, dtype=np.uint8)
    img.shape = h,w,3

    cv2.imshow('img', img)

    if cv2.waitKey(50) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()

I encountered the same problem. My solution is
1. read one yuv frame (such as I420) to a string object "yuv".
2. convert the yuv frame to BGR24 format. I use libyuv to do it. It is easy to write a python wrapper for libyuv functions. now you get another string object "bgr" with BGR24 format.
3. use numpy.fromstring to get image object from the "bgr" string object. you need to change the shape of the image object.

Below is a simple yuv viewer for your reference.

import cv2
# below is the extension wrapper for libyuv
import yuvtorgb
import numpy as np

f = open('i420_cif.yuv', 'rb')

w = 352
h = 288
size = 352*288*3/2

while True:
    try:
        yuv = f.read(size)
    except:
        break
    if len(yuv) != size:
        f.seek(0, 0)
        continue

    bgr = yuvtorgb.i420_to_bgr24(yuv, w, h)

    img = np.fromstring(bgr, dtype=np.uint8)
    img.shape = h,w,3

    cv2.imshow('img', img)

    if cv2.waitKey(50) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()
真心难拥有 2024-08-28 15:40:25

供将来参考:我已将 @xianyanlin 的出色答案转换为 Python 3。以下代码适用于从 Raspberry Pi 拍摄的视频相机并似乎输出正确的颜色和纵横比。

警告:它使用numpy格式来指定高度*宽度的分辨率,例如1080 * 1920、480 * 640。

class VideoCaptureYUV:
    def __init__(self, filename, size):
        self.height, self.width = size
        self.frame_len = self.width * self.height * 3 // 2
        self.f = open(filename, 'rb')
        self.shape = (int(self.height*1.5), self.width)

    def read_raw(self):
        try:
            raw = self.f.read(self.frame_len)
            yuv = np.frombuffer(raw, dtype=np.uint8)
            yuv = yuv.reshape(self.shape)
        except Exception as e:
            print(str(e))
            return False, None
        return True, yuv

    def read(self):
        ret, yuv = self.read_raw()
        if not ret:
            return ret, yuv
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_I420, 3)
        return ret, bgr

For future reference: I have converted @xianyanlin's brilliant answer to Python 3. The below code works with videos taken from the Raspberry Pi camera and seems to output correct color and aspect ratio.

Warning: it uses the numpy format for specifying resolution of height * width, e.g. 1080 * 1920, 480 * 640.

class VideoCaptureYUV:
    def __init__(self, filename, size):
        self.height, self.width = size
        self.frame_len = self.width * self.height * 3 // 2
        self.f = open(filename, 'rb')
        self.shape = (int(self.height*1.5), self.width)

    def read_raw(self):
        try:
            raw = self.f.read(self.frame_len)
            yuv = np.frombuffer(raw, dtype=np.uint8)
            yuv = yuv.reshape(self.shape)
        except Exception as e:
            print(str(e))
            return False, None
        return True, yuv

    def read(self):
        ret, yuv = self.read_raw()
        if not ret:
            return ret, yuv
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_I420, 3)
        return ret, bgr
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文