音视频iOS开发-图像处理

GPUImage(一):伴我成长的图像处理框架

2017-08-01  本文已影响4911人  ANTI_JAM

GPUImage作为iOS相当老牌的图片处理三方库已经有些日子了(2013年发布第一个版本),至今甚至感觉要离我们慢慢远去(2015年更新了最后一个release)。可能现在分享这个稍微有点晚,再加上落影大神早已发布过此类文章,但是还是想从自己的角度来分享一下对其的理解和想法。

本文集所有内容皆为原创,严禁转载。


        我在2015年底因为手头项目需求原因接触到GPUImage,在此之前在上海一家十分传统的金融互联网公司做着我的第一份iOS开发工作,面对着每天的TableView、CollectionView显示着异步获取下来的各式数据做着各种屏幕自适应,至今我还特别讨厌iPad翻转自适应,不过现在有诸如Masonry、SnapKit等相当好用的框架后好像容易解决一些。

        机缘巧合下来到杭州,完全一个人的开发工作,边上就一个天天迟到的产品在一直画着他想做的原型图。拿到之前版本的源码后花了几天看了滤镜、贴纸的实现,滤镜是通过CoreImage框架实现,贴纸则只是用视图假装的,之后面试的应聘者说到自己所掌握的图像处理也基本上有这些内容,相信这些的实现只要花点时间就能掌握。突然有一天他提了一个需求:能不能做一个类似PS的遮罩的功能?这个问题对于当时只会数据处理展示的我真是一大阻碍。有花了一些时间,最后发现CoreAnimation框架中的CALayer就能完美实现,并且它的子类的功能十分令人惊讶,包括之后的界面效果等使用其都能很好的满足需求,我的简书中也有自己对CALayer的一些理解。接下来才是真正让我遇到大困难的时候,当决定要做一款图像处理软件时我已经知道官方的CoreImage、CoreGraphics这些框架已经远远不能满足功能需求,并且在那时候也没特别多的时间允许我去学习诸如OpenGLES、OpenCV或者Metal等图像处理框架,最后抱着一丝希望,从GitHub上发现了GPUImage。

        GPUImage的wiki并不像是一个完整的说明文档。它只是告诉你:1.GPUImage是基于OpenGLES2.0实现;2.比CoreImage快;3.如何使用GPUImage来对一张静态图片、一段视频或者摄像头实时捕捉到的影响加一个滤镜效果并显示在GPUImageView上或者导出成相应格式的文件。可能只有我只看懂了以上三点,至于其他说到的例如:为什么加完滤镜要有效果必须调用processImage方法;导出图片前不调用useNextFrameForImageCapture方法就会crash;glsl语言有什么作用?这些问题对于当时的我完全毫无概念。并且,当时通过各搜索引擎获取到相关的资料无论中英文都是少之又少。因此,刚试手用GPUImage去实现那些复杂的图像处理需求时我差点选择了放弃。这么一说,我突然好想去翻译一下GPUImage的wiki。

       接下来正式说一下我对GPUImage的大体理解,如有错误,劳烦指正:

1.OpenGLES可以通过片段着色器(fragment)、顶点着色器(Vertex)两方面对图像进行做处理。GPUImage在iOS官方提供的基于OpenGLES的GLKit的再次封装或者说封装加拓展的库。GLKit是可以指定使用的OpenGLES版本,而GPUImage使用的是OpenGLES2.0。个人理解,GPUImage库中提供的大部分滤镜是通过片段着色器的一系列操作来实现相应的效果,原因是大部分滤镜效果并不需要改变图片本身的大小和外形,只是对图片中各像素进行计算生成新的像素,就可达到滤镜或者其他调整效果,这也体现了着色器语言(glsl),尤其是片段着色器语言在GPUImage框架中的份量之重。

2.GPUImage给使用者提供了一个链的概念,这个链并不像Masonry体现在使用上,而是将每一个输入源、滤镜、输出源假设为一段段独立管道,只有将其完整串起来后,才能把输入源的图像信息经过每一个独立管道进行处理最终流出得到具有所有处理效果的内容,以上同样是个人在使用GPUImage时对其工作流程的抽象理解。

3.在2的基础上,若想实现每个独立管道都能串联,GPUImage使用了一种在iOS不常用到的设计模式后:MVP。必然使用到的那些类,也就是可以加入到串联中的那些类,1.他们都继承于GPUImageOutput,除了GPUImageView;2.他们都遵循GPUImageInput协议,除了输入源类(GPUImage提供了五种输入源类:GPUImagePicture、GPUImageRawDataInput、GPUImageMovie、GPUImageUIElement、GPUImageVideoCamera,通过名字即可知道应根据输入源类型应选择相应类进行输入源对象创建)。GPUImageOutput类在使用过程中并不会去直接创建该类的对象,使用者所使用到的都是它的子类。从名字可以看出,GPUImageOutput的大致作用是可以作为一个输出源来使用,而遵循了GPUImageInput协议的类则可以理解成可以作为输出源来使用。所以在一段段独立管道的串联过程中,1.最源头的地方只要作为输入源,即五种输入源类不遵循GPUImageInput协议;2.在过程中需要串联的类,比如所有filter的父类GPUImageFilter则又是GPUImageOutput的子类又要遵循GPUImageInput协议,因为它要将它上一个管道中处理好的数据接收,处理完了之后要将相应数据作为下一个的输入源传递给下一个管道;3.在最后节点部分,GPUImageView就不需要继承GPUImageOutput,原因是在它之后并不存在下一个管道

        以上是我认为GPUImage最重要的三个点,如果之后还有想起其他就再来补充。这篇是我对GPUImage理解的开卷,之后想从多方面说明对其的深入理解。想写这些的原因其实也简单,现在手头的项目里依然用着GPUImage实现主要功能,但是没想到它竟然可以强大到只要有想法就能实现各式各样需求的地步,然后在开发过程中也希望自己能够对它有更深入的理解,从而能帮助项目更好的优化和进步。再有嘛,可能对今后的发展也有一些帮助吧。那第一篇就到这里,以上。

上一篇下一篇

猜你喜欢

热点阅读