使用CoreImage实现素描滤镜

  • 前言
  • 开始
    • 设计
    • 实现
    • 封装
  • 总结
    • hint

前言

本文读者应该已经掌握了CoreImage基本用法,能够简单使用CIFilter并能够用CIContext渲染输出图片。
本文不会主要讲述如何使用CoreImage,如果对于CoreImage还不太熟悉,请前往苹果官方文档查看并学习:https://developer.apple.com/library/ios/documentation/GraphicsImaging/Conceptual/CoreImaging/CoreImage.pdf

开始

在我们开始实现之前,我们最好先把整个思路设计一下,就像软件工程里面的概要设计和详细设计一样,它能有效的帮助我们理清思路,找到问题的解决方案,为我们的实现打下良好的基础

设计

实现之前必先设计

自iOS5以后,图像处理应用大量兴起,主要是由于CoreImage的出现。CI框架提供了大量的built in滤镜供开发者们使用,并且使用起来异常简单。而CI的built in滤镜有一个非常强大的高可扩展性设计:我们可以通过子类化一个CIFilter自定义自己的滤镜效果。

在苹果的官方文档中提到,如果built in滤镜无法满足开发的需求,比如开发者们需要将多个滤镜组成一个滤镜链用来处理图片,而这个滤镜链会被多次使用,这时我们就可以将这个滤镜链通过子类化CIFIlter来封装成一个我们自己的filter对象。

如何实现自己想要的滤镜效果呢?首先我们要知道多个滤镜组合起来对一张图片进行处理后的效果是怎样的。

用 Quartz Composer,来做 Core Image 滤镜图表的原型非常有用,可以从 苹果官方网站上面下载
使用CoreImage实现素描滤镜_第1张图片

对于一些特殊的滤镜效果,在built in中没有的,而在ps中大家经常遇到的效果,比如今天我们要实现的素描效果的滤镜,大家可以上搜索网站进行搜索,找到这个滤镜效果的实现流程,比如素描滤镜的实现:

 1、去色;

 2、复制去色图层,并且反色;

 3、对反色图像进行高斯模糊;

 4、模糊后的图像叠加模式选择颜色减淡效果。

好了,到这一步,我们的设计思路已经出来了:

1、去网上搜索素描滤镜的实现流程;

2、使用滤镜链对图片进行处理以验证该流程的准确性;

3、子类化一个CIFilter将该流程封装成一个单滤镜效果。

第一步实际上是比较灵活的,我们既可以使用Quatz Composer来试验(如果你有图像处理或者PS比较不错的经验的话,应该能很快找到相应的滤镜链)使用哪几个个滤镜组成的滤镜链能够达到自己想要的效果;我们也可以像我写的这样,直接去网上搜索某个效果的滤镜实现。

最后针对设计的每一步再详细的进行具体的设计:

1、首先我们要知道素描滤镜的滤镜链是怎样串联起来的 。

这个问题可以在网上进行搜索,我们可以创建一个

去色->反色->高斯模糊->颜色减淡叠加

的滤镜链,而在去色后需要拷贝一次图像,用拷贝的图像来继续下面的滤镜操作。

2、然后我们需要知道如何实现滤镜链。

在官方文档中进行了描述:

You can create amazing effects by chaining filters—that is, using the output image from one filter as input to another filter.
简单的说,就是把一个滤镜的输出图片作为下一个滤镜的输入图片

3、如何通过子类化一个CIFilter来封装一个滤镜链的效果

这个问题同样在官方文档中有具体的流程:

To subclass a filter you need to perform the following tasks:

  • Declare properties for the filter’s input parameters. You must prefix each input parameter name with input, such as inputImage.
  • Override the setDefaults method, if necessary. (It’s not necessary in this example because the input parameters are set values.)
  • Override the outputImage method.

简单来说就是三步:

1、声明一个属性用来接收输入图片,这个属性的名字必须命名为inputXXX。
2、如果有必要的话重写setDefaults方法
3、重写outputImage方法

嗯,苹果官方开发者文档还是多强大哈。

实现

我们的整个设计思路完成以后,就可以开始着手实现了。实现的时候根据我们之前的设计来进行。

我们新建一个工程出来,在ViewController中来实现我们今天的demo。首先我们需要一个imageView用来展现我们处理后的图片。

@interface ViewController ()

@property (nonatomic, strong) UIImageView * imageView;
@property (nonatomic, strong) UIImage * image;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(30,200,320,240)];
    [self.view addSubview:self.imageView];
    self.image = [UIImage imageNamed:@"1.jpg"];
}

接下来我们就可以实现滤镜链了。
第一步,去色,去色可以直接使用built in滤镜

CIImage * inputImage = [[CIImage alloc] initWithImage:self.image];

    CIFilter * monoFilter = [CIFilter filterWithName:@"CIPhotoEffectMono"];
    [monoFilter setValue:inputImage forKey:kCIInputImageKey];

    CIImage * outImage = [monoFilter valueForKey:kCIOutputImageKey];

第二步就是把去色的这张图片拷贝一份,然后把拷贝的这张图片进行反色。由于CIImage是支持NSCopying协议的,所以我们可以直接调用copy方法来进行拷贝。接着上面的代码在viewDidLoad中继续实现:

    CIImage * invertImage = [outImage copy];

    CIFilter * invertFilter = [CIFilter filterWithName:@"CIColorInvert"];
    [invertFilter setValue:invertImage forKey:kCIInputImageKey];
    invertImage = [invertFilter valueForKey:kCIOutputImageKey];

