2012-01-23 15 views
6

Tôi đang cố gắng hoàn thành Chuyển đổi YV12 sang RGB được đề cập in this post bằng trình đổ bóng GLSL.Sự cố khi chuyển đổi YV12 sang RGB qua GLSL

Ứng dụng của tôi tải khung YV12 thô từ đĩa và cố thực hiện chuyển đổi bằng trình đổ bóng GLSL. Tuy nhiên, ảnh kết quả được lật theo chiều dọc và có một số vấn đề về màu sắc. Tôi nghĩ rằng vấn đề có thể là hình ảnh đang được đọc như là một mảng của char (1 byte) và sau đó chuyển đổi thành một mảng của GLushort (2 byte). Bạn nghĩ sao?

Đây là cách khung YUV liệu trông giống như:

enter image description here

and the raw frame loaded by the application can be downloaded from here.

và đây là kết quả tôi nhận được:

enter image description here

Tôi chia sẻ mã nguồn của ứng dụng dưới đây:

#include <assert.h> 
#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#include <GL/glew.h> 
#include <GL/glut.h> 
#include <GL/glu.h> 

#include <iostream> 

#include <fstream> 

#ifndef SEEK_SET 
# define SEEK_SET 0 
#endif 

static GLfloat Xrot = 0, Yrot = 0, Zrot = 0; 
static GLint ImgWidth, ImgHeight; 
static GLushort *ImageYUV = NULL; 


static void DrawObject(void) 
{ 
    glBegin(GL_QUADS); 

    glTexCoord2f(0, 0); 
    glVertex2f(-1.0, -1.0); 

    glTexCoord2f(ImgWidth, 0); 
    glVertex2f(1.0, -1.0); 

    glTexCoord2f(ImgWidth, ImgHeight); 
    glVertex2f(1.0, 1.0); 

    glTexCoord2f(0, ImgHeight); 
    glVertex2f(-1.0, 1.0); 

    glEnd(); 
} 

static void Display(void) 
{ 
    glClear(GL_COLOR_BUFFER_BIT); 

    glPushMatrix(); 
     glRotatef(Xrot, 1.0, 0.0, 0.0); 
     glRotatef(Yrot, 0.0, 1.0, 0.0); 
     glRotatef(Zrot, 0.0, 0.0, 1.0); 
     DrawObject(); 
    glPopMatrix(); 

    glutSwapBuffers(); 
} 

static void Reshape(int width, int height) 
{ 
    glViewport(0, 0, width, height); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 

    // Vertical flip so texture appears right 
    glFrustum(-1.0, 1.0, 1.0, -1.0, 10.0, 100.0); 
    //glFrustum(-1.0, 1.0, -1.0, 1.0, 10.0, 100.0); 

    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glTranslatef(0.0, 0.0, -15.0); 
} 

static void Key(unsigned char key, int x, int y) 
{ 
    (void) x; 
    (void) y; 
    switch (key) { 
     case 27: 
     exit(0); 
     break; 
    } 
    glutPostRedisplay(); 
} 

static void SpecialKey(int key, int x, int y) 
{ 
    float step = 3.0; 
    (void) x; 
    (void) y; 

    switch (key) { 
     case GLUT_KEY_UP: 
     Xrot += step; 
     break; 
     case GLUT_KEY_DOWN: 
     Xrot -= step; 
     break; 
     case GLUT_KEY_LEFT: 
     Yrot += step; 
     break; 
     case GLUT_KEY_RIGHT: 
     Yrot -= step; 
     break; 
    } 
    glutPostRedisplay(); 
}   

bool CheckShader(int n_shader_object) 
{ 
    int n_tmp; 
    glGetShaderiv(n_shader_object, GL_COMPILE_STATUS, &n_tmp); 
    bool b_compiled = n_tmp == GL_TRUE; 
    int n_log_length; 
    glGetShaderiv(n_shader_object, GL_INFO_LOG_LENGTH, &n_log_length); 
    // query status ... 

    if(n_log_length > 1) { 
     char *p_s_temp_info_log; 
     if(!(p_s_temp_info_log = (char*)malloc(n_log_length))) 
      return false; 
     int n_tmp; 
     glGetShaderInfoLog(n_shader_object, n_log_length, &n_tmp, 
      p_s_temp_info_log); 
     assert(n_tmp <= n_log_length); 

     fprintf(stderr, "%s\n", p_s_temp_info_log); 
     free(p_s_temp_info_log); 
    } 
    // get/concat info-log 

    return b_compiled; 
} 

