Вопрос по opengl, macos, cocoa – Как отобразить необработанный кадр YUV в программе Cocoa OpenGL

1

Мне было поручено написать программу, которая берет образец исходного файла YUV и отображает его в программе Cocoa OpenGL.

Я стажер на своей работе, и я не знаю, как начать. Я читаю Википедию & amp; статьи о YUV, но я не смог найти хорошего исходного кода о том, как открыть необработанный файл YUV, извлечь данные, преобразовать их в RGB и отобразить в окне просмотра.

По сути, мне нужна помощь со следующими аспектами задачи - как извлечь данные YUV из примера файла YUV -Как преобразовать данные YUV в цветовое пространство RGB -Как отображать цветовое пространство RGB в OpenGL. (Я думаю, что со временем это можно понять, но мне действительно нужна помощь по первым двум пунктам)

пожалуйста, скажите мне, какие классы использовать, или укажите места, где я могу узнать о графическом / видео дисплее YUV

Ваш Ответ

3   ответа
8

снятыми с камеры CCD. К сожалению, существует ряд различных форматов YUV. Я считаю, что Apple использует дляGL_YCBCR_422_APPLEормат @ текстур технически 2VUY422. Чтобы преобразовать изображение из кадра YUV422, сгенерированного камерой IIDC Firewire, в 2VUY422, я использовал следующее:

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

Для эффективного отображения видео источника YUV, вы можете использовать Расширение клиентского хранилища Apple, который вы можете настроить, используя что-то вроде следующего:

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

Это позволяет быстро изменять данные, хранящиеся в вашей видео-текстуре на стороне клиента, перед отображением каждого кадра на экране.

Для рисования вы можете использовать код, подобный следующему:

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();
Большое спасибо, Брэд ReachConnection
1

Этот ответ не верный, см. Другие ответы и комментарии. Оригинальный ответ оставлен ниже для потомков.

Вы не можете отобразить это напрямую. Вам нужно будет преобразовать его в RGB-текстуру. Как вы, возможно, собрались из Wikipedia, существует множество вариаций цветового пространства YUV. Убедитесь, что вы используете правильный.

Для каждого пикселя преобразование из YUV в RGB является простым линейным преобразованием. Вы просто делаете то же самое для каждого пикселя независимо друг от друга.

Как только вы конвертируете изображение в RGB, вы можете отобразить его, создав текстуру. Вам нужно позвонитьglGenTextures() для выделения дескриптора текстуры,glBindTexture() для привязки текстуры к контексту рендеринга иglTexImage2D() для загрузки данных текстуры в графический процессор. Чтобы сделать это, вы снова звонитеglBindTexture() с последующим рендерингом квадрата с правильно заданными координатами текстуры.

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

Чтобы сделать:

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

И не забудь позвонитьglEnable(GL_TEXTURE_2D) в какой-то момент во время инициализации и позвоните,glDeleteTextures(1, &texture) во время выключения.

Большое спасибо за информацию, Адам, я действительно ценю это. Теперь вы можете сказать мне, как извлечь файл с необработанными данными с жесткого диска? Я знаком с NSOpenPanel, я могу использовать его для извлечения пути к файлу данных, однако, как мне использовать путь к файлу и загрузить файл YUV в приложение? ReachConnection
Предполагая, что файл содержит только пиксели, вы просто используете NSData или NSFileHandle и читаете все. Если файл содержит метаданные, такие как информация о размере, вам придется интерпретировать это с помощью стандартных указателей C и / или структурных операторов. Peter Hosey
Этот ответ неверен. Начиная с MacOS 10.2 и более поздних версий, каждая система Apple поддерживает расширения OpenGL, которые могут напрямую загружать и отображать данные YUV. Ищите «GL_YCBCR_422_APPLE» для деталей. Независимо от того, преобразуются ли данные где-либо (драйвером в программном обеспечении, графическим процессором в аппаратном обеспечении и т. Д.), Это не ваше дело при использовании этого расширения (и когда графический процессор конвертирует его, поверьте мне, оно может превзойти ваш пример кода выше как минимум на 1000%) Mecki
6

уры YCbCr (цифровой эквивалент YUV), используяGL_YCBCR_422_APPLE формат текстуры, как указано в APPLE_ycbcr_422 расширение.

Похожие вопросы