Pregunta

Tengo problemas para mostrar un archivo YUV sin formato que está en formato NV12.

Puedo mostrar un marco seleccionado, sin embargo, todavía está principalmente en blanco y negro con ciertos tonos de rosa y verde.

Así es como se ve mi salidaalt text

De todos modos, así es como funciona mi programa.(Esto se hace en cacao/objective-c, pero necesito su consejo experto sobre el algoritmo del programa, no sobre la sintaxis).

Antes de la ejecución del programa, el archivo YUV se almacena en un archivo binario llamado "test.yuv".El archivo está en formato NV12, lo que significa que primero se almacena el plan Y y luego se entrelaza el plan UV.La extracción de mi archivo no tiene ningún problema porque hice muchas pruebas con él.

Durante el inicio, creo una tabla de búsqueda que convertirá binarios/8 bits/un byte/caracteres en valores flotantes Y, U, V respetados.

Para el avión Y este es mi código

-(void)createLookupTableY //creates a lookup table for converting a single byte into a float between 0 and 1
{
    NSLog(@"YUVFrame: createLookupTableY");
    lookupTableY = new float [256];

    for (int i = 0; i < 256; i++)
    {
        lookupTableY[i] = (float) i /255;
        //NSLog(@"lookupTableY[%d]: %f",i,lookupTableY[i]);//prints out the value of each float
    }
}

La tabla de búsqueda del plano U

-(void)createLookupTableU //creates a lookup table for converting a single byte into a float between 0 and 1
{
    NSLog(@"YUVFrame: createLookupTableU");
    lookupTableU = new float [256];

    for (int i = 0; i < 256; i++)
    {
        lookupTableU[i] =  -0.436 + (float) i / 255* (0.436*2);
        NSLog(@"lookupTableU[%d]: %f",i,lookupTableU[i]);//prints out the value of each float
    }
}

Y la tabla de consulta V

-(void)createLookupTableV //creates a lookup table for converting a single byte into a float between 0 and 1
{
    NSLog(@"YUVFrame: createLookupTableV");
    lookupTableV = new float [256];

    for (int i = 0; i < 256; i++)
    {
        lookupTableV[i] =  -0.615 + (float) i /255 * (0.615*2);
        NSLog(@"lookupTableV[%d]: %f",i,lookupTableV[i]);//prints out the value of each float
    }
}

Después de este punto, extraigo el plan Y y UV y los almaceno en dos buffers, yBuffer y uvBuffer.

En este punto, intento convertir los datos YUV y almacenarlos en una matriz de búfer RGB llamada "frameImage".

-(void)sortAndConvert//sort the extracted frame data into an array of float
{
    NSLog(@"YUVFrame: sortAndConvert");
    int frameImageCounter = 0;
    int pixelCounter = 0;
    for (int y = 0; y < YUV_HEIGHT; y++)//traverse through the frame's height
    {
        for ( int x = 0; x < YUV_WIDTH; x++)//traverse through the frame's width
        {

            float Y = lookupTableY [yBuffer [y*YUV_WIDTH + x] ];
            float U = lookupTableU [uvBuffer [ ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2  ] ]; 
            float V = lookupTableV [uvBuffer [  ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2 + 1] ];

            float RFormula = Y + 1.13983f * V;
            float GFormula = Y - 0.39465f * U - 0.58060f * V;
            float BFormula = Y + 2.03211f * U;

             frameImage [frameImageCounter++] = [self clampValue:RFormula];
             frameImage [frameImageCounter++] = [self clampValue:GFormula];
             frameImage [frameImageCounter++] = [self clampValue:BFormula];

        }
    }

}

luego intenté dibujar la imagen en OpenGl

-(void)drawFrame:(int )x
{

    GLuint texture;

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, YUV_WIDTH, YUV_HEIGHT, 0, GL_RGB, GL_FLOAT, frameImage);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);
    glRotatef( 180.0f, 1.0f, 0.0f, 0.0f );

    glBegin( GL_QUADS );
    glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
    glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
    glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
    glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
    glEnd();

    glFlush();
}

Básicamente, este es mi programa en pocas palabras.Básicamente, leo los archivos binarios YUV y almaceno todos los datos en un búfer de matriz de caracteres.Luego traduzco estos valores a sus respetados valores flotantes YUV.

Aquí es donde creo que podría estar el error:en la tabla de búsqueda Y estandaricé el plano Y a [0,1], en el plano U estandaricé los valores entre [-0.435,0.436], y en el plano V los estandaricé entre [-0.615,0.615].Hice esto porque esos son los rangos de valores YUV según wikipedia.

Y la fórmula de YUV a RGB es la misma fórmula de Wikipedia.También he probado varias otras fórmulas, y esta es la única fórmula que da una perspectiva aproximada del marco.Cualquiera podría aventurarse a adivinar por qué mi programa no muestra correctamente los datos del marco YUV.Creo que tiene algo que ver con mi técnica de estandarización, pero me parece bien.

He realizado muchas pruebas y estoy 100% seguro de que el error se debe a la tabla de consulta.No creo que mis fórmulas de configuración sean correctas.

Una nota para todos los que están leyendo esto.Durante mucho tiempo, mi marco no se mostraba correctamente porque no pude extraer los datos del marco correctamente.

Cuando comencé a programar, tuve la impresión de que en un clip de, por ejemplo, 30 cuadros, los datos planos de 30 años se escriben primero en el archivo de datos, seguido y luego por datos de plano UV.

