如何为AVCaptureVideoPreviewLayer应用“滤镜”

32

我的应用目前使用AVFoundation从iPhone后置摄像头中获取原始摄像数据,并实时在AVCaptureVideoPreviewLayer上显示。

我的目标是有条件地对预览层应用简单的图像滤镜。这些图像不需要保存,因此我不需要捕获输出。例如,我想切换一项设置,将预览层中传入的视频转换为黑白模式。

我在这里找到了一个问题(链接),似乎可以通过在缓冲区中捕获单个视频帧、应用所需的转换,然后将每个帧显示为UIImage来实现类似的功能。由于几个原因,这对我的项目来说可能过于繁琐,我想避免任何可能引起性能问题的情况。

这是实现我的目标的唯一方式吗?

正如我提到的,我不打算捕获任何AVCaptureSession的视频,只是预览它。

3个回答

66
处理这个问题最高效的方式可能是使用OpenGL ES来过滤和显示这些视频帧。除了在与另一个视图或图层重叠时调整其透明度之外,您将无法直接对AVCaptureVideoPreviewLayer进行太多操作。
我有一个示例应用程序在这里,在其中我从相机中获取帧并应用OpenGL ES 2.0着色器以实时处理视频以供显示。在此应用程序(在此处详细说明在这里)中,我使用基于颜色的过滤器来跟踪相机视图中的对象,但其他人已修改了此代码以执行一些不错的视频处理效果。该应用程序中所有基于GPU的过滤器都可以在我的iPhone 4上以60 FPS的速度运行并显示在屏幕上。
唯一一款支持视频播放但没有OpenGL ES 2.0兼容GPU的iOS设备是iPhone 3G。如果您需要针对该设备进行定位,您可能可以借鉴视频捕获和生成OpenGL ES纹理的基本代码,然后使用苹果的GLImageProcessing示例应用程序中的过滤器代码。该应用程序构建在OpenGL ES 1.1上,所有iOS设备都支持该版本。
然而,我强烈建议使用OpenGL ES 2.0,因为您可以使用着色器实现比使用固定功能的OpenGL ES 1.1管道更多种类的效果。 (编辑:2012年2月13日)关于上面的更新,我现在创建了一个名为GPUImage的开源框架,封装了这种自定义图像过滤。它还处理捕获视频并在经过过滤后将其显示到屏幕上,只需要不到六行代码即可设置所有这些内容。有关该框架的更多信息,请阅读我的更详细的公告

2
@UFO - iOS 7存在已知的两次过滤器与静态图像之间的错误。它们可以处理实时摄像头输入和电影,但在该操作系统上使用静态图像会产生黑色图像。我正在研究这个问题。 - Brad Larson
1
@BradLarson,我能否在您的代码中使用cifilter(单色,色调,黑白等)? - souvickcse
1
这个示例代码已经过时,甚至在Xcode 8.2上都无法运行。 - Mujib Saiyyed
@Crashalot - 需要更新什么?如果您指的是示例代码,则更新它是读者的练习。基本概念仍然适用。 - Brad Larson
谢谢您的快速回复!只是想知道答案是否过时或仍然相关--也就是说,最有效的方法是OpenGL ES,AVCaptureVideoPreviewLayer提供的灵活性很少--因为这个答案已经超过6年了。谢谢! - Crashalot

3
我建议查看iOS开发库中的Rosy Writer示例。Brad Larson的GPUImage Library非常棒,但似乎有点过于复杂了。如果您只是想将OpenGL着色器(又名滤镜)添加到AVCaptureVideoPreviewLayer,则工作流程是将捕获会话的输出发送到OpenGL视图进行渲染。
AVCaptureVideoDataOutput *videoOut = [[AVCaptureVideoDataOutput alloc] init];
videoOut.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(_renderer.inputPixelFormat) };
[videoOut setSampleBufferDelegate:self queue:_videoDataOutputQueue];

然后在 captureOutput: 委托中,将样本缓冲区发送到 OpenGL 渲染器。
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CVPixelBufferRef sourcePixelBuffer = CMSampleBufferGetImageBuffer( sampleBuffer );
    _renderer copyRenderedPixelBuffer:sourcePixelBuffer];
}

在OpenGL渲染器中,将sourcePixelBuffer附加到纹理上,您可以在OpenGL着色器中对其进行过滤。 着色器是一个按像素运行的程序。 Rosy Writer示例还展示了使用不同滤波技术而不是OpenGL的示例。

这个着色器能否修改创建的电影/视频,还是只能修改预览层? - Crashalot

2

这个项目对我来说是一个巨大的帮助,当我决定要添加滤镜时,它帮助我重构了我的应用程序,使用MTKView而不是AVCaptureVideoPreviewLayer。感谢苹果和感谢Nick指出这一点! - zakray

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