iOS8 Core Image In Swift:更复杂的滤镜

2023-05-16

iOS8 Core Image In Swift:自动改善图像以及内置滤镜的使用

iOS8 Core Image In Swift:更复杂的滤镜

iOS8 Core Image In Swift:人脸检测以及马赛克

iOS8 Core Image In Swift:视频实时滤镜


上面那篇文章主要是Core Image的基础,只是为了说明CIImage、CIFilter、CIContext,以及基础滤镜的简单使用。在上一篇中几乎没有对滤镜进行更复杂的操作,都是直接把inputImage扔给CIFilter而已,而Core Image实际上还能对滤镜进行更加细粒度的控制,我们在新的工程中对其进行探索。为此,我重新建立了一个空的workspace,并把之前所使用的工程添加到这个workspace中,编译、运行,没问题的话我们就开始创建新的工程。

通过workspace左下角的Add Files to添加已有的工程文件(xx.xcodeproj):



当添加工程到workspace的时候,记得要把被添加的工程关掉,不然workspacce不能识别。
另外,在流程上这篇也会与上一篇不同,上一篇一开始我就给出了代码,然后先看效果再步步为营,这篇不会在一开始给出代码。



动态改变滤镜参数的值

用Single View Application的工程模板建立一个新的工程,在View上放一个UIImageView,还是同样的frame,同样的ContentMode设置为Aspect Fit,同样的关闭Auto Layout以及Size Classes,最后把上个工程中使用的图片复制过来,在这个工程中同样使用这张图。

做完上面这些基础工作后,我们回到VC中,把showFiltersInConsole方法从上个工程中复制过来,然后在viewDidLoad里调用,在运行之前我们先看看Core Image有哪些类别,毕竟全部的滤镜有127种,不可能一一用到的。

类别有很多,而且我们从上一篇中知道了滤镜可以同时属于不同的类别,除此之外,类别还分为两大类:

按效果分类:

  • kCICategoryDistortionEffect 扭曲效果,比如bump、旋转、hole
  • kCICategoryGeometryAdjustment 几何开着调整,比如仿射变换、平切、透视转换
  • kCICategoryCompositeOperation 合并,比如源覆盖(source over)、最小化、源在顶(source atop)、色彩混合模式
  • kCICategoryHalftoneEffect Halftone效果,比如screen、line screen、hatched
  • kCICategoryColorAdjustment 色彩调整,比如伽马调整、白点调整、曝光
  • kCICategoryColorEffect 色彩效果,比如色调调整、posterize
  • kCICategoryTransition 图像间转换,比如dissolve、disintegrate with mask、swipe
  • kCICategoryTileEffect 瓦片效果,比如parallelogram、triangle
  • kCICategoryGenerator 图像生成器,比如stripes、constant color、checkerboard
  • kCICategoryGradient 渐变,比如轴向渐变、仿射渐变、高斯渐变
  • kCICategoryStylize 风格化,比如像素化、水晶化
  • kCICategorySharpen 锐化、发光
  • kCICategoryBlur 模糊,比如高斯模糊、焦点模糊、运动模糊

按使用场景分类:

  • kCICategoryStillImage 能用于静态图像
  • kCICategoryVideo 能用于视频
  • kCICategoryInterlaced 能用于交错图像
  • kCICategoryNonSquarePixels 能用于非矩形像素
  • kCICategoryHighDynamicRange 能用于HDR
这些专业词太难翻译了,有不准确的地方还望告知
此外还有我们之前用到的 kCICategoryBuiltIn
我们把 kCICategoryColorAdjustment这个类别下的滤镜打印出来看看:
有11个滤镜,其中有一个 CIHueAdjust,这个看名字应该是修改图像色调的,效果应该会比较明显,看看它有哪些参数:
它的详细信息里除了我们之前了解的inputImage和所属分类信息以外,多了个 inputAngle,显然这是一个输入参数,而且这个参数也打印的非常清晰,其中包括了:
  • 参数类型:NSNumber
  • 默认值:0
  • kCIAttributeIdentity:虽然这个值大部分情况下与默认值是一样的,但是它们的含义不一样,kCIAttributeIdentity表示的含义是这个值被应用到参数上的时候,就表示被应用的参数不会对inputImage造成任何影响
  • 最大值:Ԉ
  • 最小值:
  • 属性类型:角度
上面的这些参数以及取值对不同的CIFilter来说都不一样,要具体情况具体分析。
了解了以上情况后,我们就可以开始编码了。首先在VC里添加上个工程中的常用属性:

class ViewController: UIViewController {

    @IBOutlet var imageView: UIImageView!

    @IBOutlet var slider: UISlider!

    lazy var originalImage: UIImage = {

        return UIImage(named: "Image")

    }()

    

