概述
GPUImage框架是一个获得bsd许可的iOS库,允许您对图像、实时摄像机视频和电影应用gpu加速过滤器和其他效果。与Core Image (iOS 5.0的一部分)相比,GPUImage允许您编写自己的自定义过滤器,支持iOS 4.0的部署,并且具有更简单的界面。然而,它目前缺少核心图像的一些更高级的功能,比如面部检测。
对于处理图像或视频帧的大规模并行操作,gpu比cpu有一些显著的性能优势。在iPhone 4上,一个简单的图像过滤器在GPU上的运行速度比一个同等的基于cpu的过滤器快100倍。
然而,在GPU上运行自定义过滤器需要很多代码来设置和维护这些过滤器的OpenGL ES 2.0呈现目标。为此,我创建了一个示例项目:
http://www.sunsetlakesoftware.com/2010/10/22/gpu-accelerated-video-processing-mac-and-ios
我发现在它的创建过程中有很多样板代码。因此,我将这个框架放在一起,它封装了处理图像和视频时将遇到的许多常见任务,并使其不需要关心OpenGL ES 2.0的基础。
在处理视频时,这个框架比Core Image要好,在iPhone 4上只需要2.5毫秒就可以从摄像头上传一帧图像,应用gamma filter,然后显示,而在同样的操作中使用Core Image只需要106毫秒。基于cpu的处理需要460毫秒,这使得GPUImage比Core Image快40倍,比cpu绑定的处理快184倍。在iPhone 4S上,GPUImage仅比Core Image快4倍,比cpu绑定处理快102X倍。然而,对于更复杂的操作,如高斯模糊在较大的半径,核心图像目前超过GPUImage。
许可证
bsd风格,在license .txt中有完整的许可框架。
技术要求
OpenGL ES 2.0:使用这种技术的应用程序不会在最初的iPhone、iPhone 3G和第一代和第二代iPod touch上运行
iOS 4.1作为部署目标(4.0不需要一些扩展来阅读电影)。如果想在拍摄静态照片时显示实时视频预览,需要将iOS 4.3作为部署目标。
iOS 5.0 SDK构建
设备必须有摄像头才能使用与摄像头相关的功能(显然)
该框架使用自动引用计数(ARC),但如果添加为子项目,则应支持使用ARC和手动引用计数的项目,如下所述。用于针对iOS 4的手动引用计数应用程序。x,您需要将-fobjc-arc添加到应用程序项目的其他链接标志。
总体架构
GPUImage使用OpenGL ES 2.0着色器来执行图像和视频操作,比cpu绑定例程快得多。但是,它隐藏了在一个简化的Objective-C接口中与OpenGL ES API交互的复杂性。这个接口允许您为图像和视频定义输入源,在链中附加过滤器,并将结果处理后的图像或视频发送到屏幕、UIImage或磁盘上的电影。
视频的图像或帧是从源对象上传的,源对象是GPUImageOutput的子类。其中包括GPUImageVideoCamera(用于iOS摄像机的实时视频)、GPUImageStillCamera(用于相机拍照)、GPUImagePicture(用于静态图像)和GPUImageMovie(用于电影)。源对象将静态图像帧作为纹理上传到OpenGL ES,然后将这些纹理传递给处理链中的下一个对象。
过滤器和链中的其他后续元素符合GPUImageInput协议,该协议允许它们从链中的前一个链接中获取提供的或经过处理的纹理并对其进行处理。再往下走一步的对象被认为是目标,通过向单个输出或过滤器添加多个目标,可以对处理进行分支。
例如,一个应用程序从摄像头获取实时视频,将视频转换为深褐色色调,然后将视频显示在屏幕上,它会建立一个链,看起来像这样:
GPUImageVideoCamera -> GPUImageSepiaFilter -> GPUImageView
将静态库添加到iOS项目中
注意:如果您想在Swift项目中使用这个,您需要使用“Adding this as a framework”部分中的步骤,而不是以下步骤。Swift需要第三方代码模块。
有了框架的最新源代码后,将其添加到应用程序中就相当简单了。从拖动GPUImage开始。在应用程序的Xcode项目中嵌入框架。接下来,转到应用程序的目标,并将GPUImage添加为目标依赖项。最后,需要拖动libGPUImage。从GPUImage框架的Products文件夹到链接二进制与库的应用程序目标构建阶段的库。
GPUImage需要一些其他框架链接到您的应用程序中,因此您需要在您的应用程序目标中添加以下链接库:
CoreMedia
CoreVideo
OpenGLES
AVFoundation
QuartzCore
您还需要找到框架标头,因此在项目的构建设置中,要将标头搜索路径设置为从应用程序到GPUImage源目录中的框架/子目录的相对路径。使这个头搜索路径递归。
要在应用程序中使用GPUImage类,只需使用以下方法包括核心框架头:
#import "GPUImage.h"
注意:如果您在尝试使用Interface Builder构建接口时遇到“未知类GPUImageView在Interface Builder中”之类的错误,那么您可能需要在项目的构建设置中向其他链接器标记添加-ObjC。
另外,如果您需要将其部署到iOS 4中。x,看来Xcode的当前版本(4.3)要求您在最终应用程序中弱链接核心视频框架,或者在创建用于上传到应用程序商店或特别发布的存档时,看到“符号未找到:_CVOpenGLESTextureCacheCreate”的消息崩溃。要做到这一点,请转到您的项目的构建阶段选项卡,使用库组展开链接二进制文件,并在列表中找到corevido .framework。将列表最右侧的设置从Required更改为Optional。
此外,这是一个支持arc的框架,所以如果你想在面向iOS 4的手动引用计数应用程序中使用它。x,还需要将-fobjc-arc添加到其他链接器标志中。
在命令行中构建静态库
如果不希望将项目作为依赖项包含在应用程序的Xcode项目中,可以为iOS模拟器或设备构建通用静态库。要做到这一点,运行build。命令行。生成的库和头文件将位于build/Release-iphone上。您还可以通过更改构建中的IOSSDK_VER变量来更改iOS SDK的版本。sh(可以使用xcodebuild -showsdks找到所有可用的版本)。
将其作为框架(模块)添加到Mac或iOS项目中
Xcode 6和iOS 8支持完整框架的使用,Mac也是如此,这简化了向应用程序添加这个框架的过程。要将其添加到应用程序中,我建议将.xcodeproj项目文件拖放到应用程序的项目中(就像在静态库目标中那样)。
对于您的应用程序,转到它的目标构建设置并选择构建阶段选项卡。在目标依赖项分组下,在iOS上添加GPUImageFramework(不是构建静态库的GPUImage)或在Mac上添加GPUImage。
这将导致GPUImage作为框架构建。在Xcode 6中,这也将作为一个模块构建,允许您在Swift项目中使用它。在如上所述设置时,您只需使用
import GPUImage
即可。
然后,您需要添加一个新的复制文件构建阶段,将目标设置为框架,并为此添加GPUImage.framework构建产品。这将允许框架与您的应用程序捆绑在一起(否则,您将看到“dyld: Library not loaded: @rpath/GPUImage.framework/GPUImage”执行错误)。
文档
文档是使用appledoc从标题注释中生成的。要构建文档,请切换到Xcode中的“文档”模式。您应该确保“APPLEDOC_PATH”(用户定义的构建设置)指向appledoc二进制文件,可以在Github上使用,也可以通过Homebrew使用。它还将构建和安装一个.docset文件,您可以使用自己喜欢的文档工具查看该文件。
执行常见任务
过滤视频直播
要从iOS设备的摄像头中过滤实时视频,可以使用以下代码:
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, viewWidth, viewHeight)];
// Add the view somewhere so it's visible
[videoCamera addTarget:customFilter];
[customFilter addTarget:filteredVideoView];
[videoCamera startCameraCapture];
这就设置了一个来自iOS设备背对摄像头的视频源,使用了一个预置,试图捕捉640x480像素。这段视频是在竖屏模式下拍摄的,左置景观摄像头需要旋转其视频帧才能显示。一个自定义过滤器,使用来自文件CustomShader的代码。然后将fsh设置为摄像机的视频帧的目标。这些经过过滤的视频帧最终在UIView子类的帮助下显示在屏幕上,这个UIView子类可以显示这个管道产生的经过过滤的OpenGL ES纹理。
GPUImageView的填充模式可以通过设置它的fillMode属性来改变,这样如果源视频的长宽比与视图不同,视频要么被拉伸,以黑色条为中心,要么被缩放以填充。
对于混合过滤器和接受多个图像的其他过滤器,您可以创建多个输出并添加一个过滤器作为这两个输出的目标。作为目标添加输出的顺序将影响输入图像混合或以其他方式处理的顺序。
另外,如果你想让麦克风的音频捕捉记录到电影,你需要把摄像机的audioEncodingTarget设置为你的电影编剧,比如:
videoCamera.audioEncodingTarget = movieWriter;
捕捉和过滤静止照片
要捕获和过滤静态照片,可以使用类似于过滤视频的过程。你用的不是GPUImageVideoCamera,而是GPUImageStillCamera:
stillCamera = [[GPUImageStillCamera alloc] init];
stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
filter = [[GPUImageGammaFilter alloc] init];
[stillCamera addTarget:filter];
GPUImageView *filterView = (GPUImageView *)self.view;
[filter addTarget:filterView];
[stillCamera startCameraCapture];
这将给你一个实时的,过滤后的静止摄像头的预览视频。请注意,这个预览视频仅在iOS 4.3及更高版本上提供,因此如果您希望具有此功能,可能需要将其设置为部署目标。
一旦你想要捕捉照片,你使用一个回调块如下:
[stillCamera capturePhotoProcessedUpToFilter:filter withCompletionHandler:^(UIImage *processedImage, NSError *error){
NSData *dataForJPEGFile = UIImageJPEGRepresentation(processedImage, 0.8);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSError *error2 = nil;
if (![dataForJPEGFile writeToFile:[documentsDirectory stringByAppendingPathComponent:@"FilteredPhoto.jpg"] options:NSAtomicWrite error:&error2])
{
return;
}
}];
上面的代码捕获了由preview视图中使用的相同筛选器链处理的全尺寸照片,并将该照片作为JPEG保存在应用程序的documents目录中。
请注意,由于纹理大小的限制,该框架目前无法处理超过2048像素宽或高的旧设备(iPhone 4S、iPad 2或Retina iPad之前的设备)上的图像。这意味着,iPhone 4的摄像头输出的照片仍然比这大,无法捕捉到这样的照片。目前正在实施一种平铺机制来解决这个问题。所有其他设备都应该能够使用这种方法捕获和过滤照片。
处理静态图像
有几种方法可以处理静态图像并创建结果。第一种方法是创建一个静态图像源对象并手动创建一个过滤器链:
UIImage *inputImage = [UIImage imageNamed:@"Lambeau.jpg"];
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:inputImage];
GPUImageSepiaFilter *stillImageFilter = [[GPUImageSepiaFilter alloc] init];
[stillImageSource addTarget:stillImageFilter];
[stillImageFilter useNextFrameForImageCapture];
[stillImageSource processImage];
UIImage *currentFilteredVideoFrame = [stillImageFilter imageFromCurrentFramebuffer];
注意,要手动从过滤器捕获图像,您需要设置-useNextFrameForImageCapture,以便告诉过滤器稍后需要从它捕获图像。默认情况下,GPUImage在过滤器中重用framebuffer来保存内存,所以如果您需要保存过滤器的framebuffer以进行手动图像捕获,您需要提前让它知道。
对于您希望应用于图像的单个过滤器,您可以简单地执行以下操作:
GPUImageSepiaFilter *stillImageFilter2 = [[GPUImageSepiaFilter alloc] init];
UIImage *quickFilteredImage = [stillImageFilter2 imageByFilteringImage:inputImage];
编写一个自定义的过滤器
与iOS上的核心映像相比,这个框架的一个显著优点是能够编写自己的自定义映像和视频处理过滤器。这些过滤器作为OpenGL ES 2.0片段着色器提供,用类似c的OpenGL着色语言编写。
使用如下代码初始化自定义筛选器
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
用于片段着色器的扩展名为.fsh。另外,如果您不想在应用程序包中发布片段着色器,可以使用-initWithFragmentShaderFromString: initializer将片段着色器作为字符串提供。
片段着色器对要在该筛选阶段呈现的每个像素执行计算。他们使用OpenGL Shading Language (GLSL)来做到这一点,这是一种类似于c语言的语言,添加了特定于2d和3d图形。片段着色器的一个例子是:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp vec4 outputColor;
outputColor.r = (textureColor.r * 0.393) + (textureColor.g * 0.769) + (textureColor.b * 0.189);
outputColor.g = (textureColor.r * 0.349) + (textureColor.g * 0.686) + (textureColor.b * 0.168);
outputColor.b = (textureColor.r * 0.272) + (textureColor.g * 0.534) + (textureColor.b * 0.131);
outputColor.a = 1.0;
gl_FragColor = outputColor;
}
要使图像过滤器在GPUImage框架中可用,需要前两行包含textureCoordinate变化(对于纹理内的当前坐标,归一化到1.0)和inputImageTexture uniform(对于实际输入的图像帧纹理)。
着色器的其余部分在传入纹理的这个位置抓取像素的颜色,以产生深褐色色调的方式操作它,并将像素颜色写出来,以便在处理管道的下一阶段使用。
在Xcode项目中添加片段着色器时需要注意的一点是,Xcode认为它们是源代码文件。为了解决这个问题,您需要手动将着色器从编译源构建阶段移动到复制包资源阶段,以便将着色器包含到应用程序包中。
过滤和重新编码一个电影
电影可以通过GPUImageMovie类加载到框架中,过滤,然后使用GPUImageMovieWriter写出。GPUImageMovieWriter的速度也足够快,可以在640x480英寸的iPhone 4相机上实时录制视频,因此可以直接将经过过滤的视频源输入其中。目前,GPUImageMovieWriter的速度足以在iPhone 4上以高达20帧/秒的速度录制720p的视频,在iPhone 4S上(以及在新iPad上)以30帧/秒的速度录制720p和1080p的视频。
下面是一个示例,演示如何加载示例影片,将其通过像素化过滤器传递,然后将结果以480x640的速度记录到磁盘h.264电影:
movieFile = [[GPUImageMovie alloc] initWithURL:sampleURL];
pixellateFilter = [[GPUImagePixellateFilter alloc] init];
[movieFile addTarget:pixellateFilter];
NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.m4v"];
unlink([pathToMovie UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];
movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(480.0, 640.0)];
[pixellateFilter addTarget:movieWriter];
movieWriter.shouldPassthroughAudio = YES;
movieFile.audioEncodingTarget = movieWriter;
[movieFile enableSynchronizedEncodingUsingMovieWriter:movieWriter];
[movieWriter startRecording];
[movieFile startProcessing];
录制完成后,您需要从过滤链中删除影片记录器,并使用如下代码关闭录制:
[pixellateFilter removeTarget:movieWriter];
[movieWriter finishRecording];
电影在结束之前是不能用的,所以如果在这之前中断了,影片就会丢失。
与OpenGL ES交互
GPUImage可以通过分别使用其GPUImageTextureOutput和GPUImageTextureInput类从OpenGL ES导出和导入纹理。这允许您从OpenGL ES场景中录制电影,该场景通过绑定纹理呈现到framebuffer对象,或过滤视频或图像,然后将它们作为纹理输入到OpenGL ES中以显示在场景中。
这种方法的一个警告是,在这些过程中使用的纹理必须通过共享组或类似的方式在GPUImage的OpenGL ES上下文和任何其他上下文之间共享。
示例应用程序
几个示例应用程序与框架源代码捆绑在一起。大多数都兼容iPhone和ipad级设备。它们试图展示框架的各个方面,并应该在框架开发期间作为API的最佳示例使用。这些包括:
SimpleImageFilter
在启动时将绑定的JPEG图像加载到应用程序中,对其应用过滤器,并将结果呈现到屏幕上。此外,本示例还展示了两种方法,分别是获取图像、对其进行过滤和将其保存到磁盘。
SimpleVideoFilter
一个像素过滤器被应用到实时视频流中,带有UISlider控件,允许您调整实时视频的像素大小。
SimpleVideoFileFilter
从磁盘加载一个电影文件,对其应用一个不清晰的掩码过滤器,过滤后的结果被重新编码为另一个电影。
MultiViewFilterExample
从一个摄像机提要,4个视图被应用到摄像机上的实时过滤器填充。一个是直接拍摄的视频,一个是预编程的深褐色调,两个是基于着色程序的自定义滤镜。
FilterShowcase
这演示了GPUImage提供的每个过滤器。
BenchmarkSuite
这是通过对cpu绑定例程和核心映像进行测试来测试整个框架的性能的。涉及静态图像和视频的基准测试是针对这三种方法运行的,结果显示在应用程序中。
CubeExample
这证明了GPUImage与OpenGL ES渲染的交互能力。镜框从相机上捕捉下来,然后用一个乌贼滤镜对它们进行处理,然后把它们放入一个纹理中,然后应用到一个可以用手指旋转的立方体的表面。这个立方体依次被渲染到一个有纹理支持的framebuffer对象中,然后这个纹理被反馈回GPUImage,在渲染到屏幕之前应用一个像素化过滤器。
换句话说,这个应用的路径是camera -> sepia tone filter -> cube -> pixellation filter -> display。
ColorObjectTracking
我在http://www.sunsetlakesoftware.com/2010/10/22/gpu- accelerator -video-processing-mac- ios中移植了一个使用GPUImage的颜色跟踪示例,这个应用程序在场景中使用颜色来从实时摄像机提要跟踪对象。四个视图之间切换可以包括原始相机饲料,相机饲料白色像素匹配颜色阈值,处理视频编码的位置在哪里的颜色在像素通过阈值测试,最后点的视频实时跟踪选定的颜色。轻触屏幕可更改颜色以跟踪匹配手指下像素的颜色。在屏幕上点击和拖动使颜色阈值或多或少得到了原谅。这在第二种颜色阈值视图上最明显。
目前,最后一步中颜色平均的所有处理都是在CPU上完成的,所以这部分非常慢。