IOS使用GPUImage滤镜初级试水

GPUImage是基于GPU的图像和视频处理一个开源的iOS框架,可以处理图像,给图片和视频增加滤镜等,类似于美图秀秀,里面内置了多款默认的效果,如果想自定义效果,可以参考后面所写的参考文章列表

一、GPUImage的下载安装

GPUImage的github地址:https://github.com/BradLarson/GPUImage

安装教程作者在github上是这么说的,你可以按照他说的

首先,将GPUImage.xcodeproj文件拖动到应用程序的Xcode项目中,以将框架嵌入到项目中。接下来,转到应用程序的目标并将GPUImage添加为目标依赖关系。最后,您需要将libGPUImage.a库从GPUImage框架的Products文件夹拖动到应用程序目标中的Link Binary With Libraries构建阶段。

因为嫌拖工程文件麻烦,并且后面也需要使用cocoapod管理,所以就试了下生成库,发现也是可以使用的

一、1、进入framework文件夹,打开GPUImage.xcodeproj,然后分别用真机和模拟机生成一遍静态.a库,把两个库合并:《合并静态库

2、之后把这个静态库拖入项目工程之后把头文件拖入工程即可

二、如果不想生成静态库,也可以自己拖.m和.h文件,把Source文件夹下面除了Source/IOS/Framework下面的Info.plist文件和module.modulemap文件外,其他所有的.h文件和.m文件全部引入工程就行了

注意GPUImage需要的另外的系统库,需要引入工程

  • CoreMedia

  • CoreVideo

  • OpenGLES

  • AVFoundation

  • QuartzCore

二、GPUImage对图片的使用

对图片的滤镜,全部都是一个流程,不同的是滤镜不同和滤镜的参数不同

    //褐色滤镜
    GPUImageSepiaFilter *disFilter = [[GPUImageSepiaFilter alloc] init];
    
    //设置要渲染的区域
    [disFilter forceProcessingAtSize:self.myImg.size];
    [disFilter useNextFrameForImageCapture];
    
    //获取数据源
    GPUImagePicture *stillImageSource = [[GPUImagePicture alloc]initWithImage:self.myImg];
    
    //添加上滤镜
    [stillImageSource addTarget:disFilter];
    //开始渲染
    [stillImageSource processImage];
    //获取渲染后的图片
    UIImage *newImage = [disFilter imageFromCurrentFramebuffer];
    
    //加载出来
    UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];
    imageView.frame = CGRectMake(50,50,self.myImg.size.width-200 ,self.myImg.size.height-200);
    [self.view addSubview:imageView];

2.1、系统提供调节的滤镜

系统提供了多种默认可调的参数,感谢强大的网友总结

#import "GLProgram.h"

// Base classes

import "GPUImageOpenGLESContext.h"

import "GPUImageOutput.h"

import "GPUImageView.h"

import "GPUImageVideoCamera.h"

import "GPUImageStillCamera.h"

import "GPUImageMovie.h"

import "GPUImagePicture.h"

import "GPUImageRawDataInput.h"

import "GPUImageRawDataOutput.h"

import "GPUImageMovieWriter.h"

import "GPUImageFilterPipeline.h"

import "GPUImageTextureOutput.h"

import "GPUImageFilterGroup.h"

import "GPUImageTextureInput.h"

import "GPUImageUIElement.h"

import "GPUImageBuffer.h"

// Filters

import "GPUImageFilter.h"

import "GPUImageTwoInputFilter.h"

pragma mark - 调整颜色 Handle Color

import "GPUImageBrightnessFilter.h"                //亮度

import "GPUImageExposureFilter.h"                  //曝光

import "GPUImageContrastFilter.h"                  //对比度

import "GPUImageSaturationFilter.h"                //饱和度

import "GPUImageGammaFilter.h"                     //伽马线

import "GPUImageColorInvertFilter.h"               //反色

import "GPUImageSepiaFilter.h"                     //褐色(怀旧)

import "GPUImageLevelsFilter.h"                    //色阶

import "GPUImageGrayscaleFilter.h"                 //灰度

import "GPUImageHistogramFilter.h"                 //色彩直方图,显示在图片上

import "GPUImageHistogramGenerator.h"              //色彩直方图

import "GPUImageRGBFilter.h"                       //RGB

import "GPUImageToneCurveFilter.h"                 //色调曲线

import "GPUImageMonochromeFilter.h"                //单色

