GPUImage之美颜滤镜

标签: 

gpuimage

分类: 多媒体/FFMPEG/协议
  • GPUImageFilter就是用来接收源图像,通过自定义的顶点、片元着色器来渲染新的图像,并在绘制完成后通知响应链的下一个对象。
  • GPUImageFramebuffer就是用来管理纹理缓存的格式与读写帧缓存的buffer。
  • GPUImageVideoCameraGPUImageOutput的子类,提供来自摄像头的图像数据作为源数据,一般是响应链的源头。
  • GPUImageView是响应链的终点,一般用于显示GPUImage的图像。

琨君基于GPUImage的实时美颜滤镜对GPUImage实现美颜滤镜的原理和思路做了详细介绍。
本文以琨君的代码为demo,结合前两篇解析,探究美颜过程中的GPUImage实现。

GPUImage类介绍

1、GPUImageFilterGroup

GPUImageFilterGroup是多个filter的集合,terminalFilter为最终的filter,initialFilters为filter数组。GPUImageFilterGroup本身不绘制图像,对GPUImageFilterGroup添加删除Target操作的操作都会转为terminalFilter的操作。

2、GPUImageTwoInputFilter

GPUImageTwoInputFilterGPUImageFilter的子类,对两个输入纹理进行通用的处理,需要继承它并准备自己的片元着色器。
两个输入纹理默认为inputImageTextureinputImageTexture2

  • 重写了下面的函数,修改GPUImageFilter绘制的逻辑。

    - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;

    下面这部分是核心的绘制逻辑:
    glActiveTexture()是选择纹理单元,glBindTexture()是把纹理单元和firstInputFramebuffersecondInputFramebuffer管理的纹理内存绑定。glUniform1i()告诉GLSL选择的纹理单元是2。
    这部分在上一篇介绍也有提到,再详细阐述:glActiveTexture()选择的是纹理单元,和glGenTextures()返回的数字没有关系,可以在纹理单元2上面绑定纹理12。
    glGenTextures()返回的纹理可以是GL_TEXTURE_2D类型也可以是GL_TEXTURE_CUBE_MAP类型,取决于glBindTexture()第一次绑定纹理的是GL_TEXTURE_2D还是GL_TEXTURE_CUBE_MAP

    glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, [firstInputFramebuffer texture]); glUniform1i(filterInputTextureUniform, 2); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, [secondInputFramebuffer texture]); glUniform1i(filterInputTextureUniform2, 3);
  • nextAvailableTextureIndex用于获取下一个纹理索引

    - (NSInteger)nextAvailableTextureIndex; { if (hasSetFirstTexture) { return 1; } else { return 0; } }

    setInputFramebuffer: atIndex:会根据上面获取的textureIndex设置firstInputFramebuffersecondInputFramebuffer。如果是textureIndex = 0,设置hasSetFirstTexture表示已经设置第一个纹理。

3、GPUImageThreeInputFilter

GPUImageThreeInputFilter的逻辑与GPUImageTwoInputFilter类似,增加了thirdInputFramebuffer作为第三个纹理inputImageTexture3的输入。

4、GPUImageBeautifyFilter

GPUImageBeautifyFilter基于GPUImage的实时美颜滤镜中的美颜滤镜,包括GPUImageBilateralFilterGPUImageCannyEdgeDetectionFilterGPUImageCombinationFilterGPUImageHSBFilter

绘制流程

GPUImage之美颜滤镜
绘制流程图
  • 1、GPUImageVideoCamera捕获摄像头图像
    调用newFrameReadyAtTime: atIndex:通知GPUImageBeautifyFilter

  • 2、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex:
    通知GPUImageBilateralFliter输入纹理已经准备好;

  • 3、GPUImageBilateralFliter 绘制图像后在informTargetsAboutNewFrameAtTime()
    调用setInputFramebufferForTarget: atIndex:
    把绘制的图像设置为GPUImageCombinationFilter输入纹理,
    并通知GPUImageCombinationFilter纹理已经绘制完毕;

  • 4、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex:
    通知 GPUImageCannyEdgeDetectionFilter输入纹理已经准备好;

  • 5、同3,GPUImageCannyEdgeDetectionFilter 绘制图像后,
    把图像设置为GPUImageCombinationFilter输入纹理;

  • 6、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex:
    通知 GPUImageCombinationFilter输入纹理已经准备好;

  • 7、GPUImageCombinationFilter判断是否有三个纹理,三个纹理都已经准备好后
    调用GPUImageThreeInputFilter的绘制函数renderToTextureWithVertices: textureCoordinates:
    图像绘制完后,把图像设置为GPUImageHSBFilter的输入纹理,
    通知GPUImageHSBFilter纹理已经绘制完毕;

  • 8、GPUImageHSBFilter调用renderToTextureWithVertices: textureCoordinates:绘制图像,
    完成后把图像设置为GPUImageView的输入纹理,并通知GPUImageView输入纹理已经绘制完毕;

  • 9、GPUImageView把输入纹理绘制到自己的帧缓存,然后通过
    [self.context presentRenderbuffer:GL_RENDERBUFFER];显示到UIView上。

