Получите RGB CVPixelBuffer от ARKit

Я пытаюсь получить CVPixelBuffer в цветовом пространстве RGB из ARKit от Apple. В func session(_ session: ARSession, didUpdate frame: ARFrame) методе ARSessionDelegate я получаю экземпляр ARFrame. На странице Отображение опыта AR с Metal я обнаружил, что этот буфер пикселей находится в формате YCbCr (YUV ) цветовое пространство.

Мне нужно преобразовать это в цветовое пространство RGB (мне действительно нужно CVPixelBuffer, а не UIImage). Я нашел кое-что о преобразовании цвета в iOS, но мне не удалось заставить это работать в Swift 3.


person tomas789    schedule 07.06.2017    source источник
comment
В каком варианте использования вам понадобится RGB поверх YUV?   -  person Guig    schedule 13.06.2017
comment
У меня есть специальный конвейер обработки, для которого требуется RGB   -  person tomas789    schedule 14.06.2017
comment
В порядке. Если в вашем конвейере используется какой-то opengl / Metal, там легко выполнить преобразование. Я не уверен, что есть хороший способ напрямую получить RGB.   -  person Guig    schedule 14.06.2017


Ответы (5)


Есть несколько способов сделать это, в зависимости от того, что вам нужно. Лучший способ сделать это в реальном времени (например, визуализировать буфер в представление) - использовать собственный шейдер для преобразования YCbCr CVPixelBuffer в RGB.

Использование Metal: если вы создаете новый проект, выберите «Приложение дополненной реальности» и выберите «Металл» для технологии контента, созданный проект будет содержать код и шейдеры, необходимые для этого преобразования.

Использование OpenGL: пример GLCameraRipple от Apple использует AVCaptureSession для захвата камеры и показывает, как сопоставить полученный CVPixelBuffer с текстурами GL, которые затем преобразуются в RGB в шейдерах (опять же, как показано в примере).

Не в реальном времени: ответ на вопрос о переполнении стека. обращается к преобразованию буфера в UIImage и предлагает довольно простой способ сделать это.

person joshue    schedule 11.06.2017

Я тоже несколько дней зацикливаюсь на этом вопросе. Все фрагменты кода, которые я смог найти в Интернете, написаны на Objective-C, а не на Swift, что касается преобразования CVPixelBuffer в UIImage.

Наконец, следующий фрагмент кода идеально подходит для меня, чтобы преобразовать изображение YUV в формат файла JPG или PNG, а затем вы можете записать его в локальный файл в своем приложении.

func pixelBufferToUIImage(pixelBuffer: CVPixelBuffer) -> UIImage {
    let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
    let context = CIContext(options: nil)
    let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
    let uiImage = UIImage(cgImage: cgImage!)
    return uiImage
}
person SteveGuanqi    schedule 27.06.2017
comment
Это действительно не отвечает на вопрос. - person tomas789; 28.06.2017
comment
Это лучший ответ, поскольку он правильно обрабатывает цветовое пространство и гамма-преобразование, но имейте в виду, что выделение такого CIContext при каждом вызове будет ДЕЙСТВИТЕЛЬНО медленным. Удерживайте ссылку на CIContext на время существования повторяющихся вызовов, и ваше приложение будет работать эффективно. Если вы заинтересованы в полной металлической имплементации преобразования RGB- ›YCbCr, взгляните на: github.com/mdejong / MetalBT709Decoder - person MoDJ; 11.01.2019
comment
ARKit с SceneKit предоставляет snapshot метод для прямого UIImage получения текущего кадра. - person Juan Boero; 21.06.2019

В документации прямо говорится, что вам нужен доступ к плоскостям яркости и цветности. :

ARKit захватывает пиксельные буферы в плоском формате YCbCr (также известном как YUV). Чтобы отобразить эти изображения на дисплее устройства, вам потребуется доступ к плоскостям яркости и цветности буфера пикселей и преобразование значений пикселей в формат RGB.

Таким образом, нет возможности напрямую получить плоскости RGB, и вам придется обрабатывать это в своих шейдерах, либо в Metal, либо в openGL, как описано @joshue

person Guig    schedule 16.06.2017