import "GPUImageOpacityFilter.h"                   //不透明度

import "GPUImageHighlightShadowFilter.h"           //提亮阴影

import "GPUImageFalseColorFilter.h"                //色彩替换(替换亮部和暗部色彩)

import "GPUImageHueFilter.h"                       //色度

import "GPUImageChromaKeyFilter.h"                 //色度键

import "GPUImageWhiteBalanceFilter.h"              //白平横

import "GPUImageAverageColor.h"                    //像素平均色值

import "GPUImageSolidColorGenerator.h"             //纯色

import "GPUImageLuminosity.h"                      //亮度平均

import "GPUImageAverageLuminanceThresholdFilter.h" //像素色值亮度平均,图像黑白(有类似漫画效果)

import "GPUImageLookupFilter.h"                    //lookup 色彩调整

import "GPUImageAmatorkaFilter.h"                  //Amatorka lookup

import "GPUImageMissEtikateFilter.h"               //MissEtikate lookup

import "GPUImageSoftEleganceFilter.h"              //SoftElegance lookup

pragma mark - 图像处理 Handle Image

import "GPUImageCrosshairGenerator.h"              //十字

import "GPUImageLineGenerator.h"                   //线条

import "GPUImageTransformFilter.h"                 //形状变化

import "GPUImageCropFilter.h"                      //剪裁

import "GPUImageSharpenFilter.h"                   //锐化

import "GPUImageUnsharpMaskFilter.h"               //反遮罩锐化

import "GPUImageFastBlurFilter.h"                  //模糊

import "GPUImageGaussianBlurFilter.h"              //高斯模糊

import "GPUImageGaussianSelectiveBlurFilter.h"     //高斯模糊,选择部分清晰

import "GPUImageBoxBlurFilter.h"                   //盒状模糊

import "GPUImageTiltShiftFilter.h"                 //条纹模糊,中间清晰,上下两端模糊

import "GPUImageMedianFilter.h"                    //中间值,有种稍微模糊边缘的效果

import "GPUImageBilateralFilter.h"                 //双边模糊

import "GPUImageErosionFilter.h"                   //侵蚀边缘模糊,变黑白

import "GPUImageRGBErosionFilter.h"                //RGB侵蚀边缘模糊,有色彩

import "GPUImageDilationFilter.h"                  //扩展边缘模糊,变黑白

import "GPUImageRGBDilationFilter.h"               //RGB扩展边缘模糊,有色彩

import "GPUImageOpeningFilter.h"                   //黑白色调模糊

import "GPUImageRGBOpeningFilter.h"                //彩色模糊

import "GPUImageClosingFilter.h"                   //黑白色调模糊,暗色会被提亮

import "GPUImageRGBClosingFilter.h"                //彩色模糊,暗色会被提亮

import "GPUImageLanczosResamplingFilter.h"         //Lanczos重取样,模糊效果

import "GPUImageNonMaximumSuppressionFilter.h"     //非最大抑制,只显示亮度最高的像素,其他为黑

import "GPUImageThresholdedNonMaximumSuppressionFilter.h" //与上相比,像素丢失更多

import "GPUImageSobelEdgeDetectionFilter.h"        //Sobel边缘检测算法(白边,黑内容,有点漫画的反色效果)

import "GPUImageCannyEdgeDetectionFilter.h"        //Canny边缘检测算法(比上更强烈的黑白对比度)

import "GPUImageThresholdEdgeDetectionFilter.h"    //阈值边缘检测(效果与上差别不大)

import "GPUImagePrewittEdgeDetectionFilter.h"      //普瑞维特(Prewitt)边缘检测(效果与Sobel差不多,貌似更平滑)

import "GPUImageXYDerivativeFilter.h"              //XYDerivative边缘检测,画面以蓝色为主,绿色为边缘,带彩色

import "GPUImageHarrisCornerDetectionFilter.h"     //Harris角点检测,会有绿色小十字显示在图片角点处

import "GPUImageNobleCornerDetectionFilter.h"      //Noble角点检测,检测点更多

import "GPUImageShiTomasiFeatureDetectionFilter.h" //ShiTomasi角点检测,与上差别不大

import "GPUImageMotionDetector.h"                  //动作检测

import "GPUImageHoughTransformLineDetector.h"      //线条检测

import "GPUImageParallelCoordinateLineTransformFilter.h" //平行线检测

