2009-07-03 7 views
1

Ich wurde mit der Aufgabe beauftragt, ein Programm zu schreiben, das eine rohe YUV-Beispieldatei aufnimmt und sie in einem Cocoa OpenGL-Programm anzeigt.Wie man einen rohen YUV-Rahmen in einem Cocoa OpenGL-Programm anzeigt

Ich bin ein Praktikant in meinem Job und ich habe wenig oder keine Ahnung, wie ich anfangen soll. Ich habe wikipedia & Artikel auf YUV gelesen, aber ich konnte keinen guten Quellcode finden, wie man eine rohe YUV-Datei öffnet, die Daten extrahiert und sie in RGB umwandelt und es im Ansichtsfenster anzeigt.

Im Grunde muß ich mit den folgenden Aspekten der Aufgabe helfen -wie die YUV-Daten aus der Probe YUV-Datei zu extrahieren -wie die YUV-Daten in RGB-Farbraum konvertieren -wie den RGB-Farbraum anzuzeigen in OpenGL. (Ich denke, ich kann mit der Zeit herausfinden, aber ich brauche wirklich Hilfe mit den ersten beiden Punkten)

Bitte entweder mir die Klassen zu verwenden, oder zeigen Sie mir Orte, an denen ich über YUV Grafik/Video lernen kann Anzeige

Antwort

1

Diese Antwort ist nicht korrekt, siehe die anderen Antworten und Kommentare. Original-Antwort links unten für die Nachwelt.


Sie können es nicht direkt angezeigt werden soll. Sie müssen es in eine RGB-Textur konvertieren. Wie Sie vielleicht aus Wikipedia gesammelt haben, gibt es eine Reihe von Variationen über den YUV-Farbraum. Stellen Sie sicher, dass Sie die richtige verwenden.

Für jedes Pixel ist die Konvertierung von YUV in RGB eine einfache lineare Transformation. Sie machen das gleiche für jedes Pixel unabhängig.

Sobald Sie das Bild in RGB konvertiert haben, können Sie es anzeigen, indem Sie eine Textur erstellen. Sie müssen glGenTextures() aufrufen, um ein Textur-Handle zuzuweisen, glBindTexture(), um die Textur an den Render-Kontext zu binden, und glTexImage2D(), um die Texturdaten auf die GPU hochzuladen. Um es zu rendern, rufen Sie erneut glBindTexture() auf, gefolgt vom Rendern eines Quads mit korrekt eingerichteten Texturkoordinaten.

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

zu machen:

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

Und vergessen Sie nicht glEnable(GL_TEXTURE_2D) an einem gewissen Punkt während der Initialisierung zu rufen, und glDeleteTextures(1, &texture) beim Herunterfahren aufrufen.

+0

Vielen Dank für die Informationen Adam ich wirklich zu schätzen. Können Sie mir jetzt sagen, wie Sie die Rohdatendatei von der Festplatte extrahieren? Ich bin vertraut mit NSOpenPanel, ich kann das verwenden, um den Datendateipfad zu extrahieren, wie verwende ich jedoch einen Dateipfad und lade die YUV-Datei in die Anwendung ein? – ReachConnection

+0

Angenommen, die Datei enthält nur Pixel, dann würden Sie einfach NSData oder NSFileHandle verwenden und alles lesen. Wenn die Datei Metadaten wie Größeninformationen enthält, müssen Sie diese mit den Standard-C-Pointer- und/oder Strukturoperatoren interpretieren. –

+0

Diese Antwort ist falsch. Ab MacOS 10.2 unterstützt jedes Apple-System eine OpenGL-Erweiterung, die YUV-Daten direkt laden und anzeigen kann. Suchen Sie nach "GL_YCBCR_422_APPLE" für Details. Ob die Daten irgendwo konvertiert werden (vom Treiber in Software, auf der GPU in Hardware, etc.) geht Sie nichts an, wenn Sie diese Erweiterung benutzen (und wenn die GPU es konvertiert, glauben Sie mir, es kann Ihr Codeprobe von oben um mindestens 1000%) – Mecki

6

Adam Rosenfields Kommentar ist falsch. Auf Macs können Sie YCbCr-Texturen (das digitale Äquivalent zu YUV) mit dem Texturformat GL_YCBCR_422_APPLE anzeigen, wie in der Erweiterung APPLE_ycbcr_422 angegeben.

8

Ich habe dies mit YUV Frames von einer CCD-Kamera aufgenommen. Leider gibt es eine Reihe verschiedener YUV-Formate. Ich glaube, dasjenige, das Apple für das GL_YCBCR_422_APPLE Texturformat verwendet, ist technisch 2VUY422.Um ein Bild von einem YUV422 Rahmen zu konvertieren, erzeugt durch eine IIDC Firewire Kamera 2VUY422, habe ich folgende Zwecke verwendet:

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

Für eine effiziente Anzeige einer YUV-Videoquelle, können Sie möchten Apple's client storage extension verwenden, die können Sie die im Anschluss an so etwas wie die Verwendung von:

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

auf diese Weise können Sie schnell die Daten vor jedem Frame in Ihrer clientseitigen Videotextur gespeichert ändern heraus auf dem Bildschirm angezeigt werden. Um

zu ziehen, könnten Sie dann verwenden, um Code wie folgt aus:

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

danke viel brad – ReachConnection