总结

GPUImageFilter
GPUImageFramebuffer
GPUImageVideoCamera
GPUImageView
GPUImageFilterGroup
GPUImageTwoInputFilter
GPUImageThreeInputFilter
这是学习这个demo需要了解的7个类。
在绘制流程图的过程中,对GPUImage的响应链有了更清晰的认识。



文/落影loyinglin(简书作者)
原文链接:http://www.jianshu.com/p/2ce9b63ecfef
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”
 
 
 

GPUImage--美颜滤镜GPUImageBeautifyFilter

关于GPUimage的介绍请看这里GPUImage:滤镜、美颜、视频流处理

美颜只是不同滤镜组合起来的效果,实际上美颜也是一种滤镜,它的需求较多,于是自称一派。


GPUImageBeautifyFilter

/*
GPUImageBeautifyFilter是基于GPUImage的实时美颜滤镜中的美颜滤镜,包括GPUImageBilateralFilter、GPUImageCannyEdgeDetectionFilter、GPUImageCombinationFilter、GPUImageHSBFilter。
*/
  • 1
  • 2
  • 3

在GPUImageBeautifyFilter.h中有:分别创建对应上面的对象

#import "GPUImage.h"

@class GPUImageCombinationFilter;

@interface GPUImageBeautifyFilter : GPUImageFilterGroup {
    GPUImageBilateralFilter *bilateralFilter;
    GPUImageCannyEdgeDetectionFilter *cannyEdgeFilter;
    GPUImageCombinationFilter *combinationFilter;
    GPUImageHSBFilter *hsbFilter;
}

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

绘制流程分为:

  1. 准备纹理
  2. 绘制纹理
  3. 显示处理后的纹理

一、 准备纹理:这里用到的类 
[GPUImageVideoCamera] -  
[GPUImageBeautifyFilter] -  
[GPUImageBilateralFliter] -  
[GPUImageCombinationFilter] -  
[GPUImageCannyEdgeDetectionFilter] -

准备 过程:

第一个纹理: 
1、GPUImageVideoCamera捕获摄像头图像 
调用newFrameReadyAtTime: atIndex:通知GPUImageBeautifyFilter; 
2、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex: 
通知GPUImageBilateralFliter输入纹理已经准备好; 
第二个纹理: 
3、GPUImageBilateralFliter 绘制图像后, 
informTargetsAboutNewFrameAtTime(), 
调用setInputFramebufferForTarget: atIndex: 
把绘制的图像设置为GPUImageCombinationFilter输入纹理, 
并通知GPUImageCombinationFilter纹理已经绘制完毕; 
4、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex: 
通知 GPUImageCannyEdgeDetectionFilter输入纹理已经准备好; 
第三个纹理: 
5、同3,GPUImageCannyEdgeDetectionFilter 绘制图像后, 
把图像设置为GPUImageCombinationFilter输入纹理; 
6、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex: 
通知 GPUImageCombinationFilter输入纹理已经准备好;

原文:http://blog.sina.com.cn/s/blog_61bc01360102wpl2.html

二、绘制纹理:

7、判断纹理数量 
GPUImageCombinationFilter判断是否有三个纹理,三个纹理都已经准备好后 
调用GPUImageThreeInputFilter的绘制函数renderToTextureWithVertices: textureCoordinates:, 
图像绘制完后,把图像设置为GPUImageHSBFilter的输入纹理, 
通知GPUImageHSBFilter纹理已经绘制完毕; 
8、绘制纹理 
GPUImageHSBFilter调用renderToTextureWithVertices:  
textureCoordinates:绘制图像, 
完成后把图像设置为GPUImageView的输入纹理,并通知GPUImageView输入纹理已经绘制完毕;

显示纹理

