GPUImage详细解析(九)图像的输入输出和滤镜通道

前言

GPUImage系列解析已经接近尾声,这次介绍的是:

  • 纹理输入输出GPUImageTextureOutputGPUImageTextureOutput
  • 二进制数据输入输出GPUImageRawDataInputGPUImageRawDataOutput
  • 滤镜通道GPUImageFilterPipeline

demo用来展示如何使用GPUImageRawDataOutput

概念介绍

1、GPUImageTextureOutput

GPUImageTextureOutput类实现GPUImageInput协议,可以接受响应链的图像,并返回对应的OpenGL ES纹理。

  • delegate属性:实现了GPUImageTextureOutputDelegate协议的回调对象;
  • texture属性:OpenGL ES的纹理,只读;
  • enabled属性:是否有效,默认为有效;
  • -doneWithTexture方法:结束处理纹理图像,解锁firstInputFramebuffer。

2、GPUImageTextureInput

GPUImageTextureInput类继承GPUImageOutput类,可以作为响应链的起点,把OpenGL ES纹理对应的纹理信息导入响应链处理。
textureSize属性为纹理尺寸;
初始化的时候,分配一个GPUImageFramebuffer,缓存纹理单元的信息;
process的时候直接调用targets对应的就绪方法,因为图像信息就在OpenGL ES控制内存中。

GPUImageTextureOutput 和 GPUImageTextureInput 用于 向OpenGL ES 输入或者输出纹理,把GPUImage的输出作为OpenGL ES的纹理或者把OpenGL ES的输出作为GPUImage的纹理输入。

3、GPUImageRawDataOutput

GPUImageRawDataOutput类实现协议GPUImageInput,可以接受响应链的图像信息,并且以二进制的格式返回数据;

  • rawBytesForImage属性: 二进制数据的指针;

  • GPUByteColorVector结构体:RGBA颜色空间结构体,便于读取二进制数据;

  • supportsFastTextureUpload用的BGRA的颜色格式;
    如果需要输出RGBA,则可以对BGRA格式再做一次RGBA->BRGA的颜色转换;
    RGBA -> BGRA 的操作如下:
    texture2D(inputImageTexture, textureCoordinate).bgra;

  • lockNextFramebuffer属性:标志是否要读取图像信息如果为YES,会调用CVPixelBufferLockBaseAddress锁住对应的CVPixelBufferRef;

4、GPUImageRawDataInput

GPUImageRawDataInput类继承GPUImageOutput类,可以接受二进制数据,按照特定的颜色格式,把数据转成图像并传入响应链;
GPUImageRawDataInput不会对传入的数据copied或者retained,但你不需要在使用完之后去释放;二进制数据发送到GPU的纹理单元,默认的颜色格式是BGRA和整型数据。

  • 上传图片的逻辑:先申请outputframebuffer ,然后绑定纹理,最后用glTexImage2D 上传图像数据到GPU。
  • processData方法:处理图片;如果上一次操作还未完成,则直接返回。

5、GPUImageFilterPipeline

GPUImageFilterPipeline类是滤镜通道,把inputs的滤镜组合起来,然后添加output为最后的输出目标。核心代码如下:

  • filters为输入的滤镜,output为输出目标;
  • 把filters的滤镜按照链表的形式串联起来。
- (void)_refreshFilters {

    id prevFilter = self.input;
    GPUImageOutput<GPUImageInput> *theFilter = nil;

    for (int i = 0; i < [self.filters count]; i++) {
        theFilter = [self.filters objectAtIndex:i];
        [prevFilter removeAllTargets];
        [prevFilter addTarget:theFilter];
        prevFilter = theFilter;
    }

    [prevFilter removeAllTargets];

    if (self.output != nil) {
        [prevFilter addTarget:self.output];
    }
}

demo思路

GPUImage详细解析(五)滤镜视频录制的流程图为左边部分;
demo的流程图为右边部分:

  • 1、这次把GPUImageVideoCamera的输出设置为GPUImageRawDataOutput
    self.mOutput = [[GPUImageRawDataOutput alloc] initWithImageSize:CGSizeMake(640, 480) resultsInBGRAFormat:YES];
    [videoCamera addTarget:self.mOutput];
  • 2、输出的二进制数据转换为CVPixelBufferRef
        [strongOutput lockFramebufferForReading];
        GLubyte *outputBytes = [strongOutput rawBytesForImage];
        NSInteger bytesPerRow = [strongOutput bytesPerRowInOutput];
        CVPixelBufferRef pixelBuffer = NULL;
        CVReturn ret = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, 640, 480, kCVPixelFormatType_32BGRA, outputBytes, bytesPerRow, nil, nil, nil, &pixelBuffer);
  • 3、根据buffer创建CGImageRef,再是UIImage
        CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, strongOutput.rawBytesForImage, bytesPerRow * 480, NULL);
        CGImageRef cgImage = CGImageCreate(640, 480, 8, 32, bytesPerRow, rgbColorSpace, kCGImageAlphaPremultipliedFirst|kCGBitmapByteOrder32Little, provider, NULL, true, kCGRenderingIntentDefault);
        UIImage *image = [UIImage imageWithCGImage:cgImage];
  • 4、最后添加到UIView的UIImageView上。
// 此处必须在主线程操作UI
    dispatch_async(dispatch_get_main_queue(), ^{
        self.mImageView.image = image;
    });

遇到的问题

1、创建像素错误

CVPixelBufferCreateWithBytes方法返回kCVReturnInvalidArgument错误。

检查CVPixelBufferCreateWithBytes的width、height、pixelFormatType参数。

2、输出图像方向错误

GPUImageVideoCamera输出图像旋转了90°。

In previous iOS versions, the front-facing camera would always deliver buffers in VCaptureVideoOrientationLandscapeLeft。
添加以下代码:

videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    videoCamera.horizontallyMirrorFrontFacingCamera = YES;

3、创建图像失败

通过二进制数据创建NSData,再创建UIImage,发现image为nil。

        NSData* data = [[NSData alloc] initWithBytes:strongOutput.rawBytesForImage length:bytesPerRow * 480];
        UIImage *image = [[UIImage alloc] initWithData:data];

暂未找到原因,猜测是data参数有问题。
+initWithData:的data参数要求必须是系统支持的image类型。
The data in the data parameter must be formatted to match the file format of one of the system’s supported image types.

4、图像未显示

检查UIImage对象不为nil,屏幕未显示。

GPUImageVideoCamera回调不在主线程,直接赋值image无效。

总结

GPUImageTextureOutput和GPUImageTextureInput用于GPUImage和OpenGL ES之间的协调,GPUImageRawDataOutput和GPUImageRawDataInput用于GPUImage和UIKit之间的协调, GPUImageFilterPipeline用于把多个滤镜简单串联。
代码地址

扩展

小端模式中ARGB的内存储存方式为BGRA。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

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

推荐阅读更多精彩内容