import "GPUImageLocalBinaryPatternFilter.h"        //图像黑白化,并有大量噪点

import "GPUImageLowPassFilter.h"                   //用于图像加亮

import "GPUImageHighPassFilter.h"                  //图像低于某值时显示为黑

pragma mark - 视觉效果 Visual Effect

import "GPUImageSketchFilter.h"                    //素描

import "GPUImageThresholdSketchFilter.h"           //阀值素描,形成有噪点的素描

import "GPUImageToonFilter.h"                      //卡通效果(黑色粗线描边)

import "GPUImageSmoothToonFilter.h"                //相比上面的效果更细腻,上面是粗旷的画风

import "GPUImageKuwaharaFilter.h"                  //桑原(Kuwahara)滤波,水粉画的模糊效果;处理时间比较长,慎用

import "GPUImageMosaicFilter.h"                    //黑白马赛克

import "GPUImagePixellateFilter.h"                 //像素化

import "GPUImagePolarPixellateFilter.h"            //同心圆像素化

import "GPUImageCrosshatchFilter.h"                //交叉线阴影,形成黑白网状画面

import "GPUImageColorPackingFilter.h"              //色彩丢失,模糊(类似监控摄像效果)

import "GPUImageVignetteFilter.h"                  //晕影,形成黑色圆形边缘,突出中间图像的效果

import "GPUImageSwirlFilter.h"                     //漩涡,中间形成卷曲的画面

import "GPUImageBulgeDistortionFilter.h"           //凸起失真,鱼眼效果

import "GPUImagePinchDistortionFilter.h"           //收缩失真,凹面镜

import "GPUImageStretchDistortionFilter.h"         //伸展失真,哈哈镜

import "GPUImageGlassSphereFilter.h"               //水晶球效果

import "GPUImageSphereRefractionFilter.h"          //球形折射,图形倒立

    

import "GPUImagePosterizeFilter.h"                 //色调分离,形成噪点效果

import "GPUImageCGAColorspaceFilter.h"             //CGA色彩滤镜,形成黑、浅蓝、紫色块的画面

import "GPUImagePerlinNoiseFilter.h"               //柏林噪点,花边噪点

import "GPUImage3x3ConvolutionFilter.h"            //3x3卷积,高亮大色块变黑,加亮边缘、线条等

import "GPUImageEmbossFilter.h"                    //浮雕效果,带有点3d的感觉

import "GPUImagePolkaDotFilter.h"                  //像素圆点花样

import "GPUImageHalftoneFilter.h"                  //点染,图像黑白化,由黑点构成原图的大致图形

pragma mark - 混合模式 Blend

import "GPUImageMultiplyBlendFilter.h"             //通常用于创建阴影和深度效果

import "GPUImageNormalBlendFilter.h"               //正常

import "GPUImageAlphaBlendFilter.h"                //透明混合,通常用于在背景上应用前景的透明度

import "GPUImageDissolveBlendFilter.h"             //溶解

import "GPUImageOverlayBlendFilter.h"              //叠加,通常用于创建阴影效果

import "GPUImageDarkenBlendFilter.h"               //加深混合,通常用于重叠类型

import "GPUImageLightenBlendFilter.h"              //减淡混合,通常用于重叠类型

import "GPUImageSourceOverBlendFilter.h"           //源混合

import "GPUImageColorBurnBlendFilter.h"            //色彩加深混合

import "GPUImageColorDodgeBlendFilter.h"           //色彩减淡混合

import "GPUImageScreenBlendFilter.h"               //屏幕包裹,通常用于创建亮点和镜头眩光

import "GPUImageExclusionBlendFilter.h"            //排除混合

import "GPUImageDifferenceBlendFilter.h"           //差异混合,通常用于创建更多变动的颜色

import "GPUImageSubtractBlendFilter.h"             //差值混合,通常用于创建两个图像之间的动画变暗模糊效果

import "GPUImageHardLightBlendFilter.h"            //强光混合,通常用于创建阴影效果

import "GPUImageSoftLightBlendFilter.h"            //柔光混合

import "GPUImageChromaKeyBlendFilter.h"            //色度键混合

import "GPUImageMaskFilter.h"                      //遮罩混合

import "GPUImageHazeFilter.h"                      //朦胧加暗

import "GPUImageLuminanceThresholdFilter.h"        //亮度阈

