iOS GPUImage实时美颜滤镜
2018-10-09 本文已影响68人
FPA有缘自续
1.背景
前段时间由于项目需求,做了一个基于GPUImage的美颜+滤镜相机。现在各种各样的直播、小视频App层出不穷,美颜滤镜的需求也越来越多。为了回馈开源,现在我把它放到了GitHub上面(就上传了精华部分),感兴趣的朋友可以去下载。下面将主要介绍实现美颜滤镜的原理和思路。
2. GPUImage
GPUImage是一个开源的基于GPU的图片或视频的处理框架,其本身内置了多达120多种常见的滤镜效果。有了它,添加实时的滤镜只需要简单地添加几行代码。
- GPUImageFilter 就是用来接收源图像,通过自定义的顶点、片元着色器来渲染新的图像,并在绘制完成后通知响应链的下一个对象。
- GPUImageFramebuffer 就是用来管理纹理缓存的格式与读写帧缓存的buffer。
- GPUImageVideoCamera 是GPUImageOutput的子类,提供来自摄像头的图像数据作为源数据,一般是响应链的源头。
- GPUImageView 是响应链的终点,一般用于显示GPUImage的图像。
下面的例子是以摄像头的数据为源,设置一个混合滤镜
摄像头开启捕获代码块
// 设置GPUImage处理链 从数据源->滤镜->界面展示
self.movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:[NSURL fileURLWithPath:self.moviePath] size:CGSizeMake(kCameraWidth, kCameraWidth) fileType:AVFileTypeQuickTimeMovie outputSettings:self.videoSettings];
self.videoCamera.audioEncodingTarget = _movieWriter;
// 这边是初始化一个美颜+美白+可爱的一个混合滤镜
self.normalFilter = [[GPUImageFilterGroup alloc] init];
//默认美颜 美白
self.beautyLevel = [DSFilterInfo getBeautyValue];
self.brightLevel = [DSFilterInfo getBrightValue];
[self addGPUImageFilter:[[DSGPUImageBeautyFilter alloc] initCustomFilterWithBeautyValue:self.beautyLevel brightValue:self.brightLevel]];
//默认添加可爱滤镜
GPUImageOutput<GPUImageInput> *filter = [[DSFilterHandleTool sharedInstance] getFilterWithfilterType:LZBFilterType_Beauty];
if (filter) [self addGPUImageFilter:filter];
//默认美颜
[self.videoCamera addTarget:self.normalFilter];
[self.normalFilter addTarget:self];
// 调用startCameraCapture采集视频,底层会把采集到的视频源,渲染到GPUImageView上,接着界面显示 (放在主线程初始化很卡)
kDISPATCH_GLOBAL_QUEUE_DEFAULT(^{[self.videoCamera startCameraCapture]; });
GPUImage提供了拍照的方法
代码块
[self.videoCamera capturePhotoAsImageProcessedUpToFilter:self.normalFilter
withCompletionHandler:^(UIImage *processedImage, NSError *error) {
}];
视频开启拍摄和结束拍摄方法
代码块
//开始视频录制
- (void)recordStartHandler {
//设定视频录制方向
self.currentMovieDirection = self.currentDirection;
// 如果已经存在文件,AVAssetWriter会有异常,删除旧文件
unlink([self.moviePath UTF8String]);
//滤镜加到writer
[self.normalFilter addTarget:self.movieWriter];
//开始录制
[self.movieWriter startRecording];
}
//结束视频录制
- (void)recordFinishHandler:(void(^)(UIImage *firstVideoImage))handler {
//移除target
[self.normalFilter removeTarget:self.movieWriter];
@weakify(self);
[self.movieWriter finishRecordingWithCompletionHandler:^{
@strongify(self);
[self createNewWritter];
// 此时视频文件已经在对应的路径下哦~
}];
}
GPUImage填坑
视频拍摄第一帧黑屏
- (void)createNewWritter {
self.movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:[NSURL fileURLWithPath:self.moviePath] size:CGSizeMake(kCameraWidth, kCameraWidth) fileType:AVFileTypeQuickTimeMovie outputSettings:self.videoSettings];
/// 如果不加上这一句,会出现第一帧闪现黑屏
[_videoCamera addAudioInputsAndOutputs];
_videoCamera.audioEncodingTarget = _movieWriter;
}
新增需求
由于boss随口一说 本次美颜滤镜相机还增加了聚焦、曝光操作以及仿苹果原生相机横屏拍摄
下面是涉及这些功能的精华代码
聚焦、曝光(在view上增加一个点击手势即可)
- (void)focusTap:(UITapGestureRecognizer *)tap {
// self.cameraView.userInteractionEnabled = NO;
CGPoint touchPoint = [tap locationInView:tap.view];
[self layerAnimationWithPoint:touchPoint];
touchPoint = CGPointMake(touchPoint.x / tap.view.bounds.size.width, touchPoint.y / tap.view.bounds.size.height);
/*以下是相机的聚焦和曝光设置,前置不支持聚焦但是可以曝光处理,后置相机两者都支持,下面的方法是通过点击一个点同时设置聚焦和曝光,当然根据需要也可以分开进行处理
*/
if ([self.videoCamera.inputCamera isExposurePointOfInterestSupported] && [self.videoCamera.inputCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
NSError *error;
if ([self.videoCamera.inputCamera lockForConfiguration:&error]) {
[self.videoCamera.inputCamera setExposurePointOfInterest:touchPoint];
[self.videoCamera.inputCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
if([self.videoCamera.inputCamera isFocusPointOfInterestSupported] && [self.videoCamera.inputCamera isFocusModeSupported:AVCaptureFocusModeAutoFocus])
{
[self.videoCamera.inputCamera setFocusPointOfInterest:touchPoint];
[self.videoCamera.inputCamera setFocusMode:AVCaptureFocusModeAutoFocus];
}
[self.videoCamera.inputCamera unlockForConfiguration];
} else {
NSLog(@"ERROR = %@", error);
}
}
}
横屏拍摄(其实根据拍摄的第一帧判断时候为横屏 涉及工具 加速仪)
/** 开启加速仪(方向检测)
在按下快门的那一刻记录当前帧的方向
然后在视频拍摄完成后根据第一帧方向对视频进行处理 判断是否需要旋转
*/
- (void)startDeviceMotion {
if (![self.motionManager isDeviceMotionAvailable]) {return;}
[self.motionManager setDeviceMotionUpdateInterval:1.f];
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
//Gravity 获取手机的重力值在各个方向上的分量
double x = motion.gravity.x;
double y = motion.gravity.y;
double z = motion.gravity.z;
self.currentDirection = UIImageOrientationUp;
if (fabs(z) < 0.5) {
if (fabs(y)>=fabs(x)) {
if (y >= 0) self.currentDirection = UIImageOrientationDown;
}
else {
if (x >= 0) self.currentDirection = UIImageOrientationRight;
else self.currentDirection = UIImageOrientationLeft;
}
}
}];
}