    lazy var context: CIContext = {

        return CIContext(options: nil)

    }()

    

    var filter: CIFilter!

......

与之前工程中不同的是,我多加了一个UISlider,Main.storyboard中VC的view像这样:

把UIImageView及UISlider的连线与VC中的连接起来,然后我们在viewDidLoad方法里写上: 

override func viewDidLoad() {

    super.viewDidLoad()

    

    imageView.layer.shadowOpacity = 0.8

    imageView.layer.shadowColor = UIColor.blackColor().CGColor

    imageView.layer.shadowOffset = CGSize(width: 1, height: 1)

    

    slider.maximumValue = Float(M_PI)

    slider.minimumValue = Float(-M_PI)

    slider.value = 0

    slider.addTarget(self, action: "valueChanged", forControlEvents: UIControlEvents.ValueChanged)


    let inputImage = CIImage(image: originalImage)

    filter = CIFilter(name: "CIHueAdjust")

    filter.setValue(inputImage, forKey: kCIInputImageKey)

    slider.sendActionsForControlEvents(UIControlEvents.ValueChanged)

    

    showFiltersInConsole()

}

imageView的设置同以前一样,增加点阴影显得好看多了。

接着对slider初始化,在之前我们了解到CIHueAdjust滤镜的inputAngle参数最大值是Ԉ,最小值是负Ԉ,默认值是0,就用这些值来初始化,然后添加一个当值发生改变时触发的事件。

初始化filter,由于只有一个滤镜,filter对象也可以重用,设置完inputImage后,触发slider的事件就可以了。

valueChanged方法实现:

@IBAction func valueChanged() {

    filter.setValue(slider.value, forKey: kCIInputAngleKey)

    let outputImage = filter.outputImage

    let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())

    imageView.image = UIImage(CGImage: cgImage)

} 

filter会在每次触发这个事件的时候更新inputAngle属性,同时输出到imageView上。

虽然我并不是在Storyboard里把slider的valueChanged事件连接到VC的方法上,但是在这里使用@IBAction也是适当的,这样可以表明这个方法不是业务逻辑方法,而是一个UI控件触发的方法。

编译、运行,应该可以看到效果了。




复合滤镜--老电影效果

在此之前,无论是使用简单滤镜,还是能动态修改参数值的滤镜,都不算复杂,因为我们最多也只是对一个滤镜设置点参数而已。可是如果现有的滤镜没有想要的效果,或者说单个滤镜实现不了自己想要的效果,就只能自己处理了,其中,最简单的做法是把 多个滤镜组合起来
Core Image并没有内置类似于老电影的效果,就是那种影像有点发黄,同时还会带点黑条、白条之类的,而我们如果要实现这种效果,总体上就像这样:
大致过程如下:
  • 需要使用CISepiaTone滤镜,CISepiaTone能使整体颜色偏棕褐色,又有点像复古
  • 需要创建随机噪点图,很像以前电视机没信号时显示的图像,再通过它生成一张白斑图滤镜
  • 需要创建另一个随机噪点图,然后通过它生成一张黑色磨砂图滤镜,就像是一张使用过的黑色砂纸一样
  • 把它们组合起来
在开始之前首先要知道一件事,我们已经知道了一些简单的滤镜,它们只需要设置inputImage就行了;还有一些除了inputImage参数外有其他参数的滤镜,除此之外,还有一些滤镜 不需要任何参数,就是上面提到的随机噪点图,另外, 有些Core Image滤镜会生成无限大小的图,比如CICategoryTileEffect类别下的滤镜,在渲染它们生成的图之前,必须先把这些无限大小的图裁剪一番,你可以通过CICrop滤镜来完成这一步,也可以通过在一个有限的矩形范围之类渲染这张图来达到同样的效果。
然后我们就动手吧。
在VC里添加一个IBAction方法:oldFilmEffect,然后在Storyboard的VC上增加一个按钮,就叫“老电影”,然后连接到oldFilmEffect方法上,oldFilmEffect方法实现的代码稍后给出,这里先描述下详细步骤,其实通过这些详细步骤,已经可以自己先实现出来了:

应用CISepiaTone滤镜到原图上

  • 设置inputImage为原图
  • 设置inputIntensity为1.0

创建白斑图滤镜

用CIRandomGenerator生成随机噪点滤镜,然后通过 imageByCroppingToRect方法对其进行裁剪, 在imageByCroppingToRect方法内Core Image隐式的使用了CICrop滤镜
接下来使用 CIColorMatrix滤镜,该滤镜可以很方便的调整图片中RGBA各分量的值,其参数设置如下:
  • 设置inputImage为CIRandomGenerator生成的随机噪点图
  • 设置inputRVector、inputGVector和inputBVector为(0,1,0,0)
  • 设置inputBiasVector为(0,0,0,0)
