关于使用GLSL for iOS将YUV(YV12)转换为RGB

时间:2012-01-31 05:55:29

标签: ios glsl shader rgb yuv

我正在尝试使用GLSL着色器将YUV(YV12)转换为RGB。

如下步骤。

  1. 从图像文件
  2. 读取原始YUV(YV12)数据
  3. 从原始YUV(YV12)数据中过滤Y,Cb和Cr
  4. 映射纹理
  5. 发送片段着色器。
  6. 但结果图像与原始数据不同。

    下图是原始数据。

    screenshot of raw image link(Available for download)

    以下图片是转换数据。

    screenshot of convert image link(Available for download)

    以下是我的源代码。

    - (void) readYUVFile     
    {    
          ...     
          NSData* fileData = [NSData dataWithContentsOfFile:file];    
          NSInteger width  = 720;    
          NSInteger height = 480;    
          NSInteger uv_width  = width  / 2;    
          NSInteger uv_height = height / 2;    
          NSInteger dataSize = [fileData length];    
    
          GLint nYsize  = width * height;     
          GLint nUVsize = uv_width * uv_height;      
          GLint nCbOffSet = nYsize;    
          GLint nCrOffSet = nCbOffSet + nUVsize;    
    
          Byte* uData = spriteData + nCbOffSet;    
          Byte* vData = uData + nUVsize;    
          GLfloat imageY[ 345600 ], imageU[ 86400 ], imageV[ 86400 ];    
    
          int x, y, nIndexY = 0, nIndexUV = 0;    
          for( y = 0; y < height; y++ )    
          {    
                    for( x = 0; x < width; x++ )    
                    {    
                              imageY[ nIndexY ] = (GLfloat)spriteData[ nIndexY ] - 16.0;    
                              if( (y < uv_height) && (x < uv_width) )    
                              {        
                                        imageU[ nIndexUV ] = (GLfloat)uData[ nIndexUV ] - 128.0;    
                                        imageV[ nIndexUV ] = (GLfloat)vData[ nIndexUV ] - 128.0;    
                                        nIndexUV++;    
                              }    
                              nIndexY++;    
                    }    
          }     
    
          m_YpixelTexture = [self textureY:imageY widthType:width heightType:height];    
          m_UpixelTexture = [self textureU:imageU widthType:uv_width heightType:uv_height];    
          m_VpixelTexture = [self textureV:imageV widthType:uv_width heightType:uv_height];    
    
          ...    
     }
    
    
    - (GLuint) textureY: (GLfloat*)imageData        
              widthType: (int) width       
             heightType: (int) height       
    {          
        GLuint texName;    
        glActiveTexture( GL_TEXTURE0 );    
        glGenTextures( 1, &texName );     
        glBindTexture( GL_TEXTURE_2D, texName );    
    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    
    
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData );    
    
        return texName;    
    }    
    
    - (GLuint) textureU: (GLfloat*)imageData        
              widthType: (int) width       
             heightType: (int) height       
    {          
        GLuint texName;    
        glActiveTexture( GL_TEXTURE1 );    
        glGenTextures( 1, &texName );     
        glBindTexture( GL_TEXTURE_2D, texName );    
    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    
    
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData );    
    
        return texName;    
    }    
    
    - (GLuint) textureV: (GLfloat*)imageData        
              widthType: (int) width       
             heightType: (int) height       
    {          
        GLuint texName;    
        glActiveTexture( GL_TEXTURE2 );    
        glGenTextures( 1, &texName );     
        glBindTexture( GL_TEXTURE_2D, texName );    
    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);    
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    
    
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData );    
    
        return texName;    
    }    
    

    及以下是Fragment Shader的源代码。

    uniform sampler2D Ytexture; // Y Texture Sampler    
    uniform sampler2D Utexture; // U Texture Sampler    
    uniform sampler2D Vtexture; // V Texture Sampler    
    varying highp vec2 TexCoordOut;    
    
    void main()    
    {    
        highp float y, u, v;
        highp float r, g, b;
    
        y = texture2D( Ytexture, TexCoordOut ).p;
        u = texture2D( Utexture, TexCoordOut ).p;
        v = texture2D( Vtexture, TexCoordOut ).p;
    
        y = 1.1643 * ( y - 0.0625 );
        u = u - 0.5;
        v = v - 0.5;
    
        r = y + 1.5958 * v;
        g = y - 0.39173 * u - 0.81290 * v;
        b = y + 2.017 * u;
    
        gl_FragColor = highp vec4( r, g, b, 1.0 );
    }
    

    Y数据很好,但U和V数据不好。并且图像数据的y轴是反向输出。

    如何解决此问题?

1 个答案:

答案 0 :(得分:1)

由于轴的简单分歧,图像可能在水平方向上镜像 - OpenGL遵循方格纸惯例,其中(0,0)是左下角,y轴向上,而几乎所有图形图像格式都遵循英语阅读顺序约定,其中(0,0)是左上角,y轴是向下。只需翻转您的输入y坐标(如果需要,可以在顶点着色器中)。

至于颜色,第二个截图目前对我不起作用(根据我的评论),但我最好的猜测是你在构建imageU和imageV时减去128,然后再在着色器中减去0.5。大概你实际上只想做一个或那个(具体来说,在着色器中做它因为纹理数据是无符号的)?你使用imageY犯了同样的错误,但净效果只是略微变暗图像,而不是将所有颜色移动到刻度的一半。

我唯一的另一个想法是你的个人纹理只有一个通道,所以最好将它们上传为GL_LUMINANCE而不是GL_RGBA。