GPUImage(三):万源之父GPUImageOutput

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

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


        正如之前两章中对GPUImage的理解,它的处理流程就像一个链或者管道,官方叫法为Pipeline。有管道就一定存在源头,也就是使用GPUImage时需要做的第一件事就是把源头准备好,才能进行接下来的滤镜也好调整也好的各种操作,最后把结果从管道中导出。所以理解GPUImage对输入源到底做了哪些事情对于理解整个处理过程十分重要。因为之前使用GPUImage基本是针对静态图片的处理,所以本章就针对静态图片作为输入源的具体操作做介绍。可能之后马上也会涉及到视频输入源,那这些就之后再补充。

        上篇文章中官方文档有提到,GPUImage提供了四种可以作为输入源的资源类型,再加上我补充的一种。其中GPUImagePicture、GPUImageRawDataInput这两种可作为静态图片资源类型的输入源初始化类型。但在具体说明者两个类之前,要先重点了解一下GPUImage中所有可作为输入源类型的父类:GPUImageOutput。


GPUImageOutput

      此类的介绍在该类的头文件注释中,大概意思是:图片或者视频帧可通过继承了GPUImageOutput的资源对象进行加载,资源对象把图片或者视频帧加载到OpenGL ES的纹理中,处理完成后再把这些纹理传递处理管道中的下一个对象。

        简单的说,就是继承了这个类的子类对象都可以:1.纹理信息传递给下一位;2.纹理信息导出成图片。因此,在此类中的实现就差不多分为三部分:1.自身数据的管理;2.数据传递对象的管理。3.导出图片。以下根据这三点分别介绍相应方法及属性。

1.自身数据的管理:

 //这个就是所有管道中每个节点传递的内容。GPUImage或者说OpenGL ES处理图片或者视频过程中,会将需要处理的图像内容放置到Buffer中处理。Framebuffer顾名思义,就是用来渲染单张图片或者一帧图像内容的对象,也就是传说中的FBO在GPUImage中的对应。可以看出,在OpenGL ES中也把其对象化,但本身基于C的api限制,在使用的时候并不能体现面向对象的特征,因此GPUImage把它严格意义上的变成了一个对象类型。我对FrameBuffer的理解是一个带有图片原始内容(texture)+各种纹理参数的类

GPUImageFramebuffer *outputFramebuffer;

        说到FrameBuffer其实还需要单独一章来说明其具体作用和GPUImage对此类对象的特殊管理方式,那就放到之后,现在先把它理解成一个GPUImageOutput类型的对象A必定有一个outputFramebuffer对象的属性,A从管道的上一个节点得到数据后进行处理完的数据内容会存在自身的outputFramebuffer中,然后再把outputFramebuffer传递给下一个节点。

       GPUImageOutput中并没有涉及到outputFramebuffer属性的初始化部分,原因在于针对不同的数据内容再加上上面提到的GPUImage对frameBuffer的特殊管理,outputFramebuffer的初始化部分都在GPUImageOutput的各个子类中进行。

//这个方法就是用来把自身的outputFramebuffer传递给下一个目标节点,也就是方法中的target。另外可以注意到方法还有第二个参数inputTextureIndex,简单理解就是,每个节点的输入源不一定只有一个。简单的处理流程一般都为一张图片加一个滤镜输出,但要实现对于把两张或者多张图片进行合并后输出这种需要两个以上输入源的需求时就需要增加这个参数来。

- (void)setInputFramebufferForTarget:(id)target atIndex:(NSInteger)inputTextureIndex;

{

[target setInputFramebuffer:[self framebufferForOutput] atIndex:inputTextureIndex];

}

2.数据传递对象的管理

//targets用于记录自身对象添加过的目标节点,所以对于一个管道来说,并不是只有一条路,每个节点都可以产生多个分支。例如,一张图片作为输入源,可以分别进行加马赛克和图片锐化操作,最终导出两张图片。