Возможно, вам понадобятся функции преобразования изображений платформы Accelerate. Возможно сочетание vImageConvert_420Yp8_Cb8_Cr8ToARGB8888 и vImageConvert_ARGB8888toRGB888 (если вам не нужен альфа-канал). По моему опыту, они работают в режиме реального времени.

person rob    schedule 12.08.2019

Я долго боролся с этим, и в итоге я написал следующий код, который у меня работает:

// Helper macro to ensure pixel values are bounded between 0 and 255
#define clamp(a) (a > 255 ? 255 : (a < 0 ? 0 : a));

- (void)processImageBuffer:(CVImageBufferRef)imageBuffer
{
    OSType type  = CVPixelBufferGetPixelFormatType(imageBuffer);
    if (type == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
    {
        CVPixelBufferLockBaseAddress(imageBuffer, 0);
        // We know the return format of the base address based on the YpCbCr8BiPlanarFullRange format (as per doc)
        StandardBuffer baseAddress = (StandardBuffer)CVPixelBufferGetBaseAddress(imageBuffer);

        // Get the number of bytes per row for the pixel buffer, width and height
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
        size_t width = CVPixelBufferGetWidth(imageBuffer);
        size_t height = CVPixelBufferGetHeight(imageBuffer);

        // Get buffer info and planar pixel data
        CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;
        uint8_t* cbrBuff = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1);
        // This just moved the pointer past the offset
        baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
        int bytesPerPixel = 4;
        uint8_t *rgbData =  rgbFromYCrCbBiPlanarFullRangeBuffer(baseAddress,
                                                                cbrBuff,
                                                                bufferInfo,
                                                                width,
                                                                height,
                                                                bytesPerRow);

        [self doStuffOnRGBBuffer:rgbData width:width height:height bitsPerComponent:8 bytesPerPixel:bytesPerPixel bytesPerRow:bytesPerRow];

        free(rgbData);
        CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
    }
    else
    {
        NSLog(@"Unsupported image buffer type");
    }
}

uint8_t * rgbFromYCrCbBiPlanarFullRangeBuffer(uint8_t *inBaseAddress,
                                              uint8_t *cbCrBuffer,
                                              CVPlanarPixelBufferInfo_YCbCrBiPlanar * inBufferInfo,
                                              size_t inputBufferWidth,
                                              size_t inputBufferHeight,
                                              size_t inputBufferBytesPerRow)
{
    int bytesPerPixel = 4;
    NSUInteger yPitch = EndianU32_BtoN(inBufferInfo->componentInfoY.rowBytes);
    uint8_t *rgbBuffer = (uint8_t *)malloc(inputBufferWidth * inputBufferHeight * bytesPerPixel);
    NSUInteger cbCrPitch = EndianU32_BtoN(inBufferInfo->componentInfoCbCr.rowBytes);
    uint8_t *yBuffer = (uint8_t *)inBaseAddress;

    for(int y = 0; y < inputBufferHeight; y++)
    {
        uint8_t *rgbBufferLine = &rgbBuffer[y * inputBufferWidth * bytesPerPixel];
        uint8_t *yBufferLine = &yBuffer[y * yPitch];
        uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch];
        for(int x = 0; x < inputBufferWidth; x++)
        {
            int16_t y = yBufferLine[x];
            int16_t cb = cbCrBufferLine[x & ~1] - 128;
            int16_t cr = cbCrBufferLine[x | 1] - 128;

            uint8_t *rgbOutput = &rgbBufferLine[x*bytesPerPixel];

            int16_t r = (int16_t)roundf( y + cr *  1.4 );
            int16_t g = (int16_t)roundf( y + cb * -0.343 + cr * -0.711 );
            int16_t b = (int16_t)roundf( y + cb *  1.765);

            // ABGR image representation
            rgbOutput[0] = 0Xff;
            rgbOutput[1] = clamp(b);
            rgbOutput[2] = clamp(g);
            rgbOutput[3] = clamp(r);
        }
    }

    return rgbBuffer;
}
person Vlad    schedule 21.05.2018
comment
Есть ли способ использовать этот код в приложении SWIFT? - person JCutting8; 08.06.2020