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
複製程式碼