iOS 开发每天分享优质文章

iOS Core Image - 入门导读

2019-01-24  本文已影响6人  小白进城
高斯模糊

简介

Core Image 是一种图像处理和分析技术,可为静态和视频图像提供高性能处理。 使用许多内置图像过滤器来处理图像并通过链接过滤器来构建复杂效果。

关于过滤器种类和效果可以查看官方文档:Core Image Filter Reference

系统内置了多种滤镜效果,你可以将多种滤镜组合使用,一般情况下的需求都可以满足。当然,你也可以自定义滤镜效果。

关于自定义了滤镜可以查看官方文档:Core Image Programming Guide

基础知识

三个重要的类

Core Image 中的图像类,类似 UIKit 中的 UIImage 类

Core Image 中的图像过滤器,可以使用它来处理一个或者多个图像,并产生新的图像数据

Core Image 中处理图像的上下文,可以用来渲染、分析图像

CIContext 和 CIImage 对象是不可变的,因此多个线程可以使用同一个 CIContext 对象来呈现 CIImage 对象。但是,CIFilter 对象是可变的,因此无法在线程之间安全地共享,每个线程必须创建自己的 CIFilter 对象。

应用在过滤器中的参数类型

定义了特定颜色空间

坐标值,方向矢量,矩阵和其他非标量值的容器,用于 Core Image 中的过滤器参数

过滤器的边界形状和过滤操作的定义域的描述

图像输入,输出和处理的像素数据格式

内置过滤器的使用示例

三种过滤器

上图使用了三种过滤器组合起来实现效果:

1、使用 sepia 对带有红褐色色调的图像进行着色

2、添加 bloom 滤镜以突出显示高光

3、使用 Lanczos 比例滤镜缩小图像

接下来我们来演示一下滤镜的使用步骤。

步骤一:上下文和资源图像

正像我们之前提到的,我们需要一块处理图像的上下文:CIContext ,创建该对象是非常消耗资源的,因此我们可以在应用的整个生命周期中一直持有该对象重复使用。

let context = CIContext()

巧妇难为无米之炊,我们还需要提供被处理的图像资源:CIImage ,该对象本身是不可显示的,你可以将其转换为 UIImage 进行显示,或者将 UIImage 转为 CIImage 对象。

let imageURL = URL(fileURLWithPath: "\(Bundle.main.bundlePath)/YourImageName.png")

// CIImage 对象
let originalCIImage = CIImage(contentsOf: imageURL)!

// 转换为 UIImage 对象显示
self.imageView.image = UIImage(ciImage:originalCIImage)
原始图片

步骤二:选择内置过滤器

内置过滤器是不可见属性的类型,我们可以使用 KVC 的形式提前给过滤器设置过滤器的相关属性,也可以在创建过滤器时指定。如果你不知道有哪些过滤器,可以查询官方文档:Core Image Filter Reference

sepia 过滤器

系统提供了一些常见的和参数相关的key,例如 kCIInputImageKeykCIInputRadiusKey 等,如果你无法推断关联的 key,则只需使用过滤器对应参数的字符串即可。例如上图中的 inputImage 就表示 kCIInputImageKey

回到之前的例子,我们来创建 sepia 过滤器过滤图片。

func sepiaFilter(_ input: CIImage, intensity: Double) -> CIImage?
{
    // 创建过滤器
    let sepiaFilter = CIFilter(name:"CISepiaTone")
    // 设置输入图片
    sepiaFilter?.setValue(input, forKey: kCIInputImageKey)
    // 设置强度
    sepiaFilter?.setValue(intensity, forKey: kCIInputIntensityKey)
    // 返回输出值
    return sepiaFilter?.outputImage
}

这样我们就可以使用该方法得到 sepia 过滤器过滤后图片。

let sepiaCIImage = sepiaFilter(ciImage!, intensity:0.9)

我们将 sepiaCIImage 转换为 UIImage 进行显示。

self.imageView.image = UIImage.init(ciImage: sepiaCIImage!)
sepia 过滤后图片

以上就完整演示了内置滤镜的步骤。当然,我们还可以接着对已过滤的图像进一步使用过滤器。

为了完整演示示例中的效果,我们接着 使用 Bloom 过滤器,Bloom 过滤器突出了图像的高光部分。

Bloom 过滤器

