2009-07-03 9 views
1

On m'a assigné la tâche d'écrire un programme qui prend un exemple de fichier YUV brut et l'affiche dans un programme Cocoa OpenGL.Comment afficher un cadre YUV brut dans un programme Cocoa OpenGL

Je suis stagiaire dans mon travail et j'ai peu ou pas d'idée sur la façon de commencer. J'ai lu wikipedia & articles sur YUV, mais je ne pouvais pas trouver de bon code source sur la façon d'ouvrir un fichier YUV brut, extraire les données et le convertir en RVB et l'afficher dans la fenêtre d'affichage.

Essentiellement, je besoin d'aide pour les aspects suivants de la tâche -Comment pour extraire les données YUV de l'exemple de fichier YUV -Comment pour convertir les données YUV dans l'espace couleur RVB -Comment afficher l'espace couleur RVB en OpenGL. (Celui-ci je pense que je peux comprendre avec le temps, mais j'ai vraiment besoin d'aide avec les deux premiers points)

s'il vous plaît soit me dire les classes à utiliser, ou me diriger vers des endroits où je peux en apprendre davantage sur YUV graphique/vidéo afficher

Répondre

1

Cette réponse est incorrecte, voir les autres réponses et commentaires. Réponse originale ci-dessous pour la postérité.


Vous ne pouvez pas afficher directement. Vous devrez le convertir en une texture RVB. Comme vous l'avez peut-être compris de Wikipedia, il y a un tas de variations sur l'espace colorimétrique YUV. Assurez-vous que vous utilisez le bon.

Pour chaque pixel, la conversion de YUV à RGB est une simple transformation linéaire. Vous faites juste la même chose à chaque pixel indépendamment.

Une fois que vous avez converti l'image en RVB, vous pouvez l'afficher en créant une texture. Vous devez appeler glGenTextures() pour allouer un handle de texture, glBindTexture() pour lier la texture au contexte de rendu, et glTexImage2D() pour télécharger les données de texture vers le GPU. Pour le rendre, vous appelez à nouveau glBindTexture(), suivi du rendu d'un quad avec des coordonnées de texture correctement configurées.

// parameters: image: pointer to raw YUV input data 
//    width: image width (must be a power of 2) 
//    height: image height (must be a power of 2) 
// returns: a handle to the resulting RGB texture 
GLuint makeTextureFromYUV(const float *image, int width, int height) 
{ 
    float *rgbImage = (float *)malloc(width * height * 3 * sizeof(float)); // check for NULL 
    float *rgbImagePtr = rgbImage; 

    // convert from YUV to RGB (floats used here for simplicity; it's a little 
    // trickier with 8-bit ints) 
    int y, x; 
    for(y = 0; y < height; y++) 
    { 
     for(x = 0; x < width; x++) 
     { 
      float Y = *image++; 
      float U = *image++; 
      float V = *image++; 
      *rgbImagePtr++ = Y    + 1.13983f * V; // R 
      *rgbImagePtr++ = Y - 0.39465f * U - 0.58060f * V; // G 
      *rgbImagePtr++ = Y + 2.03211f * U;     // B 
     } 
    } 

    // create texture 
    GLuint texture; 
    glGenTextures(1, &texture); 

    // bind texture to render context 
    glBindTexture(GL_TEXTURE_2D, texture); 

    // upload texture data 
    glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB, GL_FLOAT, rgbImage); 

    // don't use mipmapping (since we're not creating any mipmaps); the default 
    // minification filter uses mipmapping. Use linear filtering for minification 
    // and magnification. 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

    // free data (it's now been copied onto the GPU) and return texture handle 
    free(rgbImage); 
    return texture; 
} 

Pour rendre:

glBindTexture(GL_TEXTURE_2D, texture); 

glBegin(GL_QUADS); 
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); 
    glTexCoord2f(1.0f, 0.0f); glVertex3f(64.0f, 0.0f, 0.0f); 
    glTexCoord2f(1.0f, 1.0f); glVertex3f(64.0f, 64.0f, 0.0f); 
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 64.0f, 0.0f); 
glEnd(); 

Et ne pas oublier d'appeler glEnable(GL_TEXTURE_2D) à un moment donné pendant l'initialisation, et appelez glDeleteTextures(1, &texture) lors de l'arrêt.

+0

Merci beaucoup pour l'information Adam, je l'apprécie vraiment. Maintenant pouvez-vous me dire comment extraire le fichier de données brutes à partir du disque dur? Je suis familier avec NSOpenPanel, je peux l'utiliser pour extraire le chemin du fichier de données, cependant, comment puis-je utiliser un chemin de fichier et charger le fichier YUV dans l'application? – ReachConnection

+0

En supposant que le fichier ne contient que des pixels, vous devez simplement utiliser NSData ou NSFileHandle et tout lire. Si le fichier contient des métadonnées telles que des informations de taille, vous devrez interpréter cela en utilisant les opérateurs de pointeur et/ou de structure C standard. –

+0