9、GPUImageView把输入纹理绘制到自己的帧缓存,然后通过 
[self.context presentRenderbuffer:GL_RENDERBUFFER];显示到UIView上。

GPUImageBeautifyFilter.m文件是这样的

//
//  GPUImageBeautifyFilter.m
//  BeautifyFaceDemo
//
//  Created by guikz on 16/4/28.
//  Copyright © 2016年 guikz. All rights reserved.
//

#import "GPUImageBeautifyFilter.h"

// Internal CombinationFilter(It should not be used outside)
@interface GPUImageCombinationFilter : GPUImageThreeInputFilter
{
    GLint smoothDegreeUniform;
}

@property (nonatomic, assign) CGFloat intensity;

@end

NSString *const kGPUImageBeautifyFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;
 varying highp vec2 textureCoordinate2;
 varying highp vec2 textureCoordinate3;

 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 uniform sampler2D inputImageTexture3;
 uniform mediump float smoothDegree;

 void main()
 {
     highp vec4 bilateral = texture2D(inputImageTexture, textureCoordinate);
     highp vec4 canny = texture2D(inputImageTexture2, textureCoordinate2);
     highp vec4 origin = texture2D(inputImageTexture3,textureCoordinate3);
     highp vec4 smooth;
     lowp float r = origin.r;
     lowp float g = origin.g;
     lowp float b = origin.b;
     if (canny.r < 0.2 && r > 0.3725 && g > 0.1568 && b > 0.0784 && r > b && (max(max(r, g), b) - min(min(r, g), b)) > 0.0588 && abs(r-g) > 0.0588) {
         smooth = (1.0 - smoothDegree) * (origin - bilateral) + bilateral;
     }
     else {
         smooth = origin;
     }
     smooth.r = log(1.0 + 0.2 * smooth.r)/log(1.2);
     smooth.g = log(1.0 + 0.2 * smooth.g)/log(1.2);
     smooth.b = log(1.0 + 0.2 * smooth.b)/log(1.2);
     gl_FragColor = smooth;
 }
 );

@implementation GPUImageCombinationFilter

- (id)init {
    if (self = [super initWithFragmentShaderFromString:kGPUImageBeautifyFragmentShaderString]) {
        smoothDegreeUniform = [filterProgram uniformIndex:@"smoothDegree"];
    }
    self.intensity = 0.5;
    return self;
}

- (void)setIntensity:(CGFloat)intensity {
    _intensity = intensity;
    [self setFloat:intensity forUniform:smoothDegreeUniform program:filterProgram];
}

@end

@implementation GPUImageBeautifyFilter

- (id)init;
{
    if (!(self = [super init]))
    {
        return nil;
    }

    // First pass: face smoothing filter
    bilateralFilter = [[GPUImageBilateralFilter alloc] init];
    bilateralFilter.distanceNormalizationFactor = 4.0;
    [self addFilter:bilateralFilter];

    // Second pass: edge detection
    cannyEdgeFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
    [self addFilter:cannyEdgeFilter];

    // Third pass: combination bilateral, edge detection and origin
    combinationFilter = [[GPUImageCombinationFilter alloc] init];
    [self addFilter:combinationFilter];

    // Adjust HSB
    hsbFilter = [[GPUImageHSBFilter alloc] init];
    [hsbFilter adjustBrightness:1.1];
    [hsbFilter adjustSaturation:1.1];

    [bilateralFilter addTarget:combinationFilter];
    [cannyEdgeFilter addTarget:combinationFilter];

    [combinationFilter addTarget:hsbFilter];

    self.initialFilters = [NSArray arrayWithObjects:bilateralFilter,cannyEdgeFilter,combinationFilter,nil];
    self.terminalFilter = hsbFilter;

    return self;
}


#pragma mark 绘制第一个纹理

- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
    for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters)
    {
        if (currentFilter != self.inputFilterToIgnoreForUpdates)
        {
            if (currentFilter == combinationFilter) {
                textureIndex = 2;
            }
            [currentFilter newFrameReadyAtTime:frameTime atIndex:textureIndex];
        }
    }
}

- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
{
    for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters)
    {
        if (currentFilter == combinationFilter) {
            textureIndex = 2;
        }
        [currentFilter setInputFramebuffer:newInputFramebuffer atIndex:textureIndex];
    }
}

@end
posted @ 2018-04-25 14:08  sundaysios  阅读(428)  评论(0编辑  收藏  举报