GPUImage作为iOS相当老牌的图片处理三方库已经有些日子了(2013年发布第一个版本),至今甚至感觉要离我们慢慢远去(2015年更新了最后一个release)。可能现在分享这个稍微有点晚,再加上落影大神早已发布过此类文章,但是还是想从自己的角度来分享一下对其的理解和想法。
本文集所有内容皆为原创,严禁转载。
第一篇有提到想试着翻译一下GitHub中GPUImage的说明文档来化解当时刚接触时给我留下的不会用的痛,结果发现网上已有挺多大神完成了一字不差的翻译工作。那我就选择从其中挑选重点,以个人理解来解释说明文档重点部分的意思。
Overview
GPUImage可以对图片、摄像头实时影像、视频添加通过GPU加速的滤镜和图像处理效果。
与iOS5.0上的CoreImage框架比较,GPUImage的优势:可以自定义滤镜(通过自定义glsl语句来实现);不足:没有人脸检测等此类先进技术(话说现在已经相对不先进了吧。。)。
如果需要对图片或者视频进行大量计算,那么GPU实现可以说是首选,iPhone4上比较快了100倍。
使用GPU处理图像需要编写大量代码才可实现,作者就先写了一篇demo(http://www.sunsetlakesoftware.com/2010/10/22/gpu-accelerated-video-processing-mac-and-ios)。之后发现其实之间有很多可模版化的内容,再结合具体使用,最后开发出了GPUImage。意思就是使用GPUImage可以实现基于OpenGLES2.0的GPU处理图像工作并且基本不需涉及OpenGLES2.0的api。
Technical requirements
iOS5+,ARC,有摄像头,...现在已经不存在不满足要求的吧。
General architecture
这个在第一篇当中已经大致说明我自己的理解,此处就照本翻译。
GPUImage是通过OpenGL ES 2.0着色器实现图片和视频的处理计算,所以会比CPU更快。并且!在使用过程中不需要涉及复杂的OpenGLES2.0的api,而是提供封装好的OC实现的类和方法即可实现图片或视频的多步骤处理操作、并能导出处理完成的结果。
视频或图片载入后会以GPUImageOutput的一种子类为类型的资源对象存在。GPUImage具备的资源类有:GPUImageVideoCamera、GPUImageStillCamera、GPUImagePicture、GPUImageMovie(我记得还有一种:GPUImageRawDataInput。具体使用之后细说)。资源对象会把图片或视频载入到纹理(OpenGLES中的一种存储图片信息的具体对象),并且把这些纹理传入具体的处理流程。
在整个处理流程链中的滤镜或者说除了输入源之后的对象,都需要遵循GPUImageInput协议,只有这样才能拿到上一个步骤处理完成的纹理进行相应处理。这些对象会经过预先设置好的所有目标对象中,并且处理过程中可以有多个分支的存在,即有多个下一步骤的路径。
总结:通过GPUImage可以创建一个滤镜链,链中可以有多个分支,载入所需输入源后经过每个步骤的处理可得到一个或者多个结果。
Performing common tasks(挑重点)
Filtering live video
通过CustomShader.fsh(glsl语句构成)文件作为参数生成的自定义滤镜customFilter可以作为一个目标(target)添加到从摄像头获取到的实时影像帧对象上。最终,这些经过滤镜处理后的帧内容会显示在一个继承自UIView并且可以把纹理显示出来的视图(GPUImageView,其实最终是通过它的layer,即CAEAGLLayer对象来展示)上。
接下来提到了一个重点:GPUImageView的fillMode属性。源码中该属性的每个枚举都有简短的解释:1.kGPUImageFillModeStretch, // Stretch to fill the full view, which may distort the image outside of its normal aspect ratio。拉伸内容至填满整个视图,可能会照成图片被不等比拉伸至变形。
2.kGPUImageFillModePreserveAspectRatio, // Maintains the aspect ratio of the source image, adding bars of the specified background color。保持显示图片的原比例,在未铺满区域显示设置的背景颜色。
3.kGPUImageFillModePreserveAspectRatioAndFill // Maintains the aspect ratio of the source image, zooming in on its center to fill the view。保持显示图片的原比例,等比放大填充整个视图并居中显示。
这个fillMode在我实际项目中给我带来了一些困扰。所以这里就先简单翻译,想在之后单独针对GPUImageView的文章中详细说明。
如果想在通过GPUImage录制视频时也把声音录进去,那么可以设置GPUImageVideoCamera对象的audioEncodingTarget属性。
Capturing and filtering a still photo
这里提到的一点是:在某些设备上是支持不到2048或者更高像素的摄像头图片捕捉,原因是这些设备对纹理大小有相应的限制,翻开源码,可以从继承自GPUImageOutput的五个资源类中发现,初始化资源将信息写入纹理时,会做一次资源文件大小的判断。因为对应生成的纹理对象是根据资源文件大小来生成的,所以如果过大,则会使用当前设备所支持的最大纹理大小来进行生成。纹理既然作为一个编辑过程中实时存储数据信息的对象,它也是会占据相应内存的,并且在处理过程中,如果纹理过大,或者说纹理中待处理的信息过多,也会给GPU带来压力。所以即使在使用GPUImage对图片或者视频进行处理的过程中,同样需要注意处理资源的大小问题。
Processing a still image
对一张静态图片进行滤镜或者其他效果处理可以说是GPUImage的基础入门操作(我是这么认为)。
1.使用GPUImagePicture的初始化方法,可以将一张静态图片载入到纹理中。不过GPUImagePicture的初始化方法有好几个,具体区别同样之后再细说。
2.可以通过继承自GPUImageFilter的类的对象调用imageFromCurrentFramebuffer方法得到处理后的图片。但如果要使用这种方法导出图片,则必须要在GPUImagePicture对象执行processImage方法之前执行滤镜对象的useNextFrameForImageCapture方法,否则按照GPUImage的源码来看注定会crash,控制台的提示“Tried to overrelease a framebuffer, did you forget to call -useNextFrameForImageCapture before using -imageFromCurrentFramebuffer?”。
3.另一种导出处理图片的方法是调用滤镜对象的imageByFilteringImage方法,入参为UIImage对象。
Writing a custom filter
GPUImage相比CoreImage另一个大优势在于可以自定义滤镜,通过glsl语言(OpenGL Shading Language,十分类似C语言的OpenGL着色器语言)实现。GPUImage库中的绝大多数滤镜都是通过自定义的glsl语句进行对片段着色器操作,之前也有说过,因为大多数滤镜只改变纹理内容,并不会涉及到如何贴图,或者说如何展示纹理。
Filtering and re-encoding a movie
视频文件通过GPUImageMovie类载入,GPUImageMovieWriter类导出。需要注意的点有:
1.记录,或者说处理完成后,需要将GPUImageMovieWriter对象从上一个target(一般都为GPUImageFilter对象)中移除,并且调用自身的finishRecording方法。
2.如果在记录完成前坏了,那之前的处理导出内容也没了。
Interacting with OpenGL ES
通过GPUImage的GPUImageTextureOutput和GPUImageTextureInput类,可以从OpenGLES中导入或者导出纹理。使用这种方式的过程中要注意,待处理的纹理需要通过类似共享群组的东西在GPUImage自身的OpenGLES的context和其他context之间实现共享。
Built-in filters
GPUImage库中提供125种左右的内建滤镜。
这个我记得网上已经有大神对文档中有说明的滤镜做了详细的翻译,之后会针对比较常用或者特殊的滤镜做详细说明,此处就不翻译了。
Sample applications
又想起第一次接触GPUImage时,不看文档直接download,run,完了完全不知道每个demo的具体实现内容。所以建议初学者在接触到新的三方库或者源码时,不要急着run,先看看文档或者源码注释,可能会更好上手。
1.SimpleImageFilter:对一张静态图片进行滤镜操作,保存到disk。
2.SimpleVideoFilter:马赛克录像,滑杆可调整马赛克颗粒大小。
3.SimpleVideoFileFilter:对disk中的视频文件进行虚化处理,并保存为另一个视频文件。
4.MultiViewFilterExample:摄像头实时滤镜效果,多个滤镜叠加,并且其中两个是自定义滤镜。
5.FilterShowcase:GPUImage内建滤镜的全部展示,这个demo中的判断语句优点复杂。
6.BenchmarkSuite:GPUImage与基于CPU实现的图片处理效果以及CoreImage做的处理效率比较。
7.CubeExample:说明GPUImage与OpenGLES渲染的相互关系。给摄像头获取到的每一帧内容加上sepia(乌贼色效果?)的滤镜并且显示在一个立方体的表面,可以用手机旋转立方体。立方体被渲染成一个texture-backed(没懂)的frambuffer对象,再反馈给GPUImage进行马赛克处理后显示。
7.ColorObjectTracking:待解释。