CISourceOverCompositing(源覆盖)滤镜把前景图(inputImage)覆盖在背景图(inputBackgroundImage)上:
  • 设置inputImage为CISepiaTone滤镜生成的图
  • 设置inputBackgroundImage为白斑图滤镜

创建黑色磨砂图滤镜

还是先用CIRandomGenerator生成随机噪点图,然后用CIAffineTransform滤镜对其进行处理,其实就是把生成的点放大。参数设置如下:
  • 设置inputImage为CIRandomGenerator生成的随机噪点图
  • 设置inputTransform为x放大1.5倍、y放大25倍,把点拉长、拉厚,但是它们仍然是有颜色的
在这里除了使用CIAffineTransform滤镜外,还有一种替代方法可以达到同样的效果,同时不用显式创建CIAffineTransform滤镜,就是使用CIImage的 imageByApplyingTransform:方法。
再次用CIColorMatrix滤镜对颜色进行处理:
  • 设置inputImage为CIAffineTransform生成的图
  • 设置inputRVector为(4,0,0,0)
  • 设置inputGVector、inputBVector和inputAVector为(0,0,0,0)
  • 设置inputBiasVector为(0,1,1,1)
现在产生的是一个蓝绿色磨砂图滤镜,再把CIMinimumComponent滤镜应用到这个蓝绿色磨砂图滤镜产生的图上。CIMinimumComponent滤镜会使用r、g、b的最小值生成一张灰度图像。

把所有的滤镜组合起来

使用CIMultiplyCompositing做最后的组合,参数设置如下:
  • 设置inputImage为CISourceOverCompositing滤镜生成的图(内含CISepiaTone、白斑图滤镜的效果)
  • 设置inputBackgroundImage为CIMinimumComponent滤镜生成的图(内含黑色磨砂图滤镜效果)
最后把CIMultiplyCompositing生成出的图输出到imageView上,还是以前的方式,先转成CGImage,再把CGImage转成UIImage。
有点小长,而且同时用到了多个滤镜,其实想表达的意思并没有那么复杂,可以使用 kCICategoryBuiltIn把所有的滤镜打印出来,然后对照着看它们的参数。

这里是oldFilmEffect方法实现:

@IBAction func oldFilmEffect() {

    let inputImage = CIImage(image: originalImage)

    // 1.创建CISepiaTone滤镜

    let sepiaToneFilter = CIFilter(name: "CISepiaTone")

    sepiaToneFilter.setValue(inputImage, forKey: kCIInputImageKey)

    sepiaToneFilter.setValue(1, forKey: kCIInputIntensityKey)

    // 2.创建白斑图滤镜

    let whiteSpecksFilter = CIFilter(name: "CIColorMatrix")

    whiteSpecksFilter.setValue(CIFilter(name: "CIRandomGenerator").outputImage.imageByCroppingToRect(inputImage.extent()), forKey: kCIInputImageKey)

    whiteSpecksFilter.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputRVector")

    whiteSpecksFilter.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputGVector")

    whiteSpecksFilter.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputBVector")

    whiteSpecksFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputBiasVector")

    // 3.CISepiaTone滤镜和白斑图滤镜以源覆盖(source over)的方式先组合起来

    let sourceOverCompositingFilter = CIFilter(name: "CISourceOverCompositing")

    sourceOverCompositingFilter.setValue(whiteSpecksFilter.outputImage, forKey: kCIInputBackgroundImageKey)

    sourceOverCompositingFilter.setValue(sepiaToneFilter.outputImage, forKey: kCIInputImageKey)

    // ---------上面算是完成了一半

    // 4.CIAffineTransform滤镜先对随机噪点图进行处理

    let affineTransformFilter = CIFilter(name: "CIAffineTransform")

    affineTransformFilter.setValue(CIFilter(name: "CIRandomGenerator").outputImage.imageByCroppingToRect(inputImage.extent()), forKey: kCIInputImageKey

    affineTransformFilter.setValue(NSValue(CGAffineTransform: CGAffineTransformMakeScale(1.525)), forKey: kCIInputTransformKey)

    // 5.创建蓝绿色磨砂图滤镜

    let darkScratchesFilter = CIFilter(name: "CIColorMatrix")

    darkScratchesFilter.setValue(affineTransformFilter.outputImage, forKey: kCIInputImageKey)

    darkScratchesFilter.setValue(CIVector(x: 4, y: 0, z: 0, w: 0), forKey: "inputRVector")

    darkScratchesFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputGVector")

    darkScratchesFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputBVector")

    darkScratchesFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputAVector")

    darkScratchesFilter.setValue(CIVector(x: 0, y: 1, z: 1, w: 1), forKey: "inputBiasVector")

    // 6.CIMinimumComponent滤镜把蓝绿色磨砂图滤镜处理成黑色磨砂图滤镜

    let minimumComponentFilter = CIFilter(name: "CIMinimumComponent")

    minimumComponentFilter.setValue(darkScratchesFilter.outputImage, forKey: kCIInputImageKey)

    // ---------上面算是基本完成了

    // 7.最终组合在一起

    let multiplyCompositingFilter = CIFilter(name: "CIMultiplyCompositing")

    multiplyCompositingFilter.setValue(minimumComponentFilter.outputImage, forKey: kCIInputBackgroundImageKey)

    multiplyCompositingFilter.setValue(sourceOverCompositingFilter.outputImage, forKey: kCIInputImageKey)

    // 8.最后输出

    let outputImage = multiplyCompositingFilter.outputImage

    let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())

    imageView.image = UIImage(CGImage: cgImage)

}