像 Sepia 滤镜一样,Bloom 滤镜效果的强度范围在0到1之间,其中1是最强烈的效果。Bloom 过滤器具有额外的 inputRadius 参数,以确定发光区域将扩展多少。可以使用范围值来微调效果,或者将输入参数分配给控件(如 UISlider )以允许用户调整其值。

func bloomFilter(_ input:CIImage, intensity: Double, radius: Double) -> CIImage?
{
    let bloomFilter = CIFilter(name:"CIBloom")
    bloomFilter?.setValue(input, forKey: kCIInputImageKey)
    bloomFilter?.setValue(intensity, forKey: kCIInputIntensityKey)
    // 设置发光区域
    bloomFilter?.setValue(radius, forKey: kCIInputRadiusKey)
    return bloomFilter?.outputImage
}

注:CIGloom 过滤器可以执行和 Bloom 过滤器相反的效果

let bloomCIImage = bloomFilter(sepiaCIImage!, intensity:1, radius:10)
self.imageView.image = UIImage(ciImage:bloomCIImage!)

同样的,我们将图片转换为 UIImage 进行显示。

Bloom 过滤后图片

注:实际上,通过这一步中的 Bloom 滤镜 会将图片填充莫名其妙的白色部分,并且整体图像尺寸要比原图尺寸大一些,就如下图所示。

莫名其妙的填充

细心的读者应该发现,我们之前创建的 CIContext 对象在整个滤镜过程中并未使用到对象 Context,而在 CIFilter 对象 outputImage 得到 CIImage 对象后,就使用 init(ciImage: CIImage) 方法直接转换为 UIImage 对象了(实际上这种转换是有系统自动创建 CIContext 对象完成的,但是创建 CIContext 对象是非常消耗资源的,因此并不推荐频繁的创建该对象)。要知道,在内置的许多过滤器的输出图像和输入图像具有不同的尺寸比。例如,一个模糊图像(如高斯模糊)由于采样超出了输入图像的边缘,围绕在其边界外还会有一些额外的像素,另外,Core Image 是一个 OS X 和 iOS 的图像处理框架,它采用了前者的坐标系,因此上图会出现图片整体在左下角的情况。这时候,CIContext 对象就起到了作用,我们需要 CIContext 从输出图像的一个矩形内(bounds)创建一个 CGImage。

修改之前的代码:

let bloomCIImage = bloomFilter(sepiaCIImage!, intensity:1, radius:10)
let image2 = Context.createCGImage(bloomCIImage!, from: (ciImage?.extent)!)
self.imageView.image = UIImage.init(cgImage: image2!)

这样我们就可以得到预期的图像:

Context转换后的图像 CILanczosScaleTransform

使用 CILanczosScaleTransform 可以高质量下重新采样获得新图像,通过CILanczosScaleTransform 滤镜的输入参数 aspectRatio 为 1 时,可保留原始图像的纵横比,你也可以传入[0,1]其他值,如0.5,结果将是:宽度缩小为一半,图像纵向拉伸。

func scaleFilter(_ input:CIImage, aspectRatio : Double, scale : Double) -> CIImage
{
    let scaleFilter = CIFilter(name:"CILanczosScaleTransform")!
    scaleFilter.setValue(input, forKey: kCIInputImageKey)
    // 设置目标大小
    scaleFilter.setValue(scale, forKey: kCIInputScaleKey)
    // 目标图像的宽高之比
    scaleFilter.setValue(aspectRatio, forKey: kCIInputAspectRatioKey)
    return scaleFilter.outputImage!
}

这样我们将图像的宽度缩小一半,图像整体缩小一半。

let scaledCIImage = scaleFilter(sepiaCIImage!, aspectRatio:0.5, scale:0.5)
缩小图像

图像的大小可能无法体现(读者可以试验自行查看),图像的宽高比表现很明显。

内置过滤器

查询可用的滤镜

我们可以通过 CIFilter 的方法来查询当前系统内置的过滤器种类,当然你也查询官方文档:Core Image Filter Reference

let filterNames = CIFilter.filterNames(inCategory: kCICategoryBuiltIn)
print(filterNames.count) # 当前有 180 个

一些值得留意的滤镜


更多过滤器使用详情和效果请查询官方文档:Core Image Filter Reference


本文地址:Core Image - 入门导读

上一篇 下一篇

猜你喜欢

热点阅读