iOS开发直播app-美颜滤镜GPUImageBeautifyFilter
natasa
8年前
<p>随着各种各样的直播app的爆火,实时美颜滤镜的需求也越来越多。下面将主要介绍实现美颜滤镜的原理和思路,原理可以移步看下GPUImage原理,本文主要是GPUImageBeautifyFilter美颜滤镜的实现。美颜只是不同滤镜组合起来的效果,实际上美颜也是一种滤镜,只不过它组合了各种需求的滤镜,例如磨皮、美白、提高饱和度、提亮之类的。</p> <h2>GPUImageBeautifyFilter</h2> <p>GPUImageBeautifyFilter是基于GPUImage的实时美颜滤镜,包括</p> <p>GPUImageBilateralFilter、 <strong>GPUImageCombinationFilter</strong> 、 <strong>GPUImageHSBFilter</strong> 。</p> <p>GPUImageBeautifyFilter.h创建上面的对象</p> <pre> <code class="language-objectivec">@interface GPUImageBeautifyFilter : GPUImageFilterGroup { GPUImageBilateralFilter *bilateralFilter; GPUImageCannyEdgeDetectionFilter *cannyEdgeFilter; GPUImageCombinationFilter *combinationFilter; GPUImageHSBFilter *hsbFilter; }</code></pre> <p>绘制步骤如下:</p> <p>准备纹理</p> <p>绘制纹理</p> <p>显示处理后的纹理</p> <h3><strong>一、 准备纹理(将要用到的类)</strong></h3> <p>[GPUImageVideoCamera] -</p> <p>[GPUImageBeautifyFilter] -</p> <p>[GPUImageBilateralFliter] -</p> <p>[GPUImageCombinationFilter] -</p> <p>[GPUImageCannyEdgeDetectionFilter] -</p> <p>准备过程分三步:</p> <p>第一个纹理:</p> <p>1、GPUImageVideoCamera捕获摄像头图像</p> <p>调用newFrameReadyAtTime: atIndex:通知GPUImageBeautifyFilter;</p> <p>2、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex:</p> <p>通知GPUImageBilateralFliter输入纹理已经准备好;</p> <p>第二个纹理:</p> <p>3、GPUImageBilateralFliter 绘制图像后,</p> <p>informTargetsAboutNewFrameAtTime(),</p> <p>调用setInputFramebufferForTarget: atIndex:</p> <p>把绘制的图像设置为GPUImageCombinationFilter输入纹理,</p> <p>并通知GPUImageCombinationFilter纹理已经绘制完毕;</p> <p>4、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex:</p> <p>通知 GPUImageCannyEdgeDetectionFilter输入纹理已经准备好;</p> <p>第三个纹理:</p> <p>5、GPUImageCannyEdgeDetectionFilter 绘制图像后,</p> <p>把图像设置为GPUImageCombinationFilter输入纹理;</p> <p>6、GPUImageBeautifyFilter调用newFrameReadyAtTime: atIndex:</p> <p>通知 GPUImageCombinationFilter输入纹理已经准备好;</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/9c1de4eb2f938e32205d64eb4ace2117.png"></p> <p>纹理准备.png</p> <h3><strong>二、绘制纹理:</strong></h3> <p>7、判断纹理数量</p> <p>GPUImageCombinationFilter判断是否有三个纹理,三个纹理都已经准备好后</p> <p>调用GPUImageThreeInputFilter的绘制函数renderToTextureWithVertices: textureCoordinates:,</p> <p>图像绘制完后,把图像设置为GPUImageHSBFilter的输入纹理,</p> <p>通知GPUImageHSBFilter纹理已经绘制完毕;</p> <p>8、绘制纹理</p> <p>GPUImageHSBFilter调用renderToTextureWithVertices:</p> <p>textureCoordinates:绘制图像,</p> <p>完成后把图像设置为GPUImageView的输入纹理,并通知GPUImageView输入纹理已经绘制完毕;</p> <h3><strong>三、显示纹理</strong></h3> <p>9、GPUImageView把输入纹理绘制到自己的帧缓存,然后通过</p> <p>[self.context presentRenderbuffer:GL_RENDERBUFFER];显示到UIView上。</p> <p>GPUImageBeautifyFilter.m文件</p> <pre> <code class="language-objectivec">@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 - GPUImageInput protocol</code></pre> <p>绘制纹理</p> <pre> <code class="language-objectivec">-(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]; } } }</code></pre> <p>设置绘制图像的输入纹理</p> <pre> <code class="language-objectivec">-(void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex; { for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters) { if (currentFilter == combinationFilter) { textureIndex = 2; } [currentFilter setInputFramebuffer:newInputFramebuffer atIndex:textureIndex]; } }</code></pre> <p>GPUImage集成步骤:</p> <p><strong>自定义组合滤镜美颜</strong></p> <ol> <li>使用Cocoapods导入GPUImage;</li> <li>创建视频源GPUImageVideoCamera;</li> <li>创建最终目的源:GPUImageView;</li> <li>创建GPUImageFilterGroup滤镜组合,需要组合亮度(GPUImageBrightnessFilter)和双边滤波(GPUImageBilateralFilter)这两个滤镜达到美颜效果;</li> <li>设置滤镜组链;</li> <li>设置GPUImage处理链,从数据源 -> 滤镜 -> 最终界面效果;</li> <li>开始采集视频。</li> </ol> <pre> <code class="language-objectivec">-(void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor whiteColor]; self.title = @"GPUImage美颜"; [self initBottomView]; // 1. 创建视频摄像头 // SessionPreset:屏幕分辨率,AVCaptureSessionPresetHigh会自适应高分辨率 // cameraPosition:摄像头方向 // 最好使用AVCaptureSessionPresetHigh,会自动识别,如果用太高分辨率,当前设备不支持会直接报错 GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetHigh cameraPosition:AVCaptureDevicePositionFront]; // 2. 设置摄像头输出视频的方向 videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; _videoCamera = videoCamera; // 3. 创建用于展示视频的GPUImageView GPUImageView *captureVideoPreview = [[GPUImageView alloc] initWithFrame:self.view.bounds]; [self.view insertSubview:captureVideoPreview atIndex:0]; // 4.创建磨皮、美白组合滤镜 GPUImageFilterGroup *groupFliter = [[GPUImageFilterGroup alloc] init]; // 5.磨皮滤镜 GPUImageBilateralFilter *bilateralFilter = [[GPUImageBilateralFilter alloc] init]; [groupFliter addFilter:bilateralFilter]; _bilateralFilter = bilateralFilter; // 6.美白滤镜 GPUImageBrightnessFilter *brightnessFilter = [[GPUImageBrightnessFilter alloc] init]; [groupFliter addFilter:brightnessFilter]; _brightnessFilter = brightnessFilter; // 7.设置滤镜组链 [bilateralFilter addTarget:brightnessFilter]; [groupFliter setInitialFilters:@[bilateralFilter]]; groupFliter.terminalFilter = brightnessFilter; // 8.设置GPUImage处理链 从数据源->滤镜->界面展示 [videoCamera addTarget:groupFliter]; [groupFliter addTarget:captureVideoPreview]; // 9.调用startCameraCapture采集视频,底层会把采集到的视频源,渲染到GPUImageView上,接着界面显示 [videoCamera startCameraCapture]; }</code></pre> <p><strong>ps:</strong></p> <ol> <li>GPUImageVideoCamera必须要强引用,否则在采集视频过程中会被销毁;</li> <li>必须调用startCameraCapture,底层才会把采集到的视频源,渲染到GPUImageView中才能显示;</li> <li>GPUImageBilateralFilter的distanceNormalizationFactor值越小,磨皮效果越好,distanceNormalizationFactor取值范围: 大于1。</li> </ol> <p><strong>利用美颜滤镜GPUImageBeautifyFilter实现</strong></p> <p>1、使用Cocoapods导入GPUImage;</p> <p>2、导入GPUImageBeautifyFilter文件夹;</p> <p>3、创建视频源GPUImageVideoCamera;</p> <p>4、创建最终目的源:GPUImageView;</p> <p>5、创建最终美颜滤镜:GPUImageBeautifyFilter;</p> <p>6、设置GPUImage处理链,从数据源 -> 滤镜 -> 最终界面展示。</p> <pre> <code class="language-objectivec">-(void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor whiteColor]; self.title = @"Beautify美颜"; UISwitch *switcher = [[UISwitch alloc] initWithFrame:CGRectMake(140, 80, 70, 30)]; [switcher addTarget:self action:@selector(changeBeautyFilter:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:switcher]; // 1. 创建视频摄像头 // SessionPreset:屏幕分辨率,AVCaptureSessionPresetHigh会自适应高分辨率 // cameraPosition:摄像头方向 // 最好使用AVCaptureSessionPresetHigh,会自动识别,如果用太高分辨率,当前设备不支持会直接报错 GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetHigh cameraPosition:AVCaptureDevicePositionFront]; // 2. 设置摄像头输出视频的方向 videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; _videoCamera = videoCamera; // 3. 创建用于展示视频的GPUImageView GPUImageView *captureVideoPreview = [[GPUImageView alloc] initWithFrame:self.view.bounds]; [self.view insertSubview:captureVideoPreview atIndex:0]; _captureVideoPreview = captureVideoPreview; // 4.设置处理链 [_videoCamera addTarget:_captureVideoPreview]; // 5.调用startCameraCapture采集视频,底层会把采集到的视频源,渲染到GPUImageView上,接着界面显示 [videoCamera startCameraCapture]; }</code></pre> <p>切换美颜的时候要移动处理链</p> <pre> <code class="language-objectivec">// 移除之前所有处理链 [_videoCamera removeAllTargets]; // 创建美颜滤镜 GPUImageBeautifyFilter *beautifyFilter = [[GPUImageBeautifyFilter alloc] init]; // 设置GPUImage处理链,从数据源 => 滤镜 => 最终界面效果 [_videoCamera addTarget:beautifyFilter]; [beautifyFilter addTarget:_captureVideoPreview];</code></pre> <p>参考文献</p> <p><a href="/misc/goto?guid=4959728078012877785" rel="nofollow,noindex">http://www.jianshu.com/p/2ce9b63ecfef</a></p> <p><a href="/misc/goto?guid=4959728078111114653" rel="nofollow,noindex">http://www.jianshu.com/p/4646894245ba</a></p> <p> </p> <p>来自:http://www.jianshu.com/p/6bdb4cb50f14</p> <p> </p>