О преобразовании YUV (YV12) в RGB с GLSL для iOS

Я пытаюсь преобразовать YUV (YV12) в RGB с шейдером GLSL.

Как показано ниже.

  1. читать необработанные данные YUV (YV12) из ​​файла изображения
  2. фильтрация Y, Cb и Cr из необработанных данных YUV (YV12)
  3. отображение текстуры
  4. отправить фрагментный шейдер.

но результирующее изображение не то же самое, что и необработанные данные.

изображение ниже - это необработанные данные.

снимок экрана со ссылкой на необработанное изображение (доступно для загрузки)

а изображение ниже - данные преобразования.

снимок экрана со ссылкой на преобразование изображения (доступно для загрузки)

и ниже мой исходный код.

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

а ниже - исходный код фрагментного шейдера.

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 данных изображения - это обратный вывод.

Как решить эту проблему?


person ddylee    schedule 31.01.2012    source источник
comment
Второй скриншот у меня не работает. Есть ли шанс пополнить запасы?   -  person Tommy    schedule 01.02.2012


Ответы (1)


Изображение, вероятно, зеркально отражается по горизонтали из-за простого несоответствия осей - OpenGL следует соглашению с миллиметровой бумагой, где (0, 0) - нижний левый угол, а ось Y направлена ​​вверх, тогда как почти все форматы графических изображений соответствуют английскому прочтению. соглашение о порядке, где (0, 0) - это верхний левый угол, а ось Y направлена ​​вниз. Просто переверните введенные координаты y (при необходимости в вершинном шейдере).

Что касается цветов, второй снимок экрана в настоящее время у меня не работает (согласно моему комментарию), но я думаю, что вы вычитаете 128 при создании imageU и imageV, а затем снова вычитаете 0,5 в своем шейдере. Предположительно, вы действительно хотите сделать только одно или другое (в частности, сделать это в шейдере, потому что данные текстуры беззнаковые)? Вы делаете ту же ошибку с imageY, но чистый эффект будет заключаться в небольшом затемнении изображения, а не в сдвиге всех цветов на полпути по шкале.

Моя единственная другая мысль заключается в том, что ваши отдельные текстуры имеют только один канал, поэтому лучше загружать их как GL_LUMINANCE, а не GL_RGBA.

person Tommy    schedule 01.02.2012