GPUImage之美颜滤镜
标签:
gpuimage |
分类: 多媒体/FFMPEG/协议 |
GPUImageFilter
就是用来接收源图像,通过自定义的顶点、片元着色器来渲染新的图像,并在绘制完成后通知响应链的下一个对象。GPUImageFramebuffer
就是用来管理纹理缓存的格式与读写帧缓存的buffer。GPUImageVideoCamera
是GPUImageOutput
的子类,提供来自摄像头的图像数据作为源数据,一般是响应链的源头。GPUImageView
是响应链的终点,一般用于显示GPUImage的图像。
琨君的基于GPUImage的实时美颜滤镜对GPUImage实现美颜滤镜的原理和思路做了详细介绍。
本文以琨君的代码为demo,结合前两篇解析,探究美颜过程中的GPUImage实现。
GPUImage类介绍
1、GPUImageFilterGroup
GPUImageFilterGroup
是多个filter的集合,terminalFilter
为最终的filter,initialFilters
为filter数组。GPUImageFilterGroup
本身不绘制图像,对GPUImageFilterGroup
添加删除Target操作的操作都会转为terminalFilter
的操作。
2、GPUImageTwoInputFilter
GPUImageTwoInputFilter
是GPUImageFilter
的子类,对两个输入纹理进行通用的处理,需要继承它并准备自己的片元着色器。
两个输入纹理默认为inputImageTexture
和inputImageTexture2
。
-
重写了下面的函数,修改
GPUImageFilter
绘制的逻辑。- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
下面这部分是核心的绘制逻辑:
glActiveTexture()
是选择纹理单元,glBindTexture()
是把纹理单元和firstInputFramebuffer
、secondInputFramebuffer
管理的纹理内存绑定。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
设置firstInputFramebuffer
和secondInputFramebuffer
。如果是textureIndex = 0
,设置hasSetFirstTexture
表示已经设置第一个纹理。
3、GPUImageThreeInputFilter
GPUImageThreeInputFilter
的逻辑与GPUImageTwoInputFilter
类似,增加了thirdInputFramebuffer
作为第三个纹理inputImageTexture3
的输入。
4、GPUImageBeautifyFilter
GPUImageBeautifyFilter
是基于GPUImage的实时美颜滤镜中的美颜滤镜,包括GPUImageBilateralFilter
、GPUImageCannyEdgeDetectionFilter
、GPUImageCombinationFilter
、GPUImageHSBFilter
。
绘制流程
-
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的响应链有了更清晰的认识。
原文链接:http://www.jianshu.com/p/2ce9b63ecfef
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”
GPUImage--美颜滤镜GPUImageBeautifyFilter
版权声明:转载请标注原文地址。邮箱Quinn_F@126.com https://blog.csdn.net/Xoxo_x/article/details/52743107
关于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
绘制流程分为:
- 准备纹理
- 绘制纹理
- 显示处理后的纹理
一、 准备纹理:这里用到的类
[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