Очистить CVPixelBufferRef с помощью графического процессора

Я пишу код обработки видео, используя AVComposition. Предоставляя только необходимые справочные данные, я получаю CVPixelBuffer от API Apple, который я не контролирую. Этот буфер CVPixel содержит ранее обработанный видеокадр, поскольку они, по-видимому, перерабатываются этим Apple API, который я не контролирую. Итак, моя цель — установить для всех пикселей в CVPixelBufferRef значение [0, 0, 0, 0] (в цветовом пространстве RGBA). Я могу сделать это на процессоре с помощью этой функции:

- (void)processPixelBuffer: (CVImageBufferRef)pixelBuffer
{
    CVPixelBufferLockBaseAddress( pixelBuffer, 0 );

    int bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
    int bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
    unsigned char *pixel = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);

    for( int row = 0; row < bufferHeight; row++ ) {
        for( int column = 0; column < bufferWidth; column++ ) {
            pixel[0] = 0;
            pixel[1] = 0;
            pixel[2] = 0;
            pixel[3] = 0;
            pixel += 4;
        }
    }
    CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
}

Есть ли способ сделать то же самое с помощью графического процессора? Кроме того, возможно ли это сделать через CoreImage? поскольку я не знаю openGL, и его настройка кажется довольно сложной.


person wfbarksdale    schedule 17.11.2014    source источник


Ответы (4)


Предположим, что ваш пиксельный буфер подключен к буферу изображения, поддерживаемому поверхностью ввода-вывода вы можете использовать CVOpenGLESTextureCacheCreateTextureFromImage, чтобы получить CVOpenGLESTextureCache, чтобы предоставить вам CVOpenGLESTextureRef. Это позволит продать цель текстуры и имя, чтобы вы могли связать вещь в OpenGL.

В OpenGL вы можете использовать объект фреймбуфера для рендеринга в текстуру. Сделав это, вы можете использовать glClear, чтобы очистить это. Вызовите glFinish, чтобы создать точку синхронизации с конвейером GL, и когда вы в следующий раз проверите ее из ЦП, ваша память должна быть очищена.

person Tommy    schedule 17.11.2014
comment
Удалось добиться этого с помощью примера кода Apple. Однако это было гораздо сложнее, так как мне пришлось выполнить кучу настроек для openGL, чего я, честно говоря, не понимаю. - person wfbarksdale; 17.11.2014
comment
Вот, пожалуй, лучший вопрос: действительно ли это даст какое-либо улучшение производительности по сравнению с использованием memset для обнуления пиксельного буфера? - person wfbarksdale; 17.11.2014
comment
Мое чутье подсказывает, что вам лучше всего надеяться на распараллеливание — выполните glClear для буфера B, затем заставьте процессор работать с буфером A, а затем вызовите glFinish только тогда, когда процессор захочет перейти к работе с буфером B. Но если вы еще не многопоточное, то выполнение того же самого с помощью GCD, вероятно, будет столь же эффективным. Графический процессор не просто изначально способен работать более эффективно memset — вы будете ограничены пропускной способностью памяти. - person Tommy; 17.11.2014

Вы можете использовать Accelerate vImageBufferFill, чтобы сделать именно это (но не GPU) . Это заполняет BGRA черным цветом в Swift 4:

let width: Int = 3
let height: Int = 3

var pixelBuffer: CVPixelBuffer?
var imageBuffer: vImage_Buffer
var date: Date
let iterations: Int = 10000

let pixelBufferAttributes: [CFString: Any] = [kCVPixelBufferCGImageCompatibilityKey: true, kCVPixelBufferCGBitmapContextCompatibilityKey: true]
if CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, pixelBufferAttributes as CFDictionary, &pixelBuffer) != kCVReturnSuccess || pixelBuffer == nil { fatalError("Cannot create pixel buffer.") }
if CVPixelBufferLockBaseAddress(pixelBuffer!, []) != kCVReturnSuccess { fatalError("Cannot lock pixel buffer base address.") }
imageBuffer = vImage_Buffer(data: CVPixelBufferGetBaseAddress(pixelBuffer!), height: UInt(height), width: UInt(width), rowBytes: CVPixelBufferGetBytesPerRow(pixelBuffer!))
vImageBufferFill_ARGB8888(&imageBuffer, [0, 0, 0, 0xFF], UInt32(kvImageNoFlags))
if CVPixelBufferUnlockBaseAddress(pixelBuffer!, []) != kCVReturnSuccess { fatalError("Cannot unlock pixel buffer base address.") }

Тестирование простого memset для заполнения небольшого буфера нулями за 10000 итераций на игровой площадке происходит намного быстрее. В релизной сборке с реальными данными результаты, вероятно, не будут сильно отличаться.

  • ускорить: 1629,0
  • memset: 314,0

введите здесь описание изображения

person Ian Bytchek    schedule 21.05.2018

У меня такая же проблема (заставляет переработанный буфер, предоставленный -[AVVideoCompositionRenderContext newPixelBuffer], быть черным).

Что, если вы используете Core Image для выполнения тяжелой работы? Что-то вроде этого:

EAGLContext *eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
CIContext *ciContext = [CIContext contextWithEAGLContext:eaglContext];

CIImage *image = [CIImage imageWithColor:[CIColor colorWithRed:0 green:0 blue:0]];
CVPixelBufferRef destination = [request.renderContext newPixelBuffer];
[ciContext render:image toCVPixelBuffer:destination];
[request finishWithComposedVideoFrame:destination];
CVBufferRelease(destination);

Вам нужно будет протестировать производительность, чтобы узнать, лучше ли это, чем memset, но это лучший подход за мои деньги, чем попытка заглянуть в API OpenGL только для установки нескольких битов.

person bcattle    schedule 07.01.2016

С этой страницы похоже, что вы можете получить прямой доступ к CVImageBufferRef как OpenGL Текстура через:

glBindTexture( CVOpenGLTextureGetTarget( image ), CVOpenGLTextureGetName( image ) );

Получив текстуру, вы можете использовать ее в качестве буфера отрисовки для FBO и просто вызвать для нее glClear(GL_COLOR_BUFFER_BIT);.

person user1118321    schedule 17.11.2014
comment
Кажется, не повезло с: glBindTexture(CVOpenGLESTextureGetTarget(dstPixels),CVOpenGLESTextureGetName(dstPixels)); glClear(GL_COLOR_BUFFER_BIT); glFinish(); - person wfbarksdale; 17.11.2014
comment
Вам все равно нужно прикрепить его к фреймбуферу, прежде чем он будет очищен. Но если вы используете iOS вместо MacOS, вам, вероятно, понадобится решение Томми ниже. - person user1118321; 17.11.2014
comment
Чем больше я думал об этом, тем больше я полагал, что простое использование memset для обнуления пиксельного буфера может быть столь же эффективным, как и использование glClear. Есть мысли по этому поводу? - person wfbarksdale; 17.11.2014