import "GPUImageAdaptiveThresholdFilter.h"         //自适应阈值

import "GPUImageAddBlendFilter.h"                  //通常用于创建两个图像之间的动画变亮模糊效果

import "GPUImageDivideBlendFilter.h"               //通常用于创建两个图像之间的动画变暗模糊效果

pragma mark - 尚不清楚

import "GPUImageJFAVoroniFilter.h"

import "GPUImageVoroniConsumerFilter.h"

2.2、自定义滤镜效果<br/>

如果想自己自定义滤镜,那就需要自己写IOS的Shader(着色器)了,这个需要去百度下相关文章了,后面参考链接里面我会推荐几个文章,这些自定义滤镜的效果就是参考的那几个文章。这里说一个例子比如Nashville滤镜,暖色调。

对比图:<br/>

0A24C2ABECB9FF39F20889637E01B799.png72EA798B14A94E7C95D3F43AA1FAB2E4.png

首先先定义一个shader,TestFWFilter.h文件

#import "GPUImageTwoInputFilter.h"

import "GPUImage.h"

@interface TestFWFilter : GPUImageTwoInputFilter
@end
@interface FWNashvilleFilter : GPUImageFilterGroup
{
    GPUImagePicture *imageSource ;
}
@end

TestFWFilter.m文件

#import "TestFWFilter.h"
//自定义shader
NSString *const kFWNashvilleShaderString = SHADER_STRING
(
 precision lowp float;
 
 varying highp vec2 textureCoordinate;
 
 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 
 void main()
 {
     vec3 texel = texture2D(inputImageTexture, textureCoordinate).rgb;
     texel = vec3(
                  texture2D(inputImageTexture2, vec2(texel.r, .16666)).r,
                  texture2D(inputImageTexture2, vec2(texel.g, .5)).g,
                  texture2D(inputImageTexture2, vec2(texel.b, .83333)).b);
     gl_FragColor = vec4(texel, 1.0);
 }
 );

@implementation TestFWFilter
- (id)init;
{
    if (!(self = [super initWithFragmentShaderFromString:kFWNashvilleShaderString]))
    {
        return nil;
    }
    
    return self;
}
@end

@implementation FWNashvilleFilter

- (id)init
{
    if (!(self = [super init]))
    {
        return nil;
    }
    
    UIImage *image = [UIImage imageNamed:@"nashvilleMap.png"];
    
    imageSource = [[GPUImagePicture alloc] initWithImage:image];
    TestFWFilter *filter = [[TestFWFilter alloc] init];
    
    [self addFilter:filter];
    [imageSource addTarget:filter atTextureLocation:1];
    [imageSource processImage];
    
    self.initialFilters = [NSArray arrayWithObjects:filter, nil];
    self.terminalFilter = filter;
    
    return self;
}

@end

使用的时候按照第一段里面写的格式,只修改滤镜就可以看到效果了

-(void)moreUse{
    
    FWNashvilleFilter *disFilter = [[FWNashvilleFilter alloc] init];
    
    //设置要渲染的区域
    [disFilter forceProcessingAtSize:self.myImg.size];
    [disFilter useNextFrameForImageCapture];
    
    //获取数据源
    GPUImagePicture *stillImageSource = [[GPUImagePicture alloc]initWithImage:self.myImg];
    
    //添加上滤镜
    [stillImageSource addTarget:disFilter];
    //开始渲染
    [stillImageSource processImage];
    //获取渲染后的图片
    UIImage *newImage = [disFilter imageFromCurrentFramebuffer];
    
    //加载出来
    UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];
    imageView.frame = CGRectMake(50,50,self.myImg.size.width-200 ,self.myImg.size.height-200);
    [self.view addSubview:imageView];
}

注意,这个继承的是GPUImageTwoInputFilter是因为有两个合成图片,但不是每个都是两个的,还有GPUImageThreeInputFilter和GPUImageFourInputFilter,三个和四个等,所以看具体情况。

三、GPUImage对视频的使用

GPUImage还可以直接对视频加滤镜,shader可以直接使用图片的,使用方式稍微不同,比如这里使用图片的Amaro滤镜来拍摄视频

AA620E33996E4A209DC614C6B931FD4E.jpg

3.1、初始化视频相机<br/>

