基於GPUImage的美顏BeautifyFace詳細註釋

史前圖騰發表於2017-12-13

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(片段著色器).http://www.jianshu.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
複製程式碼

專案程式碼github.com/XanderXu/Be…

相關文章