然后对这张反色的图片进行高斯模糊处理

    CIFilter * blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [blurFilter setDefaults];
    [blurFilter setValue:@5 forKey:kCIInputRadiusKey];
    [blurFilter setValue:invertImage forKey:kCIInputImageKey];

    invertImage = [blurFilter valueForKey:kCIOutputImageKey];

接下来使用混合叠加将invertImage叠加到之前去色的图片上。

    CIFilter * blendFilter = [CIFilter filterWithName:@"CIColorDodgeBlendMode"];

    [blendFilter setValue:invertImage forKey:kCIInputImageKey];
    [blendFilter setValue:outImage forKey:kCIInputBackgroundImageKey];
    CIImage * sketchImage = [blendFilter outputImage];

我们终于拿到了经过滤镜链处理的输出图片sketchImage,最后一步,我们使用CIContext将图片渲染出来。
注意我们将图片经过高斯模糊后图片的extent会被修改,所以我们的输出图片的rect应该用inputImage来获取。我们的CIContext直接渲染出来的图片是一个CGImage指针,记住要手动释放,不然会造成内存泄露。

    CGImageRef cgImage = [context createCGImage:sketchImage fromRect:[inputImage extent]];
    self.imageView.image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);

好了我们的滤镜链创建完成并且获取了渲染后的图片,我们的imageView也能够进行显示了,接下来运行一下工程看看效果。

这是我们的原图:

这是我们处理后的图片: 

使用CoreImage实现素描滤镜_第2张图片

Amazing~~~
Oh,如果我们使用模拟器来进行渲染的话,发现可能会有少许延迟,没关系,我们的项目在真机上面跑起来将会以非常高效的速度进行渲染,几乎没有时间开销。所以这一点完全不用担心。

封装

我们用viewDidLoad试验了一下我们的效果,看来非常满意,接下来我们肯定要考虑封装一下,以便放到git等开源社区上面去装逼。

在设计阶段我们已经有了思路:如何将滤镜链整合到一起实现一个我们自己的滤镜类。

按照我们的设计,首先我们需要一个CIFilter的子类,所以我们新建一个类,命名为DHSketchFilter,继承于CIFilter。根据文档描述,我们需要提供一个属性来接收输入图片。于是我们可以在DHSketchFilter.h里面这样写:

#import <CoreImage/CoreImage.h>

@interface DHSketchFilter : CIFilter

@property (nonatomic, strong) CIImage * inputImage;

@end

接下来继续按照文档的描述,我们重写outputImage方法,因为我们没有默认设置,所以不需要重写setDefaults方法
我们可以把viewDidLoad里面相关代码copy进来,稍事修改一下:

@implementation DHSketchFilter

- (CIImage *)outputImage
{
    // 1、去色;

    // 2、复制去色图层,并且反色;

    // 3、对反色图像进行高斯模糊;

    // 4、模糊后的图像叠加模式选择颜色减淡效果。

    CIFilter * monoFilter = [CIFilter filterWithName:@"CIPhotoEffectMono"];

    [monoFilter setValue:_inputImage forKey:kCIInputImageKey];

    // 1
    CIImage * outImage = [monoFilter valueForKey:kCIOutputImageKey];

    // 2
    CIImage * invertImage = [outImage copy];

    CIFilter * invertFilter = [CIFilter filterWithName:@"CIColorInvert"];
    [invertFilter setValue:invertImage forKey:kCIInputImageKey];
    invertImage = [invertFilter valueForKey:kCIOutputImageKey];
    // 3

    CIFilter * blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [blurFilter setDefaults];
    [blurFilter setValue:@5 forKey:kCIInputRadiusKey];
    [blurFilter setValue:invertImage forKey:kCIInputImageKey];

    invertImage = [blurFilter valueForKey:kCIOutputImageKey];

    // 4
    CIFilter * blendFilter = [CIFilter filterWithName:@"CIColorDodgeBlendMode"];

    [blendFilter setValue:invertImage forKey:kCIInputImageKey];
    [blendFilter setValue:outImage forKey:kCIInputBackgroundImageKey];
    CIImage * sketchImage = [blendFilter outputImage];

    return sketchImage;
}

@end

这样我们就封装好了。。简单把,接下来我们来试一下我们这个类用起来怎么样。
我们重新回到viewDidLoad中,用下面的代码替换之前用来实现滤镜链的所有代码。这次我们只需要几行代码就够了:

    CIImage * inputImage = [[CIImage alloc] initWithImage:self.image]; CIContext * context = [CIContext contextWithOptions:nil]; DHSketchFilter * filter = [DHSketchFilter new]; filter.inputImage = inputImage; CGImageRef cgImage = [context createCGImage:filter.outputImage fromRect:[inputImage extent]];
    self.imageView.image = [UIImage imageWithCGImage:cgImage];

写好了以后运行一下,是不是很有效果呢

总结

我们的总结比较简单,因为实际上大部分的总结内容都在我们的设计阶段已经进行了。所以这里简单的再写一点。

首先是如何构造我们想要的滤镜效果,也就是,我们想要的滤镜效果是通过哪些滤镜组合成的滤镜链实现的。

这个问题可以通过Quatz Composer来找到,如果想要的滤镜效果比较特殊,比如今天的素描滤镜,可以直接上网进行搜索,一般都能搜到。

然后是如何实现滤镜链和封装

查看官方文档完破!

总结到此,希望大家能通过这个例子实现更多好玩的滤镜。

hint

大家可以修改一下高斯模糊的模糊半径试试看^-^(高斯模糊的半径取值范围是0-100,越大则越模糊)

你可能感兴趣的:(ios开发,滤镜,CoreImage,素描滤镜)