以上就是一个老电影滤镜的“配方”了。
编译、运行,显示效果如下:

子类化CIFilter

有时可能会对一些图片应用同样的滤镜,我们可能会像上面那样把一连串的滤镜组合起来,以达到自己想要的效果,那么我们就可以把这些操作封装到一个CIFilter的子类中,然后在多个地方反复使用,就像使用Core Image预置的滤镜那样。
CICategoryColorEffect类别中有个 CIColorInvert滤镜,这个滤镜提供反色功能,实现起来并不复杂,因为我们并不是做一个真正的自定义滤镜,而是在里面对Core Image已有滤镜的封装,我们可以为子类定义一些输入参数,参照苹果对CIFilter子类的命名约定,输入参数必须用input作前缀,如inputImage,然后再重写outputImage方法就行了。
现在我们回到Xcode中,做以下几件事:
  1. 新建一个Cocoa Touch Class,类名就叫CIColorInvert,继承自CIFilter
  2. 添加一个inputImage参数,类型自然是CIImage,由外界赋值。
  3. 重写outputImage属性的getter。如果你之前写过Objective-C,应该对属性有这样一个印象:子类要重写父类的属性,只需要单独写个getter或setter方法就行了,但在Swift里,不能通过这种方式重写属性,必须连getter、setter(如果父类的属性支持setter的话)一起重写。在我们的例子中outputImage在CIFilter中只是一个getter属性,
  4. 在outputImage里通过CIColorMatrix滤镜对图像的各向量进行调整。
CIColorInvert类实现:

class CIColorInvert: CIFilter {

    var inputImage: CIImage!

    

    override var outputImage: CIImage! {

        get {

            return CIFilter(name: "CIColorMatrix", withInputParameters: [

                kCIInputImageKey : inputImage,

                "inputRVector" : CIVector(x: -1, y: 0, z: 0),

                "inputGVector" : CIVector(x: 0, y: -1, z: 0),

                "inputBVector" : CIVector(x: 0, y: 0, z: -1),

                "inputBiasVector" : CIVector(x: 1, y: 1, z: 1),

            ]).outputImage

        }

    }

}

然后在Storyboard的VC上增加一个按钮“反色”,连接到VC的 colorInvert方法上,colorInvert方法实现如下:

@IBAction func colorInvert() {

    let colorInvertFilter = CIColorInvert()

    colorInvertFilter.inputImage = CIImage(image: imageView.image)

    let outputImage = colorInvertFilter.outputImage

    let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())

    imageView.image = UIImage(CGImage: cgImage)

} 

这样一下,一个对Core Image预置滤镜的简单封装就完成了,每一个滤镜的效果就像是一张配方,CIFilter就是装有配方的瓶子,所以子类化CIFilter并不算自定义滤镜,但是从iOS 8开始,Core Image是支持真正的自定义滤镜的,自定义的滤镜被称之为内核(CIKernel),在WWDC视频里对其有50分钟的介绍: https://developer.apple.com/videos/wwdc/2014/#515。
运行后反色的效果,再次点击反色按钮后显示原图:

简单抠图并更换背景

利用Core Image预置的滤镜能满足大部分使用场景,我们做一个简单的替换背景的功能。
为了方便测试,加入两张新的图:
点击图片可以打开原图。
将两张图添加到当前工程中,然后把ViewController的属性originalImage改为返回左边的图:

......

lazy var originalImage: UIImage = {

    return UIImage(named: "Image2")

}()

......

然后在Storyboard的VC上增加两个按钮:一个用于显示原图:

@IBAction func showOriginalImage() {

    self.imageView.image = originalImage

}

另一个按钮就叫“更换背景”,连接到VC的IBAction方法 replaceBackground上。
我们先看要做的事情:
  • 消除深绿色
  • 组合图片

消除深绿色