Lo que descubrí a través de prueba y error fue que para un archivo de datos YUV, específicamente NV12, el archivo de datos se almacena de la siguiente manera

Y(1) UV(1) Y(2) UV(2) ......

@nschmidt

Cambié mi código al que sugeriste.

float U = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 ] ]
float V = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 + 1] ]

y este es el resultado que obtengoalt text

Aquí está la línea de impresión desde la consola.Imprimo los valores para el valor Y, U, V y RGB que se están traduciendo y almacenando en la matriz de FrameMage

YUV:[0.658824,-0.022227,-0.045824] RGBFinal:[0.606593,0.694201,0.613655]

YUV:[0.643137,-0.022227,-0.045824] RGBFinal:[0.590906,0.678514,0.597969]

YUV:[0.607843,-0.022227,-0.045824] RGBFinal:[0.555612,0.643220,0.562675]

YUV:[0.592157,-0.022227,-0.045824] RGBFinal:[0.539926,0.627534,0.546988]

YUV:[0.643137,0.025647,0.151941] RGBFinal:[0.816324,0.544799,0.695255]

YUV:[0.662745,0.025647,0.151941] RGBFinal:[0.835932,0.564406,0.714863]

YUV:[0.690196,0.025647,0.151941] RGBFinal:[0.863383,0.591857,0.742314]

Actualización 13 de julio de 2009

El problema finalmente se solucionó gracias a la recomendación de nschmidt .Resulta que mi archivo YUV en realidad estaba en formato YUV 4:1:1.Al principio me dijeron que estaba en formato YUV NV12.De todos modos, me gustaría compartir mis resultados con ustedes.

Aquí está la salida

alt text

y mi código para decodificar es el siguiente

        float Y = (float) yBuffer [y*YUV_WIDTH + x] ;
        float U = (float) uvBuffer [ ((y / 2) * (YUV_WIDTH  / 2) + (x/2))   ] ; 
        float V = (float) uvBuffer [  ((y / 2) * (YUV_WIDTH  / 2) + (x/2))  + UOffset] ;

        float RFormula = (1.164*(Y-16) + (1.596* (V - 128) ));
        float GFormula = ((1.164 * (Y - 16)) - (0.813 * ((V) - 128)) - (0.391 * ((U) - 128)));
        float BFormula = ((1.164 * (Y - 16)) + (2.018 * ((U) - 128)));


        frameImage [frameImageCounter] = (unsigned char)( (int)[self clampValue:RFormula]);
        frameImageCounter ++;
        frameImage [frameImageCounter] =  (unsigned char)((int)[self clampValue:GFormula]);
        frameImageCounter++;
        frameImage [frameImageCounter] = (unsigned char)((int) [self clampValue:BFormula]);
        frameImageCounter++;



GLuint texture;

glGenTextures(1, &texture);
glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, YUV_WIDTH, YUV_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, frameImage);

//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE_SGIS);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE_SGIS);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

glRotatef( 180.0f, 1.0f, 0.0f, 0.0f );

glBegin( GL_QUADS );
glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
glEnd();

NSLog(@"YUVFrameView: drawRect complete");
glFlush();

Básicamente, utilicé NSData para la extracción de archivos sin formato.almacenado en un buffer de matriz de caracteres.Para la conversión de YUV a RGB, utilicé la fórmula anterior, luego fijé los valores en [0:255].luego utilicé una textura 2D en OpenGL para su visualización.

Entonces, si vas a convertir YUV a RGB en el futuro, usa la fórmula anterior.Si está utilizando la fórmula de conversión de YUV a RGB de la publicación anterior, entonces necesita mostrar la textura en GL_Float ya que los valores para RGB están sujetos entre [0:1]

¿Fue útil?

Solución

A continuación intento :) Creo que eres búfer ultravioleta no está intercalada. Parece que los valores de U vienen primero y luego la matriz de valores V. Cambio de las líneas a

unsigned int voffset = YUV_HEIGHT * YUV_WIDTH / 2;
float U = lookupTableU [uvBuffer [ y * (YUV_WIDTH / 2) + x/2] ]; 
float V = lookupTableV [uvBuffer [ voffset + y * (YUV_WIDTH / 2) + x/2] ];

podría indicar si esto es realmente el caso.

Otros consejos

Creo que estás frente a los valores U y V de forma incorrecta. En lugar de:

float U = lookupTableU [uvBuffer [ ((y / 2) * (x / 2) + (x/2)) * 2  ] ]; 
float V = lookupTableV [uvBuffer [  ((y / 2) * (x / 2) + (x/2)) * 2 + 1] ];

Debe ser algo en la línea de

float U = lookupTableU [uvBuffer [ ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2  ] ]; 
float V = lookupTableV [uvBuffer [  ((y / 2) * (YUV_WIDTH / 2) + (x/2)) * 2 + 1] ];

La imagen parece que tiene el formato 4: 1: 1. Debe cambiar sus líneas a

float U = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 ] ]
float V = lookupTableU [uvBuffer [ (y * (YUV_WIDTH / 4) + (x/4)) * 2 + 1] ]

Tal vez se puede publicar el resultado de ver, lo que los demás están equivocados. Me resulta siempre difícil pensar en ello. Es mucho más fácil de acercarse a esta forma iterativa.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top