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>