static void Init(int argc, char *argv[]) 
{ 
    GLuint texObj = 100; 
    const char *file; 

    printf("Checking GL_ARB_texture_rectangle\n"); 
    if (!glutExtensionSupported("GL_ARB_texture_rectangle")) { 
     printf("Sorry, GL_ARB_texture_rectangle is required\n"); 
     exit(0); 
    } 

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 

    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texObj); 
#ifdef LINEAR_FILTER 
    /* linear filtering looks much nicer but is much slower for Mesa */ 
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
#else 
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
#endif 

    std::ifstream yuv_file("data.yv12", std::ios::in | std::ios::binary | std::ios::ate); 
    if (!yuv_file.is_open()) 
    { 
     std::cout << "> GLWidget::GLWidget !!! Failed to load yuv file"; 
     return; 
    } 
    int yuv_file_sz = yuv_file.tellg(); 

    ImgWidth = 1280; 
    ImgHeight = 720; 
    ImageYUV = new GLushort[yuv_file_sz]; 

    char* memblock = new char[yuv_file_sz]; 
    if (!memblock) 
    { 
     std::cout << "> GLWidget::GLWidget !!! Failed to allocate memblock"; 
     return; 
    } 

    yuv_file.seekg(0, std::ios::beg); 
    yuv_file.read(memblock, yuv_file_sz); 
    yuv_file.close(); 

    // A simple "memcpy(ImageYUV, memblock, yuv_file_sz);" 
    // won't work because the data read is stored as char (1 byte) and GLushort is 2 bytes. 
    // So, doing a manual copy: 
    for (int i = 0; i < yuv_file_sz; i++) 
    { 
     ImageYUV[i] = (GLushort)memblock[i]; 
    } 
    delete[] memblock; 

    printf("Image: %dx%d\n", ImgWidth, ImgHeight); 

    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 
       GL_LUMINANCE_ALPHA, ImgWidth, ImgHeight, 0, 
       GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, ImageYUV); 

    assert(glGetError() == GL_NO_ERROR); 

    glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 
        0, 0, ImgWidth, ImgHeight, 
        GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, ImageYUV); 

    assert(glGetError() == GL_NO_ERROR); 

    delete[] ImageYUV; 

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 

    glEnable(GL_TEXTURE_RECTANGLE_ARB); 

    glShadeModel(GL_FLAT); 
    glClearColor(0.3, 0.3, 0.4, 1.0); 

    static const char *p_s_vertex_shader = 
     "varying vec2 t;" 
     "void main()" 
     "{" 
     " t = gl_MultiTexCoord0.xy;" 
     " gl_Position = ftransform();" 
     "}"; 
    static const char *p_s_fragment_shader = 
     "#extension GL_ARB_texture_rectangle : enable\n" 
     "varying vec2 t;" 
     "uniform sampler2DRect tex;" 
     "void main()" 
     "{" 
     " vec2 tcEven = vec2(floor(t.x * .5) * 2.0, t.y);" 
     " vec2 tcOdd = vec2(tcEven.x + 1.0, t.y);" 
     " float Cb = texture2DRect(tex, tcEven).x - .5;" 
     " float Cr = texture2DRect(tex, tcOdd).x - .5;" 
     " float y = texture2DRect(tex, t).w;" // redundant texture read optimized away by texture cache 
     " float r = y + 1.28033 * Cr;" 
     " float g = y - .21482 * Cb - .38059 * Cr;" 
     " float b = y + 2.12798 * Cb;" 
     " gl_FragColor = vec4(r, g, b, 1.0);" 
     "}"; 

    int v = glCreateShader(GL_VERTEX_SHADER); 
    int f = glCreateShader(GL_FRAGMENT_SHADER); 
    int p = glCreateProgram(); 
    glShaderSource(v, 1, &p_s_vertex_shader, 0); 
    glShaderSource(f, 1, &p_s_fragment_shader, 0); 
    glCompileShader(v); 
    CheckShader(v); 
    glCompileShader(f); 
    CheckShader(f); 
    glAttachShader(p, v); 
    glAttachShader(p, f); 
    glLinkProgram(p); 
    glUseProgram(p); 
    glUniform1i(glGetUniformLocation(p, "tex"), 0); 

    if (argc > 1 && strcmp(argv[1], "-info")==0) { 
     printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 
     printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); 
     printf("GL_VENDOR  = %s\n", (char *) glGetString(GL_VENDOR)); 
     printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); 
    } 
} 


