代码之家  ›  专栏  ›  技术社区  ›  Asaf Pinhassi

访问iPhone视频输出图像缓冲区时的低FPS

  •  8
  • Asaf Pinhassi  · 技术社区  · 14 年前

    我想在iPhone上做一些图像处理。我在用 http://developer.apple.com/library/ios/#qa/qa2010/qa1702.html 捕捉相机画面。

    我的问题是,当我试图访问捕获的缓冲区时,相机的FPS从30下降到大约20。有人知道我该怎么修吗?

    我使用kCVPixelFormatType_32BGRA格式的最低捕获质量(AVCaptureSessionPresetLow=192x144)。如果有人知道我可以用低质量的,我愿意试试。

    当我在其他平台(如Symbian)上进行相同的图像访问时,它可以正常工作。

    这是我的代码:

    #pragma mark -
    #pragma mark AVCaptureSession delegate
    - (void)captureOutput:(AVCaptureOutput *)captureOutput 
    didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
        fromConnection:(AVCaptureConnection *)connection 
    { 
     /*We create an autorelease pool because as we are not in the main_queue our code is
      not executed in the main thread. So we have to create an autorelease pool for the thread we are in*/
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
     //Lock the image buffer
        if (CVPixelBufferLockBaseAddress(imageBuffer, 0) == kCVReturnSuccess)
        {  
    
      // calculate FPS and display it using main thread
      [self performSelectorOnMainThread:@selector(updateFps:) withObject: (id) nil waitUntilDone:NO];
    
    
      UInt8 *base = (UInt8 *)CVPixelBufferGetBaseAddress(imageBuffer); //image buffer start address
    
      size_t width  = CVPixelBufferGetWidth(imageBuffer); 
      size_t height = CVPixelBufferGetHeight(imageBuffer);
    
      int size = (height*width);
      UInt8* pRGBtmp = m_pRGBimage;
    
            /*
            Here is the problem; m_pRGBimage is RGB image I want to process.
            In the 'for' loop I convert the image from BGRA to RGB. As a resault, the FPS drops to 20.
            */ 
      for (int i=0;i<size;i++)
      {   
        pRGBtmp[0] = base[2];
        pRGBtmp[1] = base[1];
        pRGBtmp[2] = base[0];
        base = base+4;
        pRGBtmp = pRGBtmp+3;     
      }
    
    
      // Display received action
      [self performSelectorOnMainThread:@selector(displayAction:) withObject: (id) nil waitUntilDone:NO];
      //[self displayAction:&eyePlayOutput];
      //saveFrame( imageBuffer );
    
      //unlock the  image buffer
      CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    
     }
    
    
     [pool drain];
    } 
    

    作为答案的后续,我需要实时处理图像,它正在显示。

    我注意到当我使用AVCaptureSessionPresetHigh时,我做的最简单的事情是:

     for (int i=0;i<size;i++)
        x = base[0];
    

    使帧速率降至4-5 FPS。我想是因为这样大小的图像没有被缓存。

    基本上我需要96x48图像。有没有一种简单的方法来缩小相机输出图像的比例,一种使用硬件加速的方法,这样我就可以使用小的一个?

    3 回复  |  直到 14 年前
        1
  •  8
  •   Brad Larson    14 年前

    任何在图像中的每一个像素上迭代的操作,除了最快的iOS设备外,都会相当慢。例如,我用一个简单的每像素颜色测试对640x 480视频帧(307200像素)中的每一个像素进行了基准迭代,结果发现在iPhone4上这只会以大约4fps的速度运行。

    在你的例子中,你看到的是27648像素的处理速度,在iPhone4上应该可以达到每秒30帧,但这是一个比原来的iPhone3G和iPhone3G快得多的处理器,iPhone3G可能仍然难以承受这样的处理负荷。你也不会说你的Symbian设备的处理器有多快。

    我建议修改你的处理算法以避免颜色空间转换。不需要重新排序颜色组件来处理它们。

    此外,通过在图像的行和列中以特定间隔进行采样,可以有选择地仅处理几个像素。

    最后,如果您的目标是支持OpenGL ES 2.0(iPhone 3G S和更新版本)的较新iOS设备,那么您可能希望看到使用GLSL片段明暗器完全在GPU上处理视频帧。我描述了这个过程 here ,以及用于实时基于颜色的对象跟踪的示例代码。在我的基准测试中,GPU处理这种处理的速度比CPU快14-28倍。

        2
  •  1
  •   deanWombourne    14 年前

    免责声明:这个答案是猜测:)

    当缓冲区被锁定时,您正在做大量的工作;这是否阻碍了从相机捕获图像的线程?

    你可以把数据从缓冲区中复制出来,这样你就可以尽快解锁,比如

    if (CVPixelBufferLockBaseAddress(imageBuffer, 0) == kCVReturnSuccess) {
        // Get the base address and size of the buffer
        UInt8 *buffer_base = (UInt8 *)CVPixelBufferGetBaseAddress(imageBuffer); //image buffer start address
        size_t width  = CVPixelBufferGetWidth(imageBuffer); 
        size_t height = CVPixelBufferGetHeight(imageBuffer);
    
        // Copy it's contents out
        Uint8 *base = malloc(width * height * 4);
        memcpy(base, buffer_base, size);
    
        // Unlock the buffer
        CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    
        // base now points to a copy of the buffers' data - do what you want to it . . .
        ...
    
        // remember to free base once you're done ;)
        free(base);
    

    如果是锁挡住了抓捕,这应该会有帮助。

    注意:如果你知道所有的缓冲区都是相同的大小,你可以只调用malloc一次来获取内存,然后每次都重用它,只有在你处理完所有的缓冲区后才释放它,那么你可以加快速度。


    或者如果不是这个问题,你可以尝试降低这个线程的优先级

    [NSThread setThreadPriority:0.25];
    
        3
  •  0
  •   gonzojive    14 年前

    将相机帧的内容复制到一个专用缓冲区中,并从那里对其进行操作。这使我的经验大大提高了速度。我最好的猜测是相机帧所在的内存区域有特殊的保护,使读/写访问变慢。

    检查相机帧数据的内存地址。在我的设备上,相机缓冲区在 0x63ac000 . 这对我来说并不意味着什么,除了其他堆对象位于更靠近 0x1300000 . 锁的建议并没有解决我的减速问题,但是 memcpy 做。