就像Photoshop的魔法棒一样,Core Image也有类似的滤镜,但是没有那么简单粗暴,使用起来很麻烦。
在Core Image里,我们为了消除某种颜色,需要使用 CIColorCube滤镜,而CIColorCube滤镜需要一张cube映射表,这张表其实就是张颜色表(3D颜色查找表),把你想消除的颜色的alpha值设置为0,其他的颜色不变,Core Image将会把图像数据上的颜色映射为表中的颜色,以此来达到消除某种颜色的目的。
CIColorCube的这张表默认不会对inputImage作任何处理,但在我们这里要将所有的深绿色干掉,所以需要自己来建立这张表。
我们要消除的“深绿色”并不只是视觉上的一种颜色,而是颜色的范围,最直接的方法是 将RGBA转成HSV(Hue,Saturation,Value),在HSV的格式下,颜色是围绕圆柱体中轴的角度来表现的,在这种表现方法下,你能把颜色的范围想象成连在一起的扇形,然后直接把该块区域干掉(alpha设为0),这就表示我们实际上需要指定颜色区域的范围------围绕圆柱体中轴线的最小角度以及最大角度,此范围内的颜色alpha设为0。最后,Cube Map表中的数据 必须乘以alpha,所以创建Cube Map的最后一步是把RGB值乘以你刚刚计算出来的alpha值:如果是想要消除的颜色,乘出来就是0,反之则不变。这是一张代表颜色值区域的HSV(Hue值)图:
可以看到如果是纯绿色,其取值是120度,蓝色是240度,我们这种情况取值大概在60到90左右(偏绿一点), 在这个网站上可以看到更详细的RGB颜色对应的HSV值。
那么接下来我们就准备创建Cube Map表,创建Cube Map表的方法在苹果官方示例中可以找到,是C语言实现的,为了方便起见,我们就直接创建一个C文件来包含这些代码。
在工程里选择 新建一个.c文件,我取名为CubeMap.c,在创建这个.c文件的时候,不出意外的话Xcode会问你是否需要创建一个桥接头文件(xxxx.Bridging-Header.H),选择是,Xcode会创建该文件,并自动把其路径放到编译选项的 Objective-C Bridging Header中。如果你要自己添加这个文件,并且需要手动修改 Objective-C Bridging Header的编译选项,可以看 这里。
.c文件搞完以后,即把苹果官方示例中的代码(以下代码)添加进去:

struct CubeMap {

    int length;

    float dimension;

    float *data;

};


struct CubeMap createCubeMap(float minHueAngle, float maxHueAngle) {

    const unsigned int size = 64;

    struct CubeMap map;

    map.length = size * size * size * sizeof (float) * 4;

    map.dimension = size;

    float *cubeData = (float *)malloc (map.length);

    float rgb[3], hsv[3], *c = cubeData;

    

    for (int z = 0; z < size; z++){

        rgb[2] = ((double)z)/(size-1); // Blue value

        for (int y = 0; y < size; y++){

            rgb[1] = ((double)y)/(size-1); // Green value

            for (int x = 0; x < size; x ++){

                rgb[0] = ((double)x)/(size-1); // Red value

                rgbToHSV(rgb,hsv);

                // Use the hue value to determine which to make transparent

                // The minimum and maximum hue angle depends on

                // the color you want to remove

                float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f1.0f;

                // Calculate premultiplied alpha values for the cube

                c[0] = rgb[0] * alpha;

                c[1] = rgb[1] * alpha;

                c[2] = rgb[2] * alpha;

                c[3] = alpha;

                c += 4// advance our pointer into memory for the next color value

            }

        }

    }

    map.data = cubeData;

    return map;

}

我将这个方法稍微改造了一下,选回一个结构体,因为外面要用到length和dimension。苹果没有提供rgbToHSV方法的实现,可以用我找到的这个:

void rgbToHSV(float *rgb, float *hsv) {

    float min, max, delta;

    float r = rgb[0], g = rgb[1], b = rgb[2];

    float *h = hsv, *s = hsv + 1, *v = hsv + 2;

    

    min = fmin(fmin(r, g), b );

    max = fmax(fmax(r, g), b );

    *v = max;

    delta = max - min;

    if( max != 0 )

        *s = delta / max;

    else {

        *s = 0;

        *h = -1;

        return;

    }

    if( r == max )

        *h = ( g - b ) / delta;

    else if( g == max )

        *h = 2 + ( b - r ) / delta;

    else

        *h = 4 + ( r - g ) / delta;

    *h *= 60;

    if( *h < 0 )

        *h += 360;

}

我在.c文件中导入的库:

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

对了,如果那个桥接文件里没有导入这个.c文件的话是不行的,Swift的类会找不到这里面的方法。