int main(int argc, char *argv[]) 
{ 
    glutInit(&argc, argv); 
    glutInitWindowSize(1280, 720); 
    glutInitWindowPosition(0, 0); 
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); 
    glutCreateWindow(argv[0]); 
    glewInit(); 

    Init(argc, argv); 

    glutReshapeFunc(Reshape); 
    glutKeyboardFunc(Key); 
    glutSpecialFunc(SpecialKey); 
    glutDisplayFunc(Display); 

    glutMainLoop(); 
    return 0; 
} 

Trả lời

11

Vấn đề ở đây là rằng hình ảnh là thực sự không phải YV12, máy bay chrominance và độ sáng không xen kẽ, nhưng được đặt trong các khối. Điều này có thể được giải quyết theo hai cách, hoặc xen kẽ các máy bay trước khi tải nó vào kết cấu và sử dụng phần còn lại của mã như vậy, hoặc nó có thể được thực hiện trong bóng đổ. Tôi loại bỏ iostream và thay thế nó bằng stdio (tôi đang sử dụng trình biên dịch khá cũ). Đây là mã của tôi để tải hình ảnh và xen kẽ nó:

GLubyte *memblock; 
{ 
    FILE *p_fr = fopen("data.yv12", "rb"); 
    if(!p_fr) { 
     fprintf(stderr, "!!! Failed to load yuv file\n"); 
     return; 
    } 
    fseek(p_fr, 0, SEEK_END); 
    int yuv_file_sz = ftell(p_fr); 
    fseek(p_fr, 0, SEEK_SET); 
    memblock = new GLubyte[yuv_file_sz]; 
    if(!memblock) { 
     fprintf(stderr, "!!! Failed to allocate memblock\n"); 
     return; 
    } 
    fread(memblock, yuv_file_sz, 1, p_fr); 
    fclose(p_fr); 
} 
// load .raw file 

ImgWidth = 1280; 
ImgHeight = 720; 
ImageYUV = new GLushort[ImgWidth * ImgHeight]; 
// allocate an image 

int chromaWidth = ImgWidth/2; 
int chromaHeight = ImgHeight/2; // 2x2 luminance subsampling 
const GLubyte *pCb = memblock + ImgWidth * ImgHeight; // Cb block after Y 
const GLubyte *pCr = pCb + chromaWidth * chromaHeight; // Cr block after Cb 
// get pointers to smaller Cb and Cr blocks (the image is *not* interleaved) 

for(int i = 0; i < ImgWidth * ImgHeight; ++ i) { 
    int x = i % ImgWidth; 
    int y = i/ImgWidth; 
    GLubyte cb = pCb[(x/2) + (y/2) * chromaWidth]; 
    GLubyte cr = pCr[(x/2) + (y/2) * chromaWidth]; 
    ImageYUV[i] = (memblock[i] << 8) | ((x & 1)? cr : cb); 
} 
// convert (interleave) the data to YV12 

Điều này khá đơn giản và có thể được sử dụng với trình đổ bóng ở trên.

Bây giờ điều gì xảy ra nếu chúng ta muốn bỏ qua sự xen kẽ? Đầu tiên, tôi sẽ tìm ra cách thức hoạt động giải quyết vấn đề ở đây (chúng ta sẽ hành động như hình ảnh là hình ảnh đơn sắc cao hơn một chút, những chiếc máy bay chrominance chụp không gian trên máy bay sáng):

