在iOS中用白色像素替换部分像素缓冲区

7

我正在使用iPhone相机捕捉实时视频,并将像素缓冲区输入到进行一些对象识别的网络中。以下是相关代码:(我不会发布设置AVCaptureSession等的代码,因为这很标准。)

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    OSType sourcePixelFormat = CVPixelBufferGetPixelFormatType( pixelBuffer );
    int doReverseChannels;
    if ( kCVPixelFormatType_32ARGB == sourcePixelFormat ) {
        doReverseChannels = 1;
    } else if ( kCVPixelFormatType_32BGRA == sourcePixelFormat ) {
        doReverseChannels = 0;
    } else {
        assert(false);
    }

    const int sourceRowBytes = (int)CVPixelBufferGetBytesPerRow( pixelBuffer );
    const int width = (int)CVPixelBufferGetWidth( pixelBuffer );
    const int fullHeight = (int)CVPixelBufferGetHeight( pixelBuffer );
    CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
    unsigned char* sourceBaseAddr = CVPixelBufferGetBaseAddress( pixelBuffer );
    int height;
    unsigned char* sourceStartAddr;
    if (fullHeight <= width) {
        height = fullHeight;
        sourceStartAddr = sourceBaseAddr;
    } else {
        height = width;
        const int marginY = ((fullHeight - width) / 2);
        sourceStartAddr = (sourceBaseAddr + (marginY * sourceRowBytes));
    }
}

网络接受 sourceStartAddr, width, height, sourceRowBytes & doReverseChannels 作为输入。
我的问题是:最简单和/或最有效的方法是什么,可以用所有白色的“像素”替换或删除图像数据的一部分? 是否可能直接覆盖像素缓冲区数据的一部分? 如果是,如何覆盖?
我只有对这个像素缓冲区的非常基础的了解,所以如果我漏掉了一些基本的东西,我很抱歉。我在Stackoverflow上找到的与我的问题最相关的问题是这个问题,其中使用EAGLContext将文本添加到视频帧中。虽然这对于我只需要这种替换单个图像的目标来说实际上是可行的,但我认为如果应用于每个视频帧,此步骤会影响性能,我想找出是否有另一种方法。任何关于此的帮助将不胜感激。
3个回答

17

以下是一种不使用其他库(如Core Graphics或OpenGL)来操纵CVPixelBufferRef的简单方法:

这里是一种不使用其他库(例如Core Graphics或OpenGL)而直接操纵CVPixelBufferRef的简单方法:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    const int kBytesPerPixel = 4;
    CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
    int bufferWidth = (int)CVPixelBufferGetWidth( pixelBuffer );
    int bufferHeight = (int)CVPixelBufferGetHeight( pixelBuffer );
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow( pixelBuffer );
    uint8_t *baseAddress = CVPixelBufferGetBaseAddress( pixelBuffer );

    for ( int row = 0; row < bufferHeight; row++ )
    {
        uint8_t *pixel = baseAddress + row * bytesPerRow;
        for ( int column = 0; column < bufferWidth; column++ )
        {
            if ((row < 100) && (column < 100) {
                pixel[0] = 255; // BGRA, Blue value
                pixel[1] = 255; // Green value
                pixel[2] = 255; // Red value
            }
            pixel += kBytesPerPixel;
        }
    }

    CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );

    // Do whatever needs to be done with the pixel buffer
}

这将用白色像素覆盖图片左上角的100 x 100像素区域。

我在这个名为RosyWriter的苹果开发者示例中找到了这个解决方案。

对于这个问题如此简单,我很惊讶在这里没有得到任何答案。希望这能帮到某些人。


1
我在过去的一周里花费了超过100个小时来寻找像这样简单的东西。不幸的是,我没有处理RGB缓冲区。 - znelson

7

使用Swift实现进行更新。

        CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
        let bufferWidth = Int(CVPixelBufferGetWidth(pixelBuffer))
        let bufferHeight = Int(CVPixelBufferGetHeight(pixelBuffer))
        let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)

        guard let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) else {
                return
        }

        for row in 0..<bufferHeight {
            var pixel = baseAddress + row * bytesPerRow
            for col in 0..<bufferWidth {
                let blue = pixel
                blue.storeBytes(of: 255, as: UInt8.self)

                let red = pixel + 1
                red.storeBytes(of: 255, as: UInt8.self)

                let green = pixel + 2
                green.storeBytes(of: 255, as: UInt8.self)
             
                let alpha = pixel + 3
                alpha.storeBytes(of: 255, as: UInt8.self)
                
                pixel += 4;
            }
        }

        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

由于baseAddress给出的是UnsafeMutableRawPointer,它不支持下标操作,因此您必须使用storeBytes替代。这基本上是与上面Objective-C版本唯一的关键区别。


我该怎么做才能设置红色,而不是白色? - Bartłomiej Semańczyk
@BartłomiejSemańczyk 我认为你可以通过将蓝色和绿色设置为0来实现这一点? - barley
卡在这里好几天了!谢谢。 - Brett

3

我必须使用captureOutput和CVPixelBuffer处理iPhone相机的帧。我使用了您的代码(谢谢!)每秒15帧中的像素缓冲区中的大约200k像素进行循环,但我不断遇到丢帧问题。结果证明,在Swift中,while循环比for...in循环快10倍。

例如:

0.09秒:

   for row in 0..<bufferHeight {

        for col in 0..<bufferWidth {
          // process pixels

0.01秒:

    var x = 0
    var y = 0

    while y < bufferHeight
    {
        y += 1
        x = 0;
        while x < bufferWidth
        {
        // process pixels 
        }
     }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接