【问题标题】:Replace Part of Pixel Buffer with White Pixels in iOS在 iOS 中用白色像素替换部分像素缓冲区
【发布时间】:2016-08-18 17:25:00
【问题描述】:

我正在使用 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));
    }
}

然后网络将sourceStartAddrwidthheightsourceRowBytesdoReverseChannels 作为输入。

我的问题如下:用全白色“像素”替换或删除部分图像数据的最简单和/或最有效的方法是什么?是否可以直接覆盖像素缓冲区数据的e部分,如果可以,如何?

我对这个像素缓冲区的工作原理只有非常基本的了解,所以如果我在这里遗漏了一些非常基本的东西,我深表歉意。我在 Stackoverflow 上发现的与我最密切相关的问题是 this one,其中 EAGLContext 用于向视频帧添加文本。虽然这实际上适用于我只需要替换单个图像的目标,但我认为如果应用于每个视频帧,这一步会降低性能,我想知道是否有另一种方法。如有任何帮助,我们将不胜感激。

【问题讨论】:

    标签: ios objective-c avfoundation cvpixelbuffer


    【解决方案1】:

    这是一种无需使用其他库(如 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 的 Apple Developer Example 中找到了这个解决方案。

    考虑到这很容易,我在这里没有得到任何答案,有点惊讶。希望这对某人有所帮助。

    【讨论】:

    • 在过去的一周里,我确实花了 100 多个小时试图找到像这样简单的东西。不幸的是,我没有处理 RGB 缓冲区。
    【解决方案2】:

    使用 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łomiejSemańczyk 我认为您可以通过将蓝色和绿色设置为 0 而不是 255 来实现这一点?
    • 坚持了好几天!!谢谢。
    【解决方案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 
            }
         }
    

    【讨论】:

      猜你喜欢
      • 2019-03-28
      • 2022-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 2012-09-30
      • 1970-01-01
      相关资源
      最近更新 更多