我该如何快速处理iPhone相机拍摄的图像?

13

我正在编写一个iPhone应用程序,它将进行一些实时相机图像处理。我使用AVFoundation文档中提供的示例作为起点:设置捕获会话,从样本缓冲区数据制作UIImage,然后通过-setNeedsDisplay在某个位置绘制图像,我在主线程上调用该方法。

这样可以运行,但是速度比较慢(对于192 x 144预设,每帧需要50毫秒,测量结果是两次-drawRect:之间),而且我在App Store上看到一些应用程序比这还要快。
我的一半时间都花在了-setNeedsDisplay上。

如何加快这个图像处理过程?


1
如果您需要快速处理图像,例如过滤图像,则使用OpenGL将是最佳选择。请参阅此帖子的答案[链接](https://dev59.com/K2435IYBdhLWcg3w4UWs)。 - Steve McFarlin
1
@James - 这里他们实际上并不是在问如何识别或解释图像,而是如何加速处理和显示它们。OpenCV 并不一定是处理图像的性能最佳方法,也无法解决将处理后的帧显示到屏幕上的瓶颈问题。 - Brad Larson
2个回答

22
正如Steve所指出的,在我的回答here中,我鼓励人们在从iPhone相机处理和渲染图像到屏幕时使用OpenGL ES以获得最佳性能。这是因为使用Quartz不断更新UIImage到屏幕上是向显示器发送原始像素数据的相对缓慢的方式。
如果可能的话,我建议您使用OpenGL ES来进行实际处理,因为GPU非常适合这种工作。如果您需要保持OpenGL ES 1.1兼容性,您的处理选项比2.0的可编程着色器要有限得多,但仍可以进行一些基本的图像调整。
即使您正在使用CPU上的原始数据进行所有图像处理,仍然通过使用OpenGL ES纹理来更新每个帧的图像数据会更好。只需切换到该渲染路径,您就会看到性能的提升。
(更新:2012年2月18日)正如我在上面链接的回答的更新中所描述的那样,我用我的新的开源GPUImage框架使这个过程变得更加容易。这个框架为您处理了所有的OpenGL ES交互,因此您只需要专注于应用您想要的滤镜和其他效果在您的视频中。它比使用CPU绑定例程和手动显示更新处理这个过程快5-70倍。

我也在询问我现在的做法是否正确 - 通过CGImage从SampleBuffer获取UIImage(就像avfoundation中的示例),然后调用performSelectorOnMainThread与setNeedsDisplay(然后drawImageAtPoint)- 绘制需要约30毫秒,而performSelector也需要30毫秒,因此对于没有处理和最小相机分辨率,我只能得到<20 fps - 也许我做错了什么 - 特别是我不确定这个performSelector代码。 - grunge fightr
@user641443 - 再次强调,使用OpenGL ES会更快,并且您可以在后台线程上运行更新,因此无需依赖于主线程是否可用。 - Brad Larson
“我‘知道’Open GL,但是我的经验不够丰富——现在用它需要花费很多时间。至于执行选择器,我调用‘在主线程上执行选择器’只为了调用一行代码:‘set needs display’,所以我认为它不包括绘制时间。根据我的测量,它大约是:uint64_t time_a = mach_absolute_time();[self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:YES]; //大约20-30毫秒(可变)uint64_t time_b = mach_absolute_time();” - grunge fightr
由于您在“-performSelectorOnMainThread:”调用之前和之后获取了时间戳,并且“waitUntilDone”设置为YES,因此您还计算了该方法调用的持续时间。因此,“-performSelectorOnMainThread:”所需的时间至少与“-setNeedsDisplay”相同,因为“-setNeedsDisplay”在其中运行。因此,您得到的数字正是您所期望的。 - Brad Larson
-setNeedsDisplay 会在必要时触发实际的绘制,而这不会在不同的线程上发生,因此该方法只有在绘制完成后才会完成。因此,如果您的绘图程序运行缓慢,那么测量这个方法和 -performSelectorOnMainThread: 的时间其实就是在衡量 -drawRect: 运行的时间。因此,如果您的绘图程序运行缓慢,这些数字就非常合理。 - Brad Larson
显示剩余10条评论

5
在下面的示例代码中,将捕获会话的sessionPresent设置为AVCaptureSessionPresetLow,这将增加处理速度,但缓冲区中的图像质量会降低。

- (void)initCapture { AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil]; AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init] ; captureOutput.alwaysDiscardsLateVideoFrames = YES; captureOutput.minFrameDuration = CMTimeMake(1, 25); dispatch_queue_t queue; queue = dispatch_queue_create("cameraQueue", NULL); [captureOutput setSampleBufferDelegate:self queue:queue]; dispatch_release(queue); NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; [captureOutput setVideoSettings:videoSettings]; self.captureSession = [[AVCaptureSession alloc] init] ; [self.captureSession addInput:captureInput]; [self.captureSession addOutput:captureOutput]; self.captureSession.sessionPreset=AVCaptureSessionPresetLow; /*sessionPresent选择适当的值以获得所需的速度*/
if (!self.prevLayer) { self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession]; } self.prevLayer.frame = self.view.bounds; self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; [self.view.layer addSublayer: self.prevLayer];
}

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