回顾
-
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的响应链有了更清晰的认识。
网友评论
如果优化人脸识别,查看了很多资料,主要就是一个意思,计算速度变快了
和
- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex没必要重写
super处理以后加一句
[combinationFilter newFrameReadyAtTime:frameTime atIndex:2];即可
// modify this frame in this callback
@required
- (void)onFrameAvailable:(unsigned char *)y ubuf:(unsigned char *)u vbuf:(unsigned char *)v ystride:(int)ystride ustride:(int)ustride vstride:(int)vstride width:(int)width height:(int)height;
@EnD
@interface YuvPreProcessor : NSObject
@property (nonatomic, weak) id<YuvPreProcessorProtocol> delegate;
// enable preprocessor
- (void)turnOn;
// disable preprocessor
- (void)turnOff;
@EnD
我用的是第三方的 SDK,他们给了这样一个处理图像的接口,我应该怎么做才能借助 GPUImage 来给这个接口添加美颜功能呢?
[bilateralFilter addTarget:combinationFilter];
[cannyEdgeFilter addTarget:combinationFilter];
这又是什么意思啊
[_emptyFilter removeAllTargets];
[_filter removeAllTargets];
[_videoCamera removeAllTargets];
[_beau removeAllTargets];
_filter = [[GPUImageUnsharpMaskFilter alloc] init];
_beau = [[LFGPUImageBeautyFilter alloc] init];
_emptyFilter = [[GPUImageColorInvertFilter alloc]init];
[_videoCamera addTarget:_filter];
[_filter addTarget:_beau];
[_beau addTarget:_emptyFilter];
[_emptyFilter addTarget:_gpuImageView];//_gpuImageView为一个 GPUImageView 用于显示
__weak typeof(self) _self = self;
[_emptyFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
[_self processVideo:output];
}];
上面是简化后的代码.如果想要定制滤镜实现实时滤镜...在电脑上使用 VLC 播放器播放的时候正常播放 但是如果代码换成
[_testfilter removeAllTargets];
[_videoCamera removeAllTargets];
_testfilter = [[GPUImageColorInvertFilter alloc]init];
__weak typeof(self) _self = self;
[_filter forceProcessingAtSize:_configuration.videoSize];
[_testfilter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
[_self processVideo:output];
}];
[_videoCamera addTarget:_testfilter];
[_testfilter addTarget:_gpuImageView];
如果用下面的代码...播放能播放.滤镜好像也加上了 .但是会闪屏. 好像不是每一帧的数据都加上了滤镜效果,请问这可能是什么原因???
- (CVPixelBufferRef)cameraStreamingSession:(PLCameraStreamingSession *)session cameraSourceDidGetPixelBuffer:(CVPixelBufferRef)pixelBuffer; 这个CVPixelBufferRef 类型的数据要怎么操作加滤镜呢,我用coreImage 可以做,但是我想用GPUImage 然后就傻了,做不好了