//  ComplexFilters-Bridging-Header.h

//  Use this file to import your target's public headers that you would like to expose to Swift.

//


#import "CubeMap.c" 

组合图片

VC中的 replaceBackground方法只需要做三件事:
  • 创建Cube Map表
  • 创建CIColorCube滤镜并使用Cube Map
  • 用CISourceOverCompositing滤镜将处理过的人物图像和未处理过的背景图粘合起来
方法实现如下:

@IBAction func replaceBackground() {

    let cubeMap = createCubeMap(60,90)

    let data = NSData(bytesNoCopy: cubeMap.data, length: Int(cubeMap.length), freeWhenDone: true)

    let colorCubeFilter = CIFilter(name: "CIColorCube")

    

    colorCubeFilter.setValue(cubeMap.dimension, forKey: "inputCubeDimension")

    colorCubeFilter.setValue(data, forKey: "inputCubeData")

    colorCubeFilter.setValue(CIImage(image: imageView.image), forKey: kCIInputImageKey)

    var outputImage = colorCubeFilter.outputImage

    

    let sourceOverCompositingFilter = CIFilter(name: "CISourceOverCompositing")

    sourceOverCompositingFilter.setValue(outputImage, forKey: kCIInputImageKey)

    sourceOverCompositingFilter.setValue(CIImage(image: UIImage(named: "background")), forKey: kCIInputBackgroundImageKey)


    outputImage = sourceOverCompositingFilter.outputImage

    let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())

    imageView.image = UIImage(CGImage: cgImage)

}

参数设置都还比较简单,CISourceOverCompositing滤镜目前已经使用过多次了,并没有什么复杂的。
编译、运行,可以分两次执行,先看消除深绿色的效果,再看最后使用CISourceOverCompositing滤镜组合图片之后的效果:

GitHub下载地址

我在GitHub上会保持更新。


UPDATED:

我在更换背景的右侧,新加入了一个显示图2的button,已在GitHub上更新。



参考资料:

http://www.docin.com/p-387777241.html

