2012-03-25 5 views
3

OpenGL glReadPixels()에서 가져온 RGB 프레임을 YUV 프레임으로 변환하고 YUV 프레임을 파일 (.yuv)에 쓰려고합니다. 나중에 named_pipe에 FFMPEG의 입력으로 쓰고 싶지만 지금은 파일에 쓰고 YUV 이미지 뷰어를 사용하여 이미지 결과를보고 싶습니다. 그래서 지금은 "글쓰기"를 무시하십시오.C/C++에서 YUV 이미지 프레임을 쓸 때의 문제

  1. YUV 이미지 뷰어 소프트웨어에 표시된 프레임의 수는 항상 내가 내 프로그램에서 선언 된 프레임 수의 1/3 :

    내 코드를 실행 한 후, 나는 다음과 같은 오류가 발생했습니다. fps를 10으로 선언하면 3 프레임 만 볼 수 있습니다. fps를 30으로 선언하면 10 프레임 만 볼 수있었습니다. 그러나 텍스트 편집기에서 파일을 볼 때 "FRAME"이라는 단어가 파일에 올바르게 인쇄되어 있습니다. 다음은 출력 예입니다. http://www.bobdanani.net/image.yuv

  2. 올바른 이미지를 볼 수 없지만 녹색, 파란색, 노란색 및 검은 색 픽셀이 왜곡되어 있습니다.

나는 여기에 지금까지 시도한 것입니다 http://wiki.multimedia.cx/index.php?title=YUV4MPEG2http://www.fourcc.org/fccyvrgb.php#mikes_answerhttp://kylecordes.com/2007/pipe-ffmpeg

에서 YUV 형식에 대해 읽어보십시오. 이 변환 방법은 상당히 비효율적이며 나중에 최적화 할 수 있습니다. 이제는이 순진한 방식으로 작업하고 이미지가 제대로 표시되도록하고 싶습니다.

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); 
} 

이것은 매우 기본적인 접근 방법이며 나중에 변환 알고리즘을 최적화 할 수 있습니다. 누구나 내 접근 방식에서 잘못된 점을 말해 줄 수 있습니까? 내 생각 엔 unsigned char * 데이터를 char * 데이터로 변환했기 때문에 outstream.write() 호출과 관련된 문제가 데이터 정확도를 잃을 수 있다는 것입니다. 하지만 char *로 캐스팅하지 않으면 컴파일 오류가 발생합니다. 그러나 이것이 왜 출력 프레임이 손상되었는지 (총 프레임 수의 1/3에 해당) 설명하지 못합니다.

+0

해결 했습니까? –

답변

1

4 : 2 : 0 데이터의 경우 프레임 당 너무 많은 바이트가있는 것 같습니다. 연결된 사양에 따라 200x200 픽셀 4 : 2 : 0 프레임의 바이트 수는 200 * 200 * 3/2 = 60,000이어야합니다. 그러나 ~ 90,000 바이트가 있습니다. 코드를 보면 4 : 4 : 4에서 4 : 2 : 0으로 변환하는 위치를 볼 수 없습니다. 따라서 헤더를 4 : 4 : 4로 설정하거나 YCbCr 데이터를 4 : 2 : 0으로 변환 한 후 쓸 수 있습니다.

+0

이미 바이트 수를 200 * 200 * 3/2 (yuv420FrameLength에서)로 설정했습니다. 따라서 yuv 버퍼의 크기는 200 * 200 픽셀 이미지의 경우 60,000입니다. 또한 프레임 길이를 너비 * 높이 * 1.5 * fps로 설정합니다. 또한 다음 코드는 4 : 2 : 0 형식으로 데이터를 배치합니다. ypos = j * windowWidth + i;upos = (j/2) * (windowWidth/2) + i/2 + 길이; vpos = (j/2) * (windowWidth/2) + i/2 + 길이 + 길이/4; yuvdest [ypos] = y; yuvdest [upos] = u; yuvdest [vpos] = v; –

+0

아, 죄송합니다. 출력 파일을 다운로드하고 텍스트 편집기에서 보았을 때 첫 번째 "FRAME"과 두 번째 "FRAME"사이에 약 90,000 바이트가 있었지만 텍스트 편집기로 UTF8로 변환 되었기 때문에 그것이 나온 것입니다.16 진수 편집기에서이를 보면 6 만 바이트 밖에 없으므로 올바른 것으로 보입니다. – user1118321

+0

사진 전후 사진을 게시 할 수 있습니까? 그게 무슨 일이 더 명확하게 할 수 있습니다. 또한 4 : 4 : 4 데이터 (올바른 헤더 포함)를 작성하면 어떻게됩니까? 그러면 모든 프레임을 가져 옵니까? 또 다른 한가지 - 위키피디아 링크는 헤더가 'YUVMPEG4'(끝 부분에 공백이 있음)로 시작하고 모든 필드가 공백으로 시작한다는 것을 알 수 있습니다. YUV 표시와 너비 표시 사이에 2 칸이 필요합니까? (그것이 위키 백과에서 나왔는지 여부는 분명하지 않습니다.) – user1118321

0

나는 코드를 컴파일 했으므로 확실하게 upos와 vpos 값을 계산할 때 문제가있다. 나를 위해이 작품 (RGB에서 YUV NV12) :

vpos = length + (windowWidth * (j/2)) + (i/2)*2; 
upos = vpos + 1; 
관련 문제