基于GPUImage的美颜BeautifyFace详细注释

GPUImageBeautifyFilter.h文件中

#import <GPUImage/GPUImage.h>
@class GPUImageCombinationFilter;
@interface GPUImageBeautifyFilter : GPUImageFilterGroup //继承于图像滤镜组

{
    GPUImageBilateralFilter *bilateralFilter; //双边模糊(磨皮)滤镜--继承于高斯模糊滤镜GPUImageGaussianBlurFilter
    GPUImageCannyEdgeDetectionFilter *cannyEdgeFilter;//Canny边缘检测算法滤镜--继承于图像滤镜组GPUImageFilterGroup
    GPUImageHSBFilter *hsbFilter;//HSB颜色滤镜--继承于颜色矩阵滤镜GPUImageColorMatrixFilter
    GPUImageCombinationFilter *combinationFilter;//滤镜的组合---继承于三输入滤镜GPUImageThreeInputFilter
}

@end

GPUImageBeautifyFilter.m文件中

#import "GPUImageBeautifyFilter.h"
/***************************************************/
// Internal CombinationFilter(It should not be used outside)
@interface GPUImageCombinationFilter : GPUImageThreeInputFilter//继承于三输入的滤镜
{
    GLint smoothDegreeUniform;//全局磨皮参数(平滑程度)
}

@property (nonatomic, assign) CGFloat intensity;

@end


/***********************************************/
//自定义的Shader着色器代码
//Shader出现在OpenGL ES 2.0中,允许创建自己的Shader。必须同时创建两个Shader,分别是Vertex shader(顶点着色器)和Fragment shader(片段着色器).//www.greatytc.com/p/8687a040eb48

//Varyings:用来在Vertex shader和Fragment shader之间传递信息的,比如在Vertex shader中写入varying值,然后就可以在Fragment shader中读取和处理
//Uniforms:在渲染循环里作为不变的输入值
//vec2:两个浮点数,适合在Fragment shader中保存X和Y坐标的情况
//vec4:四个浮点数,在图像处理中持续追踪每个像素的R,G,V,A这四个值。
//highp:属性负责变量精度,这个被加入可以提高效率
//smpler2D:接收一个图片的引用,当做2D的纹理。


//根据这个字符串创建Shader
NSString *const kGPUImageBeautifyFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;//纹理坐标1
 varying highp vec2 textureCoordinate2;//纹理坐标2
 varying highp vec2 textureCoordinate3;//纹理坐标3
 
 uniform sampler2D inputImageTexture;//输入图像纹理1
 uniform sampler2D inputImageTexture2;//输入图像纹理2
 uniform sampler2D inputImageTexture3;//输入图像纹理3
 
 uniform mediump float smoothDegree;//平滑度
 
 void main()
 {
     highp vec4 bilateral = texture2D(inputImageTexture, textureCoordinate);//双边模糊的2D纹理
     highp vec4 canny = texture2D(inputImageTexture2, textureCoordinate2);//边缘检测的2D纹理
     highp vec4 origin = texture2D(inputImageTexture3,textureCoordinate3);//原始图像的2D纹理
     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 //组合滤镜
//Combination  Filter是我们自己定义的三输入的滤波器。三个输入分别是原图像A(x, y),双边滤波后的图像B(x, y),边缘图像C(x, y)。其中A,B,C可以看成是图像矩阵,(x,y)可以看成其中某一像素的坐标。

- (id)init {
    //Combination Filter根据kGPUImageBeautifyFragmentShaderString创建自定义的Shader.
    //在自定义的Shader中对三个输入进行处理(双边模糊的2D纹理,边缘检测的2D纹理,原始图像的2D纹理),见上面Shader代码
    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//美颜滤镜
-(instancetype)init {
    if (!(self = [super init])) {
        return nil;
    }
    //1.双边模糊
    bilateralFilter = [[GPUImageBilateralFilter alloc] init];
    bilateralFilter.distanceNormalizationFactor = 4.0;
    [self addFilter:bilateralFilter];
    //2.边缘探测
    cannyEdgeFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
    [self addFilter:cannyEdgeFilter];
    //3.合并
    combinationFilter = [[GPUImageCombinationFilter alloc] init];
    [self addFilter:combinationFilter];
    //4.调整HSB
    hsbFilter = [[GPUImageHSBFilter alloc] init];
    [hsbFilter adjustBrightness:1.1];//亮度
    [hsbFilter adjustSaturation:1.1];//饱和度
    
    //双边模糊完成后,输出到组合滤镜
    [bilateralFilter addTarget:combinationFilter];
    //边缘探测完成后,输出到组合滤镜
    [cannyEdgeFilter addTarget:combinationFilter];
    //组合滤镜处理完成后,输出到hsb滤镜
    [combinationFilter addTarget:hsbFilter];
    
    //初始滤镜组
    self.initialFilters = [NSArray arrayWithObjects:bilateralFilter,cannyEdgeFilter,combinationFilter, nil];
    //最终处理的滤镜
    self.terminalFilter = hsbFilter;
    return self;
}
#pragma mark GPUImageInput protocol

-(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 != self.inputFilterToIgnoreForUpdates) {
            if (currentFilter == combinationFilter) {
                textureIndex = 2;
            }
            [currentFilter setInputFramebuffer:newInputFramebuffer atIndex:textureIndex];
        }

    }
}
@end