https://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/CoreImaging/ci_intro/ci_intro.html


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS8 Core Image In Swift:更复杂的滤镜 的相关文章

  • Swift 3 中是否提供内置内部函数?

    我可以在 Xcode 自动完成弹出窗口中看到各种内置函数 如 builtin popount builtin clz 等 我不确定这些是从哪里获取的 单击命令不会导致快速定义或任何文档 Swift 3 中是否有 builtin 或等效的内部
  • 使用 mongoose 通过 React 应用程序将图像上传到 mongodb 数据库

    我正在为找到的对象创建一个反应应用程序 我想允许用户上传这些对象的照片 我尝试使用 axios 通过 post 请求将图像发送到猫鼬服务器 但它不起作用 这就是我如何将图像存储在带有预览的 React 组件中 handleImage eve
  • 如何防止 RealmSwift 列表中出现重复项?

    如何防止向列表中添加重复项RealmSwift 我有我的User作为领域对象 但真正的数据源是服务器 只是使用领域在本地缓存用户 当我从服务器获取当前用户数据时 我想确保存储在领域中的用户拥有来自服务器的所有播放列表 以及它们的曲目列表等
  • NSDateFormatter 返回错误的日期 + Swift

    Code let dateString 2016 04 02 var formatter NSDateFormatter NSDateFormatter formatter timeZone NSTimeZone abbreviation
  • Transit MKDirectionsRequest 产生 null 错误 Error Domain=MKErrorDomain Code=5 "(null)"

    我正在尝试使用 MapKit Directions Request 来获取两个坐标之间的交通方向 当我切换到其他 非 Transit 类型时 下面的代码可以工作 但是当我切换到 Transit 时 它会抛出一个错误 该错误在 Apple 文
  • 我们可以从 LinkPresentation 框架中的 LPLinkView 中提取图像吗?

    我想在我的应用程序中呈现丰富的链接 并将这些数据发送到我的服务器 我需要访问视图内的图像LPLinkView https developer apple com documentation linkpresentation lplinkvi
  • 如何使用 winforms 在 C# 中从网络下载显示下载进度的图像?

    我使用 WebRequest 从 URL 异步下载图像 如下所示 public void Download string url byte buffer new byte 0x1000 WebRequest request HttpWebR
  • Swift 3.0 Pin 颜色注释在 MapView 中没有改变

    我有兴趣根据案例场景更改注释的图钉颜色 在一个函数中 我发送了一个数组 用于确定引脚注释的颜色 到目前为止 我已经设置了一个名为 ColorPointAnnotation 的子类 它将确定 pinColor 然后 在 switch 语句中
  • 将数字分解为单个数字的数组

    如果我有整数 123 并且我想将数字分解为数组 1 2 3 最好的方法是什么 我已经搞乱了很多 并且我有以下工作 var number 123 var digits Array String number map Int strtoul S
  • 在Python中调整图像大小

    我有一张尺寸为 288 352 的图像 我想将其大小调整为 160 240 我尝试了以下代码 im imread abc png img im resize 160 240 Image ANTIALIAS 但它给出了一个错误TypeErro
  • 按范围迭代数组

    我有一个数组 1 2 3 4 5 6 100 我希望将此数组迭代 5 次 具体来说 取数组的前 5 个数字并获取平均值 继续处理接下来的 5 个数字并获取平均值 依此类推 我尝试过多种方法 例如Dequeue和 for 循环但未能获得所需的
  • Xcode 9 中的“addingPercentEncoding”是否损坏?

    在 Swift 3 x 和 Xcode 9 beta 2 中 使用addingPercentEncoding https developer apple com documentation swift string 1690785 addi
  • 在 UIView 中实现自定义 StaggereGrid,就像 SWIFT 中的 Etsy 应用程序一样

    我想实现一个像 Etsy 应用程序一样的 StaggeredGrid 但我对此一无所知 你能给我一些建议吗 这是我需要做的事情的屏幕截图 None
  • 退出导航控制器

    我试图离开初始视图控制器 并进入空白视图控制器 这很好 但是这会使空白视图控制器也成为导航控制器的一部分 这不是我想要的 我想脱离视图控制器 在视图控制器中 我尝试退出 它会自行弹出 当我尝试视图中的方法时 将出现目标视图控制器 self
  • Obj-C / Swift 项目中的致命陷阱异常

    我开始将 Swift 代码集成到我的 Obj C 项目中 一切都进展顺利 但今天 当我更新到 Xcode 6 1 时 事情变得很糟糕 我从之前运行良好的 Swift 代码中收到了许多 陷阱 异常 第一次崩溃位于我的 UIFont 扩展中 这
  • Firestore 读取包含子集合的文档的计费

    我正在制作一个应用程序 它存储用户使用我的应用程序学习了多少分钟 我的 Firestore 数据库以 用户 集合开始 每个用户都有自己的文档 该文档由其在 Auth 中生成的 userID 命名 我的问题是 如果我读取了他们的 userID
  • 正确的标头 php mysql blob 显示图像

    我正在尝试在我的 PHP 页面中显示来自 mysql blob 的图像 我知道这不是最佳实践 然后我会将其引入我的 iOS 应用程序中 我在设置页面标题时遇到问题 我认为需要将其设置为图像 所以 这显示了图像 但我不相信页眉是正确的 hea
  • 如何在 Swift 中调用 Objective-C 实例类型方法?

    我有一个 Objective C 类 如下所示 interface CustomObjectHavingData NSObject property nonatomic strong NSData objWithData instancet
  • iOS 11 浏览器图像错误

    在 iOS 11 中滚动页面时出现以下错误 在 Firefox Safari 和 Chrome 中 在 Android 设备中 不会发生该错误 这些是背景图像 我不知道这是否是导致错误的原因 图 2 显示了图像在 Android 中的用途和
  • 准确地从屏幕上的像素获取颜色并转换其颜色空间

    我需要从屏幕上的像素获取颜色并转换其颜色空间 我遇到的问题是 将值与数字色度计应用程序进行比较时 颜色值不相同 create a 1x1 image at the mouse position if let image CGImage CG