Cette réponse est incorrecte. Depuis MacOS 10.2 et versions ultérieures, chaque système Apple prend en charge les extensions OpenGL, qui peuvent charger et afficher directement les données YUV. Recherchez "GL_YCBCR_422_APPLE" pour plus de détails. Que les données soient converties quelque part (par le pilote dans le logiciel, sur le GPU dans le matériel, etc.) ne vous concerne pas lorsque vous utilisez cette extension (et quand le GPU la convertit, croyez-moi, il peut battre votre code ci-dessus d'au moins 1000%) – Mecki

6

Le commentaire d'Adam Rosenfield est incorrect. Sur les Mac, vous pouvez afficher les textures YCbCr (l'équivalent numérique de YUV) en utilisant le format de texture GL_YCBCR_422_APPLE, comme spécifié dans l'extension APPLE_ycbcr_422.

8

Je l'ai fait avec des trames YUV capturées à partir d'une caméra CCD. Malheureusement, il existe plusieurs formats YUV différents. Je crois que celui qu'Apple utilise pour le format de texture GL_YCBCR_422_APPLE est techniquement 2VUY422.Pour convertir une image à partir d'un cadre YUV422 généré par une caméra IIDC Firewire à 2VUY422, je l'ai utilisé les éléments suivants:

void yuv422_2vuy422(const unsigned char *theYUVFrame, unsigned char *the422Frame, const unsigned int width, const unsigned int height) 
{ 
    int i =0, j=0; 
    unsigned int numPixels = width * height; 
    unsigned int totalNumberOfPasses = numPixels * 2; 
    register unsigned int y0, y1, y2, y3, u0, u2, v0, v2; 

    while (i < (totalNumberOfPasses)) 
    { 
     u0 = theYUVFrame[i++]-128; 
     y0 = theYUVFrame[i++]; 
     v0 = theYUVFrame[i++]-128; 
     y1 = theYUVFrame[i++]; 
     u2 = theYUVFrame[i++]-128; 
     y2 = theYUVFrame[i++]; 
     v2 = theYUVFrame[i++]-128; 
     y3 = theYUVFrame[i++]; 

     // U0 Y0 V0 Y1 U2 Y2 V2 Y3 

     // Remap the values to 2VUY (YUYS?) (Y422) colorspace for OpenGL 
     // Y0 U Y1 V Y2 U Y3 V 

     // IIDC cameras are full-range y=[0..255], u,v=[-127..+127], where display is "video range" (y=[16..240], u,v=[16..236]) 

     the422Frame[j++] = ((y0 * 240)/255 + 16); 
     the422Frame[j++] = ((u0 * 236)/255 + 128); 
     the422Frame[j++] = ((y1 * 240)/255 + 16); 
     the422Frame[j++] = ((v0 * 236)/255 + 128); 
     the422Frame[j++] = ((y2 * 240)/255 + 16); 
     the422Frame[j++] = ((u2 * 236)/255 + 128); 
     the422Frame[j++] = ((y3 * 240)/255 + 16); 
     the422Frame[j++] = ((v2 * 236)/255 + 128); 
    } 
} 

Pour l'affichage efficace d'une source vidéo YUV, vous pouvez utiliser Apple's client storage extension, que vous pouvez mettre en place en utilisant quelque chose comme ce qui suit:

glEnable(GL_TEXTURE_RECTANGLE_EXT); 
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1); 

glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, videoImageWidth * videoImageHeight * 2, videoTexture); 
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_SHARED_APPLE); 
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); 

glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 

glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, videoImageWidth, videoImageHeight, 0, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, videoTexture);  

Cela vous permet de modifier rapidement les données stockées dans votre texture vidéo côté client avant chaque image à afficher sur l'écran.

Pour dessiner, vous pouvez ensuite utiliser le code comme suit:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   
glEnable(GL_TEXTURE_2D); 

glViewport(0, 0, [self frame].size.width, [self frame].size.height); 

glMatrixMode(GL_PROJECTION); 
glLoadIdentity(); 
NSRect bounds = NSRectFromCGRect([self bounds]); 
glOrtho((GLfloat)NSMinX(bounds), (GLfloat)NSMaxX(bounds), (GLfloat)NSMinY(bounds), (GLfloat)NSMaxY(bounds), -1.0, 1.0); 

glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1); 
glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, videoImageWidth, videoImageHeight, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, videoTexture); 

glMatrixMode(GL_TEXTURE); 
glLoadIdentity(); 

glBegin(GL_QUADS); 
    glTexCoord2f(0.0f, 0.0f); 
    glVertex2f(0.0f, videoImageHeight); 

    glTexCoord2f(0.0f, videoImageHeight); 
    glVertex2f(0.0f, 0.0f); 

    glTexCoord2f(videoImageWidth, videoImageHeight); 
    glVertex2f(videoImageWidth, 0.0f); 

    glTexCoord2f(videoImageWidth, 0.0f); 
    glVertex2f(videoImageWidth, videoImageHeight);  
glEnd(); 
+0

merci beaucoup brad – ReachConnection