基于GPUImage的美顏BeautifyFace詳細(xì)注釋

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;//全局磨皮參數(shù)(平滑程度)
}

@property (nonatomic, assign) CGFloat intensity;

@end


/***********************************************/
//自定義的Shader著色器代碼
//Shader出現(xiàn)在OpenGL ES 2.0中踏施,允許創(chuàng)建自己的Shader炕倘。必須同時創(chuàng)建兩個Shader,分別是Vertex shader(頂點著色器)和Fragment shader(片段著色器).http://www.reibang.com/p/8687a040eb48

//Varyings:用來在Vertex shader和Fragment shader之間傳遞信息的躺坟,比如在Vertex shader中寫入varying值属桦,然后就可以在Fragment shader中讀取和處理
//Uniforms:在渲染循環(huán)里作為不變的輸入值
//vec2:兩個浮點數(shù)熊痴,適合在Fragment shader中保存X和Y坐標(biāo)的情況
//vec4:四個浮點數(shù),在圖像處理中持續(xù)追蹤每個像素的R,G,V,A這四個值聂宾。
//highp:屬性負(fù)責(zé)變量精度果善,這個被加入可以提高效率
//smpler2D:接收一個圖片的引用,當(dāng)做2D的紋理系谐。


//根據(jù)這個字符串創(chuàng)建Shader
NSString *const kGPUImageBeautifyFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;//紋理坐標(biāo)1
 varying highp vec2 textureCoordinate2;//紋理坐標(biāo)2
 varying highp vec2 textureCoordinate3;//紋理坐標(biāo)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;
     //判斷是不是邊緣,是不是皮膚.通過膚色檢測和邊緣檢測巾陕,只對皮膚和非邊緣部分進(jìn)行處理。
     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)可以看成其中某一像素的坐標(biāo)梯刚。