/*
 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上。
*/

如何使用这个美颜工具类?
在自己的控制器中ViewController.m

#import "ViewController.h"
#import <GPUImage/GPUImage.h>
#import "GPUImageBeautifyFilter.h"
#import <Masonry/Masonry.h>

@interface ViewController ()
@property (strong, nonatomic) GPUImageVideoCamera *videoCamera;//视频相机对象
@property (strong, nonatomic) GPUImageView *filterView;//实时预览的view,GPUImageView是响应链的终点,一般用于显示GPUImage的图像。
@property (weak, nonatomic) UIButton *beautifyButton;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //相机
    self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionFront];
    self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    self.videoCamera.horizontallyMirrorRearFacingCamera = YES;
    
    //预览层
    self.filterView = [[GPUImageView alloc] initWithFrame:self.view.frame];
    self.filterView.center = self.view.center;
    [self.view addSubview:self.filterView];
    //添加滤镜到相机
    [self.videoCamera addTarget:self.filterView];
    [self.videoCamera startCameraCapture];
    //设置按钮
    UIButton *beautifyBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    self.beautifyButton = beautifyBtn;
    [self.view addSubview:beautifyBtn];
    self.beautifyButton.backgroundColor = [UIColor whiteColor];
    [self.beautifyButton setTitle:@"开启" forState:UIControlStateNormal];
    [self.beautifyButton setTitle:@"关闭" forState:UIControlStateSelected];
    [self.beautifyButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [self.beautifyButton addTarget:self action:@selector(beautify) forControlEvents:UIControlEventTouchUpInside];
    beautifyBtn.frame = CGRectMake(100, 20, 100, 40);
}
- (void)beautify {
    if (self.beautifyButton.selected) {//如果已经开启了美颜,则
        self.beautifyButton.selected = NO;
        [self.videoCamera removeAllTargets];//移除原有的
        [self.videoCamera addTarget:self.filterView];//添加普通预览层
    } else {//如果没有开启美颜
        self.beautifyButton.selected = YES;
        [self.videoCamera removeAllTargets];//移除原有的
        GPUImageBeautifyFilter *beautifyFilter = [[GPUImageBeautifyFilter alloc] init];
        [self.videoCamera addTarget:beautifyFilter];//添加美颜滤镜层
        [beautifyFilter addTarget:self.filterView];//美颜后再输出到预览层
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

项目代码https://github.com/XanderXu/BeautifyFaceTest

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容

  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    passiontim阅读 15,412评论 2 45
  • 三分钟热度是很多人的通病,尤其是学习时,总会有这样或那样的理由让自己放弃,有内因和外因,我们的图选取了六项进行说明...
    文魁大脑王峰阅读 1,224评论 2 3
  • 【连载】《阡陌》目录 【连载】《阡陌》(五) 秦罗敷端详这镜子里的自己,她其实已经发现了自己的病态,她也学着克制自...
    于昰阅读 387评论 0 2
  • 经历了四个月的闭关学习,参加完准备已久的考试,突然放松下来 ,有时间整理一下思路。这近半年的时间里...
    终点相遇阅读 314评论 0 1
  • 每天写一位 1 首席闺蜜W小姐 W小姐 生于北方 长于北方 真正的女神 性子除了北方人自带的豪迈洒脱外 又多了一点...
    鹿小丢阅读 77评论 0 0