for(int y = 0; y < ImgHeight; ++ y) { 
    for(int x = 0; x < ImgWidth; ++ x) { 
     int CbY = ImgHeight + (y/4); 
     int CrY = ImgHeight + chromaHeight/2 + (y/4); 
     int CbCrX = (x/2) + chromaWidth * ((y/2) & 1); 
     // calculate x, y of cr and cb pixels in the grayscale image 
     // where the Y, Cb anc Cr blocks are next to each other 

     assert(&memblock[CbCrX + CbY * ImgWidth] == &pCb[(x/2) + (y/2) * chromaWidth]); 
     assert(&memblock[CbCrX + CrY * ImgWidth] == &pCr[(x/2) + (y/2) * chromaWidth]); 
     // make sure the addresses are correct (and they are) 

     GLubyte cb = memblock[CbCrX + CbY * ImgWidth]; 
     GLubyte cr = memblock[CbCrX + CrY * ImgWidth]; 
     GLubyte Y = memblock[x + y * ImgWidth]; 

     ImageYUV[x + y * ImgWidth] = (Y << 8) | ((x & 1)? cr : cb); 
    } 
} 
// convert (interleave) the data to YV12 (a little bit different way, use physical layout in memory) 

Điều đó có khá nhiều tác dụng tương tự. Bây giờ chúng ta có thể lấy mã để tính toán các vị trí và đặt nó vào bóng đổ.

static const char *p_s_fragment_shader = 
    "#extension GL_ARB_texture_rectangle : enable\n" 
    "uniform sampler2DRect tex;" 
    "uniform float ImgHeight, chromaHeight_Half, chromaWidth;" 
    "void main()" 
    "{" 
    " vec2 t = gl_TexCoord[0].xy;" // get texcoord from fixed-function pipeline 
    " float CbY = ImgHeight + floor(t.y/4.0);" 
    " float CrY = ImgHeight + chromaHeight_Half + floor(t.y/4.0);" 
    " float CbCrX = floor(t.x/2.0) + chromaWidth * floor(mod(t.y, 2.0));" 
    " float Cb = texture2DRect(tex, vec2(CbCrX, CbY)).x - .5;" 
    " float Cr = texture2DRect(tex, vec2(CbCrX, CrY)).x - .5;" 
    " float y = texture2DRect(tex, t).x;" // redundant texture read optimized away by texture cache 
    " float r = y + 1.28033 * Cr;" 
    " float g = y - .21482 * Cb - .38059 * Cr;" 
    " float b = y + 2.12798 * Cb;" 
    " gl_FragColor = vec4(r, g, b, 1.0);" 
    "}"; 

Bằng cách sử dụng đổ bóng này, chúng ta có thể trực tiếp tải lên các dữ liệu thô đến một kết cấu, ngoại trừ nó là một chút cao hơn và chỉ GL_LUMINANCE:

glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 
       GL_LUMINANCE, ImgWidth, ImgHeight + ImgHeight/2, 0, // !! 
       GL_LUMINANCE, GL_UNSIGNED_BYTE, memblock); // !! 

tôi sẽ để nó ở đó. Dưới đây là mã nguồn hoàn chỉnh:

interleaving in shader (faster, preferrable)
manual interleaving in "C"

Xin lỗi vì sự kết thúc nhanh chóng, tôi sẽ có vấn đề nếu tôi không rời khỏi nơi làm việc của tôi càng sớm càng tốt :).

+0

+1 Để có ** câu trả lời chất lượng cao **. Điều này nên được bỏ phiếu rất nhiều! – karlphillip

+0

Trên Linux, với cả card đồ họa ATI và Intel [kết quả là màu vàng-ish] (http://i41.tinypic.com/2ynmrl0.jpg). Bạn có biết điều gì có thể xảy ra không? – karlphillip

+0

Nhân tiện, mã của chúng tôi trình bày kết quả được lật theo chiều dọc (lộn ngược).Để khắc phục điều đó, chỉ cần thay đổi 'glFrustum()' bên trong 'Reshape()' thành 'glFrustum (-1.0, 1.0, 1.0, -1.0, 10.0, 100.0);'. Tôi đã sửa lỗi của tôi trong câu hỏi. – karlphillip