在C/C++中编写YUV图像帧的问题

2024-02-06

我正在尝试将从 OpenGL glReadPixels() 获取的 RGB 帧转换为 YUV 帧,并将 YUV 帧写入文件 (.yuv)。后来我想将其写入命名管道作为 FFMPEG 的输入,但现在我只想将其写入文件并使用 YUV 图像查看器查看图像结果。因此,暂时忽略“写入管道”。

运行我的代码后,我遇到了以下错误:

  1. YUV Image Viewer 软件中显示的帧数始终是我在程序中声明的帧数的 1/3。当我声明 fps 为 10 时,我只能查看 3 帧。当我声明 fps 为 30 时,我只能观看 10 帧。但是,当我在文本编辑器中查看该文件时,我可以看到文件中打印了正确数量的“FRAME”一词。 这是我得到的示例输出:http://www.bobdanani.net/image.yuv http://www.bobdanani.net/image.yuv

  2. 我看不到正确的图像,只能看到一些扭曲的绿色、蓝色、黄色和黑色像素。

我读到了有关 YUV 格式的信息http://wiki.multimedia.cx/index.php?title=YUV4MPEG2 http://wiki.multimedia.cx/index.php?title=YUV4MPEG2 and http://www.fourcc.org/fccyvrgb.php#mikes_answer http://www.fourcc.org/fccyvrgb.php#mikes_answer and http://kylecordes.com/2007/pipe-ffmpeg http://kylecordes.com/2007/pipe-ffmpeg

这是我到目前为止所尝试过的。我知道这种转换方式效率相当低,以后可以优化一下。现在我只想让这种简单的方法发挥作用并正确显示图像。

int frameCounter = 1; 
int windowWidth = 0, windowHeight = 0;
unsigned char *yuvBuffer;
unsigned long bufferLength = 0;
unsigned long frameLength = 0;
int fps = 10;

void display(void) {

    /* clear the color buffers */
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /* DRAW some OPENGL animation, i.e. cube, sphere, etc 
     .......
     .......
    */

    glutSwapBuffers();

    if ((frameCounter % fps) == 1){
        bufferLength = 0;
        windowWidth = glutGet(GLUT_WINDOW_WIDTH);
        windowHeight = glutGet (GLUT_WINDOW_HEIGHT);
        frameLength = (long) (windowWidth * windowHeight * 1.5 * fps) + 100; // YUV 420 length (width*height*1.5) + header length
        yuvBuffer = new unsigned char[frameLength];
        write_yuv_frame_header();
    }

    write_yuv_frame();

    frameCounter = (frameCounter % fps) + 1;

    if ( (frameCounter % fps) == 1){
        snprintf(filename, 100, "out/image-%d.yuv", seq_num);
        ofstream out(filename, ios::out | ios::binary); 
        if(!out) { 
            cout << "Cannot open file.\n"; 
        } 

        out.write (reinterpret_cast<char*> (yuvBuffer), bufferLength);
        out.close();
        bufferLength = 0;
        delete[] yuvBuffer;
    }
}


void write_yuv_frame_header (){
    char *yuvHeader = new char[100];
    sprintf (yuvHeader, "YUV4MPEG2 W%d H%d F%d:1 Ip A0:0 C420mpeg2 XYSCSS=420MPEG2\n", windowWidth, windowHeight, fps);
    memcpy ((char*)yuvBuffer + bufferLength, yuvHeader, strlen(yuvHeader));
    bufferLength += strlen (yuvHeader);
    delete (yuvHeader);
}

void write_yuv_frame() {
    int width = glutGet(GLUT_WINDOW_WIDTH);
    int height = glutGet(GLUT_WINDOW_HEIGHT);
    memcpy ((void*) (yuvBuffer+bufferLength), (void*) "FRAME\n", 6);
    bufferLength +=6;

    long length = windowWidth * windowHeight;
    long yuv420FrameLength = (float)length * 1.5;
    long lengthRGB = length * 3;
    unsigned char *rgb      = (unsigned char *) malloc(lengthRGB * sizeof(unsigned char));
    unsigned char *yuvdest  = (unsigned char *) malloc(yuv420FrameLength * sizeof(unsigned char));
    glReadPixels(0, 0, windowWidth, windowHeight, GL_RGB, GL_UNSIGNED_BYTE, rgb);

    int r, g, b, y, u, v, ypos, upos, vpos;

    for (int j = 0; j <  windowHeight; ++j){
        for (int i = 0; i < windowWidth; ++i){
            r = (int)rgb[(j * windowWidth + i) * 3 + 0];
            g = (int)rgb[(j * windowWidth + i) * 3 + 1];
            b = (int)rgb[(j * windowWidth + i) * 3 + 2];

            y = (int)(r *  0.257 + g *  0.504 + b *  0.098) + 16;
            u = (int)(r *  0.439 + g * -0.368 + b *  -0.071) + 128;
            v = (int)(r *  -0.148 + g * -0.291 + b * 0.439 + 128);

            ypos = j * windowWidth + i;
            upos = (j/2) * (windowWidth/2) + i/2 + length;
            vpos = (j/2) * (windowWidth/2) + i/2 + length + length/4;

            yuvdest[ypos] = y;
            yuvdest[upos] = u;
            yuvdest[vpos] = v;            
        } 
    }

    memcpy ((void*) (yuvBuffer + bufferLength), (void*)yuvdest, yuv420FrameLength);
    bufferLength += yuv420FrameLength;
    free (yuvdest);   
    free (rgb);
}

这只是非常基本的做法,稍后我可以优化转换算法。 谁能告诉我我的方法有什么问题吗?我的猜测是问题之一是 outstream.write() 调用,因为我将 unsigned char* 数据转换为 char* 数据,它可能会丢失数据精度。但如果我不将其转换为 char* 我会收到编译错误。然而,这并不能解释为什么输出帧被损坏(仅占总帧数的 1/3)。


在我看来,对于 4:2:0 数据,每帧的字节数太多。根据您链接到的规范,200x200 像素 4:2:0 帧的字节数应为 200 * 200 * 3 / 2 = 60,000。但你有大约 90,000 字节。查看您的代码,我不知道您在哪里从 4:4:4 转换为 4:2:0。因此,您有 2 个选择 - 要么将标头设置为 4:4:4,要么在写出之前将 YCbCr 数据转换为 4:2:0。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在C/C++中编写YUV图像帧的问题 的相关文章

随机推荐