- (id)init {
    //Combination Filter根據(jù)kGPUImageBeautifyFragmentShaderString創(chuàng)建自定義的Shader.
    //在自定義的Shader中對三個輸入進(jìn)行處理(雙邊模糊的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.調(diào)整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捕獲攝像頭圖像
 調(diào)用newFrameReadyAtTime: atIndex:通知GPUImageBeautifyFilter;
 
 2句喜、GPUImageBeautifyFilter調(diào)用newFrameReadyAtTime: atIndex:
 通知GPUImageBilateralFliter輸入紋理已經(jīng)準(zhǔn)備好;
 
 3沟于、GPUImageBilateralFliter 繪制圖像后在informTargetsAboutNewFrameAtTime()咳胃,
 調(diào)用setInputFramebufferForTarget: atIndex:
 把繪制的圖像設(shè)置為GPUImageCombinationFilter輸入紋理旷太,
 并通知GPUImageCombinationFilter紋理已經(jīng)繪制完畢展懈;
 
 4、GPUImageBeautifyFilter調(diào)用newFrameReadyAtTime: atIndex:
 通知 GPUImageCannyEdgeDetectionFilter輸入紋理已經(jīng)準(zhǔn)備好供璧;
 
 5存崖、同3,GPUImageCannyEdgeDetectionFilter 繪制圖像后睡毒,
 把圖像設(shè)置為GPUImageCombinationFilter輸入紋理来惧;
 
 6、GPUImageBeautifyFilter調(diào)用newFrameReadyAtTime: atIndex:
 通知 GPUImageCombinationFilter輸入紋理已經(jīng)準(zhǔn)備好演顾;
 
 7供搀、GPUImageCombinationFilter判斷是否有三個紋理,三個紋理都已經(jīng)準(zhǔn)備好后
 調(diào)用GPUImageThreeInputFilter的繪制函數(shù)renderToTextureWithVertices: textureCoordinates:钠至,
 圖像繪制完后葛虐,把圖像設(shè)置為GPUImageHSBFilter的輸入紋理,
 通知GPUImageHSBFilter紋理已經(jīng)繪制完畢;
 
 8棉钧、GPUImageHSBFilter調(diào)用renderToTextureWithVertices: textureCoordinates:繪制圖像屿脐,
 完成后把圖像設(shè)置為GPUImageView的輸入紋理,并通知GPUImageView輸入紋理已經(jīng)繪制完畢宪卿;
 
 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;//視頻相機(jī)對象
@property (strong, nonatomic) GPUImageView *filterView;//實時預(yù)覽的view,GPUImageView是響應(yīng)鏈的終點奢驯,一般用于顯示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.
    //相機(jī)
    self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionFront];
    self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    self.videoCamera.horizontallyMirrorRearFacingCamera = YES;
    
    //預(yù)覽層
    self.filterView = [[GPUImageView alloc] initWithFrame:self.view.frame];
    self.filterView.center = self.view.center;
    [self.view addSubview:self.filterView];
    //添加濾鏡到相機(jī)
    [self.videoCamera addTarget:self.filterView];
    [self.videoCamera startCameraCapture];
    //設(shè)置按鈕
    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:@"關(guān)閉" 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) {//如果已經(jīng)開啟了美顏,則
        self.beautifyButton.selected = NO;
        [self.videoCamera removeAllTargets];//移除原有的
        [self.videoCamera addTarget:self.filterView];//添加普通預(yù)覽層
    } else {//如果沒有開啟美顏
        self.beautifyButton.selected = YES;
        [self.videoCamera removeAllTargets];//移除原有的
        GPUImageBeautifyFilter *beautifyFilter = [[GPUImageBeautifyFilter alloc] init];
        [self.videoCamera addTarget:beautifyFilter];//添加美顏濾鏡層
        [beautifyFilter addTarget:self.filterView];//美顏后再輸出到預(yù)覽層
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

項目代碼https://github.com/XanderXu/BeautifyFaceTest

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末次绘,一起剝皮案震驚了整個濱河市瘪阁,隨后出現(xiàn)的幾起案子朽缎,更是在濱河造成了極大的恐慌送淆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異斤寂,居然都是意外死亡硬鞍,警方通過查閱死者的電腦和手機(jī)力细,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門典蝌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人艇拍,你說我怎么就攤上這事狐蜕。” “怎么了卸夕?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵层释,是天一觀的道長。 經(jīng)常有香客問我快集,道長贡羔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任个初,我火速辦了婚禮乖寒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘院溺。我一直安慰自己楣嘁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布珍逸。 她就那樣靜靜地躺著马澈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弄息。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天勤婚,我揣著相機(jī)與錄音摹量,去河邊找鬼。 笑死馒胆,一個胖子當(dāng)著我的面吹牛缨称,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祝迂,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼睦尽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了型雳?” 一聲冷哼從身側(cè)響起当凡,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤山害,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沿量,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪慌,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年朴则,在試婚紗的時候發(fā)現(xiàn)自己被綠了权纤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡乌妒,死狀恐怖汹想,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撤蚊,我是刑警寧澤古掏,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站拴魄,受9級特大地震影響冗茸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匹中,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一夏漱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顶捷,春花似錦挂绰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至重虑,卻和暖如春践付,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缺厉。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工永高, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人提针。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓命爬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辐脖。 傳聞我的和親對象是個殘疾皇子饲宛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • afinalAfinal是一個android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,429評論 2 45
  • 三分鐘熱度是很多人的通病嗜价,尤其是學(xué)習(xí)時艇抠,總會有這樣或那樣的理由讓自己放棄幕庐,有內(nèi)因和外因,我們的圖選取了六項進(jìn)行說明...
    文魁大腦王峰閱讀 1,230評論 2 3
  • 【連載】《阡陌》目錄 【連載】《阡陌》(五) 秦羅敷端詳這鏡子里的自己练链,她其實已經(jīng)發(fā)現(xiàn)了自己的病態(tài)翔脱,她也學(xué)著克制自...
    于昰閱讀 389評論 0 2
  • 經(jīng)歷了四個月的閉關(guān)學(xué)習(xí),參加完準(zhǔn)備已久的考試媒鼓,突然放松下來 届吁,有時間整理一下思路。這近半年的時間里...
    終點相遇閱讀 318評論 0 1
  • 每天寫一位 1 首席閨蜜W小姐 W小姐 生于北方 長于北方 真正的女神 性子除了北方人自帶的豪邁灑脫外 又多了一點...
    鹿小丟閱讀 77評論 0 0