随机推荐

  • 刘慈欣(三体作者)写给200年以后的女儿的一封信

    原文地址 xff1a http blog sina com cn s blog 540d5e800101lcsb html 亲爱的女儿 xff0c 你好 xff01 这是一封你可能永远收不到的信 xff0c 我将把这封信保存到银行的保险箱中
  • 程序员玩游戏之二--篡改植物大战僵尸2的阳光值

    植物大战僵尸1几年前曾经风靡一时 xff0c 妇孺皆知 xff01 其续作奇幻时空之旅千呼万唤始出来 xff0c 不过从首发到目前都1月有余 xff0c 本人竟然还没玩过 于是昨晚下载了一个汉化版 本人系统为IOS5 0 1 xff0c a
  • 设计模式:生产者消费者模式

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题 该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度 为什么要使用生产者和消费者模式 xff1f 在线程世界里 xff0c 生产者就是生产数据的线程 xff0c
  • activemq 应用实践——queue

    首先创建发送端程序SenderTestBase和接收端程序ReceiveTestBase 发送端 xff1a SenderTestBase java package test import java util Date import jav
  • swig包装c++步骤

    使用Swig封装C 43 43 到Python的心得 01 收藏 一 xff0e 简述 前一段时间由于工作需要重点学习了一下用Swig来封装C 43 43 代码到Python的知识 xff0c 期间遇到一些问题 xff0c 也有一些心得体会
  • PS CC2019 安装过程中遇到 Command line option syntax error. Type Command /? for Help.

    背景前言 昨天买了一块高漫WH850 的数位板 手绘板 板子都买了 肯定要安装PSCC 2019 了 但是在安装过程中遇到一个问题 那就是Command line option syntax error Type Command for H
  • 【上传】Nginx 上传文件

    本文包含知识点 xff1a 1 nginx服务搭建 2 nginx文件上传模块搭建 3 文件重命名服务搭建 3 nginx整体配置 4 测试 引言 一般可以应用在上传不是跟频繁的场景 xff0c 都可以采用Nginx上传文件 我们都知道 x
  • 推荐一些非常非常实用的linux命令(持续更)

    终生学习是我追求的目标 1 通过yum命令只下载rpm软件包但不安装 PS xff1a 适合在没有外网的情况下自制本地yum源 xff0c 前提是先做好镜像 方法一 xff1a yumdownloader 如果只想通过 yum 下载软件的软
  • Android 根据打包环境不同,显示不同的应用名称

    可以动态的设置应用名称和应用图标 1 在app的buid gradle中设置resValue 值 productFlavors span class token punctuation span dev span class token p
  • springboot整合guava实现本地缓存

    springboot整合guava实现本地缓存 一 Springboot缓存 SpringBoot支持很多种缓存方式 xff1a redis guava ehcahe jcache等等 二 guava介绍 Guava Cache 是 Goo
  • Java 中如何使用枚举来消除 if/else

    今天 xff0c 准备重新学习一下 Java 中的枚举类型 为什么现在要去重新学习呐 xff1f 因为在刚开始学习 Java 的时候 xff0c 对于枚举这一块的学习不太重视 xff0c 工作之后发现用到枚举的地方挺多的 xff0c 就有了
  • Lottie- 让Android动画实现更简单

    Lottie是什么 xff1f Lottie是Airbnb开源的一个支持 Android iOS 以及 ReactNative xff0c 利用json文件的方式快速实现动画效果的库 这么看可能很难理解 xff0c 接下来我将详细的讲解如何
  • 使用HC05蓝牙模块实现数据无线传输,看完包会

    由于需要将单片机采集到的数据无线传输到上位机 xff0c 考虑到成本原因 xff0c 故采用HC05蓝牙模块进行数据传输 一 开发工具 单片机 两块HC05蓝牙模块 一个USB转TTL模块 xff0c 电脑上安装串口调试软件 xff08 推
  • 【路径规划】(4) 蚁群算法,附python完整代码

    大家好 xff0c 今天和各位分享一下蚁群算法 xff0c 并基于 tkinter 完成一个旅行商问题 完整代码可以从我的 GitHub 中获得 xff1a https github com LiSir HIT Mathematical P
  • 利用Jekyll在GitHub Pages上部署博客

    开始之前 我觉得阮一峰的 搭建一个免费的 xff0c 无限流量的Blog github Pages和Jekyll入门 是一个很经典的入门 xff0c 每个Jekyll初学者都应该先去看一看 xff0c 另外 GitHub Pages官网以及
  • CSDN博客导出工具 Mac By Swift

    写这个的目的主要是用于了解Swift语言本身 xff0c 以及如何与Objc和第三方框架交互 需要先使用CSDN账号来登录 xff0c 可以导出所有的博客文章 xff0c 添加YAML头信息的时候 xff0c 会在头信息里面包含文章对应的标
  • 如何避免在Block里用self造成循环引用

    一般来说我们总会在设置Block之后 xff0c 在合适的时间回调Block xff0c 而不希望回调Block的时候Block已经被释放了 xff0c 所以我们需要对Block进行copy xff0c copy到堆中 xff0c 以便后用
  • Xcode及模拟器SDK下载

    现在不会有人想在 Xcode 里安装旧版的模拟器了 xff0c 本篇不再维护 如果你嫌在 App Store 下载 Xcode 太慢 xff0c 你也可以选择从网络上下载 xff1a Xcode下载 xff08 Beta版打的包是不能提交到
  • Mac下最好用的文本编辑器

    友情提醒 xff1a 图多杀猫 以前在Windows下一直用gVim xff0c 可以用键盘控制一切 xff0c 操作起来是又快又爽 xff0c 还支持一大堆插件 xff0c 想怎么玩就怎么玩 后来转Mac后 xff0c 也沿袭着之前的习惯
  • iOS8 Core Image In Swift:更复杂的滤镜

    iOS8 Core Image In Swift xff1a 自动改善图像以及内置滤镜的使用 iOS8 Core Image In Swift xff1a 更复杂的滤镜 iOS8 Core Image In Swift xff1a 人脸检测