//targetTextureIndices 用于记录targets数组中对应下标的某个目标节点的输入顺序。这个顺序对于需要多个输入源的目标节点非常重要,决定了这个目标节点自己的frameBuffer的各种样式还有处理后的效果。值的范围在对应目标节点需要的输入源个数内,例如target0对象需要2个输入源,那targetTextureIndices中对应下标的元素值为0或者1。

NSMutableArray *targets, *targetTextureIndices;

//字面意思:当前目标对象是否要忽略。忽略的意思就是在处理管道中要不要选择不做这个目标对象的处理。

@property(readwrite, nonatomic) BOOL shouldIgnoreUpdatesToThisTarget;

//以下两个方法就是用来设置下一个目标节点的方法。如果下个target需要两个输入源,那么这两个输入源需按顺序调用addTarget,先add的对象的textureLocation为0,另一个则为1。方法中有两个操作比较重要:1.调用- (void)setInputFramebufferForTarget:(id)target atIndex:(NSInteger)inputTextureIndex;方法,在添加时就把自己所属的frameBuffer传递给了刚添加好的target。2.把添加的target以及自己在这个target中所占的下标保存管理。再会发现,这些操作都在一个定义好的队列中实现,如下下:

- (void)addTarget:(id)newTarget;

- (void)addTarget:(id)newTarget atTextureLocation:(NSInteger)textureLocation;

在GPUImageContext有对这个自定义队列进行初始化的操作,可以看到,当前运行的硬件环境决定了这个队列是串行还是并发。所有的任务都是同步的形式添加到队列中,目的是保证整个管道每一步的操作都能按顺序执行,这样才能保证管道中每个target得到并处理的内容都是存在且正确的。

_contextQueue = dispatch_queue_create("com.sunsetlakesoftware.GPUImage.openGLESContextQueue", GPUImageDefaultQueueAttribute());

dispatch_queue_attr_t GPUImageDefaultQueueAttribute(void)

{

#if TARGET_OS_IPHONE

if ([[[UIDevice currentDevice] systemVersion] compare:@"9.0" options:NSNumericSearch] != NSOrderedAscending)

{

return dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);

}

#endif

return nil;

}

//有添加就有移除,当当前管道中某些target的处理效果不需要或者需要更改的时候,你可以把这些节点从它的上一站移除,这个移除的操作就需要它的上一节点对象调用以下方法实现。如果作为中间节点,还需要把自己所添加的target移除。就像火车中的中间一截车厢,需要把自己与上一截和下一截的链接都去掉,才可以完全移除。当然在ARC下,全部销毁的时候不需要做这些移除工作。

- (void)removeTarget:(id)targetToRemove;

- (void)removeAllTargets;


3.导出图片

GPUImageOutput类其中一个非常独一无二的作用就是可以直接从该类处理完成后的数据内容中生成并导出图片。但是GPUImageOutput毕竟只是一个共有的基类,包括上方说到两点以及导出图片的具体方法的实现部分并没有在本类中体现,由其子类重写。

- (void)useNextFrameForImageCapture;

{

}

- (CGImageRef)newCGImageFromCurrentlyProcessedOutput;

{

return nil;

}

不过在- (CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter方法中,告诉了大家如何实现导出图片的具体步骤:

1.其中stillImageSource是一个输入源对象,把自己添加到当前target是肯定要做的;

2.调用当前将要导出图片的target的processImage之前一定要先调用useNextFrameForImageCapture方法;

3.调用当前将要导出图片的target的processImage方法。

[self useNextFrameForImageCapture];   

 [stillImageSource addTarget:(id)self];

[stillImageSource processImage];

CGImageRef processedImage = [self newCGImageFromCurrentlyProcessedOutput];


以上是GPUImageOutput类中的重要三个部分的介绍,但是像中间涉及到的frameBuffer、处理队列以及未提到的其他属性和方法并未描述完全,完全是因为作者的设计思路太狡猾,把几乎图像处理过程中会涉及到的点都进行了封装和准备。这些内容详细介绍同样放之后补上,如果有疑问或对我以上的理解有异议,欢迎私信讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容