2009-07-09 15 views
11

J'ai des problèmes pour afficher un fichier YUV brut au format NV12.Besoin d'aide avec l'affichage YUV dans OpenGl

Je peux afficher un cadre sélectionné, mais il est toujours principalement en noir et blanc avec certaines nuances de rose et de vert.

Voici comment ma sortie ressemble alt text http://i32.tinypic.com/hv0vih.png

Quoi qu'il en soit, voici comment fonctionne mon programme. (Ceci est fait dans cacao/objectif-c, mais j'ai besoin de votre avis sur l'algorithme du programme, pas sur la syntaxe.)

Avant l'exécution du programme, le fichier YUV est stocké dans un fichier binaire nommé "test.yuv" . Le fichier est au format NV12, ce qui signifie que le plan Y est stocké en premier, puis le plan UV est entrelacé. Mon extraction de fichier n'a pas de problème car j'ai fait beaucoup de tests dessus.

Au cours de l'initiation, je crée une table de consultation qui convertira binaires/8 piqûres/un octet/caractères en respecté Y, U, valeurs à virgule flottante V

Pour le plan Y ceci est mon code

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

le U plan table de recherche

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

Et la table V de recherche

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

après ce point, j'extrait le plan UV Y & et de les stocker dans deux tampons, yBuffer & uvBuffer

à ce stade, je tente de convertir les données YUV et emmagasinés dans un réseau de tampon RVB appelé " 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]; 

     } 
    } 

} 

alors j'ai essayé de dessiner l'image dans 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(); 
} 

donc en gros ce mon programme dans un coquille de noix. essentiellement je lis les fichiers YUV binaires, stocke toutes les données dans un tampon de tableau char. Je traduis ensuite ces valeurs en leurs valeurs flottantes YUV respectées.

C'est là que je pense que l'erreur pourrait être: dans la table de consultation Y, je standardise le plan Y à [0,1], dans le plan U j'ai normalisé les valeurs entre [-0,435,0,436], et dans le Le plan V I l'a standardisé [-0.615,0.615]. Je l'ai fait parce que ce sont les plages de valeurs YUV selon wikipedia.

Et la formule YUV à RGB est la même formule de Wikipedia. J'ai aussi essayé plusieurs autres formules, et c'est la seule formule qui donne la perspective grossière du cadre. N'importe qui peut tenter de deviner pourquoi mon programme n'affiche pas correctement les données de trame YUV. Je pense que c'est quelque chose à voir avec ma technique de normalisation, mais cela me semble aller.

J'ai fait beaucoup de tests, et je suis sûr à 100% que l'erreur est causée par une table de consultation. Je ne pense pas que mes formules de réglage sont correctes.

Une note à tous ceux qui sont en train de lire this.Pendant la plus longue période, mon image ne s'affichait pas correctement parce que ne pouvait pas extraire correctement les données d'image .

Quand j'ai commencé à programmer, j'étais l'impression que dans un clip de dire 30 images, toutes les 30 données planes Y sont d'abord écrites dans le fichier de données, suivi ensuite par données d'avion UV.

Je découvert par essais et erreur a été que, pour un fichier de données YUV, spécifiquement NV12, le fichier de données est stockée de la façon suivante

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

@nschmidt

J'ai changé mon code à ce que vous suggérez

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

et ceci est le résultat que je reçois alt text http://i26.tinypic.com/kdtilg.png

est ici la ligne d'impression à partir de la console. je suis imprimer les valeurs de Y, U, V et valeur RGB qui sont traduits et stockés sur de la matrice frameImage

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]

mise à jour 13 Juillet, 2009

le problème a finalement été résolu grâce à la recommandation de nschmidt. Il s'avère que mon fichier YUV était en format YUV 4: 1: 1. On m'a d'abord dit que c'était au format YUV NV12. De toute façon, j'aimerais partager mes résultats avec vous.

Voici la sortie

alt text http://i32.tinypic.com/35b9xf8.png

et mon code pour le décodage est comme suit

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

essentiellement, j'ai utilisé NSData pour l'extraction de fichier brut. stocké dans un tampon tableau de char. Pour la conversion de YUV à RVB, j'ai utilisé la formule ci-dessus, après, j'ai serré les valeurs à [0: 255]. J'ai ensuite utilisé une 2DTexture dans OpenGL pour l'affichage.

Donc, si vous convertissez YUV en RGB à l'avenir, utilisez la formule ci-dessus. Si vous utilisez la formule de conversion YUV vers RVB de la publication précédente, vous devez afficher la texture dans GL_Float à partir des valeurs pour RVB entre [0: 1]

Répondre

8

Ensuite, essayez :) Je pense que vous êtes tampon n'est pas entrelacé. Il semble que les valeurs U viennent en premier, puis le tableau des valeurs V. Modification des lignes à

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] ]; 

peut indiquer si c'est vraiment le cas.

+0

wow, j'ai testé mon programme avec votre ajustement et cela a finalement fonctionné. merci beaucoup man.it s'avère que vous aviez raison, l'avion U & V ne sont pas entrelacées comme on m'a dit à l'origine. merci beaucoup nschmidt. Je vous note – ReachConnection

+0

sans problème :). pour le dossier la première estimation au sujet du format étant 4: 1: 1 était réellement incorrecte. Vous avez le format 4: 2: 2. Le vrai problème était que vos données n'étaient pas entrelacées. – nschmidt

+0

@ReachConnection: Pouvez-vous me dire ce que [self clampValue: RFormula] cette fonction est en train de faire? J'ai l'intention d'utiliser votre code pour obtenir UIImage de YUV422. –

3

Je pense que vous adressez les valeurs U et V incorrectement. Plutôt que:

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

Il devrait être quelque chose le long des lignes 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] ]; 
+0

merci, j'ai raté cette erreur. Cependant, j'ai fait les changements et le cadre affiché est toujours principalement en noir et blanc, avec des nuances roses et vertes ici et là. – ReachConnection

+0

Je viens d'ajouter une image pour montrer comment ma sortie ressemble à – ReachConnection

0

L'image semble que vous avez le format 4: 1: 1. Vous devriez changer vos lignes à

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

Peut-être que vous pouvez afficher le résultat de voir, quoi d'autre a tort. Je trouve toujours difficile d'y penser. Il est beaucoup plus facile d'aborder cela de façon itérative.