如何从OpenCV中的YUV文件中读取一个帧?

时间:2010-02-09 18:39:04

标签: image-processing opencv video-processing

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

6 个答案:

答案 0 :(得分:6)

我写了一个非常简单的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

答案 1 :(得分:4)

如上所述,有很多种YUV格式:

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

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

  1. 为该帧数据创建适当大小的一维OpenCV Mat
  2. 为具有所需尺寸和3个通道
  3. 的RGB数据创建一个空Mat
  4. 最后使用cvtColor在两个Mats之间进行转换,使用正确的转换标志枚举
  5. 以下是 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 的尺寸。

答案 2 :(得分:3)

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

我发布的一些代码会读取单个 YUV 4:2:0平面图像文件。您可以直接将其应用于大多数YUV文件(只需继续读取同一个FILE对象)。 例外是处理YUV files that have a header时(通常,他们有*.y4m扩展名)。如果您想处理这些文件,您有两种选择:

  1. 在使用下面的代码
  2. 之前,编写自己的函数以使用FILE对象中的标题数据
  3. 从* .y4m图像中剥离标题(使用ffmpeg或类似工具)。这是我喜欢的选项,因为它是最简单的。
  4. 它也不适用于任何其他形式的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;
    }
    

答案 3 :(得分:0)

我认为不可能,至少在目前的版本中。当然,这并不难,但它不是一个有趣的特征,如:

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

但仍然可以使用cvCvtColor()进行转化,这意味着无论如何都会引起一些兴趣。

答案 4 :(得分:0)

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

以下是一个简单的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()

答案 5 :(得分:0)

供以后参考:我已将@xianyanlin's brilliant answer转换为Python3。以下代码适用于从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