-(void)testVideo{
    self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
    self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    

    //GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
    self.customFilter = [[AmomoFilter alloc] init];
    GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 300, 400)];

    // Add the view somewhere so it's visible
    [self.view addSubview:filteredVideoView];
    
    
    [self.videoCamera addTarget:self.customFilter];
    [self.customFilter addTarget:filteredVideoView];
    
    [self.videoCamera startCameraCapture];
}

3.2、开始录制视频<br/>

-(void)start{
    self.pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie4.mov"];
    unlink([self.pathToMovie UTF8String]); // 如果已经存在文件,AVAssetWriter会有异常,删除旧文件
    NSURL *movieURL = [NSURL fileURLWithPath:self.pathToMovie];
    NSLog(@"%@",movieURL);
    self.movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(480.0, 640.0)];
    self.movieWriter.encodingLiveVideo = YES;
    [self.customFilter addTarget:_movieWriter];
    _videoCamera.audioEncodingTarget = self.movieWriter;
    [self.movieWriter startRecording];
}

3.3、录制结束

-(void)end{
    [self.customFilter removeTarget:self.movieWriter];
    _videoCamera.audioEncodingTarget = nil;
    [self.movieWriter finishRecording];
}

3.4、播放录制过的视频<br/>

-(void)plays{
    NSURL *sourceMovieUrl = [NSURL fileURLWithPath:self.pathToMovie];
    NSLog(@"%@",sourceMovieUrl);
    NSLog(@"%@",[NSURL URLWithString:self.pathToMovie]);
    
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:sourceMovieUrl];
    //或者下面这样创建playerItem
    //AVAsset *movieAsset = [AVURLAsset URLAssetWithURL:sourceMovieUrl options:nil];
    //AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];
    
    //通过playerItem创建AVPlayer
    self.playss = [AVPlayer playerWithPlayerItem:playerItem];
    //或者直接使用URL创建AVPlayer
    //self.playss = [AVPlayer playerWithURL:sourceMovieUrl];
    AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.playss];
    layer.frame = CGRectMake(0, 0, 400, 600);
    layer.videoGravity =AVLayerVideoGravityResizeAspect;
    [self.view.layer addSublayer:layer];
    [self.playss play];
}

顺便说下,这里使用AVPlayer来播放本地视频,可以通过playerItem创建,也可以使用playerWithURL直接创建,都可以,但是需要注意后面的URL连接需要正确,因为self.pathToMovie完整是

file:///var/mobile/Containers/Data/Application/932D7834-DD38-4CE0-922C-32D2548E74EB/Documents/Movie4.mov

所以如果直接使用[NSURL URLWithString:self.pathToMovie]的话,会导致url变成

/var/mobile/Containers/Data/Application/6272EE22-EF19-48E5-85BA-4A986B22AC39/Documents/Movie4.mov

这样的话是出不来的,需要用<br/>

[NSURL fileURLWithPath:self.pathToMovie]

这样创建才可以,这样的url就是file:///开头的,就是本地的了<br/>

四、Demo下载:

Github下载:https://github.com/DamonHu/GPUImageDemo

GitOsc下载:http://git.oschina.net/DamonHoo/GPUImageDemo

五、参考文章:

Last modification:March 3rd, 2017 at 09:50 am
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

6 comments

  1. 电饭锅和进口量

    就这还写成博客。。。。

    1. 东东
      @电饭锅和进口量

      是的,博客最基础就是在于自我内容的梳理,便于自己的回顾,我可以选择怎么写,你可以选择怎么看

  2. 东东

    如果调用摄像头开始拍摄的时候,第一帧会突然闪一下,可以在初始化相机的时候加上[m_videoCamera addAudioInputsAndOutputs];这个代码,比如

    m_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720 cameraPosition:AVCaptureDevicePositionBack];
        //前置摄像头
        m_videoCamera.horizontallyMirrorFrontFacingCamera = YES;
        m_videoCamera.horizontallyMirrorRearFacingCamera = NO;
        m_videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
        [m_videoCamera addAudioInputsAndOutputs];
    1. callmewenxi
      @东东

      我擦,第一帧闪一下的问题终于解决了,谢谢大神

      1. 东东
        @callmewenxi

        很庆幸能帮到你,

  3. 东东

    如果是调用前置摄像头就发现成的像是左右相反了,就看下是不是开启了镜像
    self.videoCamera.horizontallyMirrorFrontFacingCamera = YES;
    self.videoCamera.horizontallyMirrorRearFacingCamera = NO;

Leave a Comment