Core Image 编程指南(上) Core Image Programming Guide
Core Image是一种用来为定格图片和视频图片提供近乎实时处理和分析的技术。它从 Core Graphic,Core Video和Image IO framework中操作各种图片数据类型,可以使用GPU和CPU的绘制路径。Core Image 隐藏了底层的图像处理,而提供了方便使用的编程接口API。你不需要知道OpenGL,OpenGL ES或者Metal影响GPU能力的细节,你也不需要了解GCD多核处理的优点。因为Core Image负责处理这些事情。
Core Image和操作系统的关系如图:
Core Image 提供一下功能:
- 访问内建的图片处理filters
- 特性检测能力 Feature detection capability
- 支持自动图片增益 image enhancement
- 链接多个filters来创建自定义效果的能力
- 支持运行在GPU上的自定义filters的创建
- 基于feedback的图片处理能力
macOS上,Core Image也提供一种为其他app打包自定义filters的方法。
Core Image 是处理和分析图片非常有效和简单的方式
Core Image土工了数百个内建filter时。通过KVC为filter设置输入参数来配置filters。一个filter的输出可以成为另一个filter的输入,这让链接许多filter来创建惊艳的特效成为可能。如果你创建了一个复合的特效,你想要在某处再次使用它,你可以继承CIFilter类来把这个效果做成一个“配方”反复使用。
filters有许多的categories。很多是为了达到艺术效果而设计的,比如 stylize 和 halftone filter categories。其他的categories为处理图片问题而优化的,比如 red eye。你只需要调用他们来做这些事情。
Core Image能在定格图片中探测人脸、在视频图像中实时追踪人脸。知道人脸在哪里能帮助你决定在哪里放置一个 vignette 或者应用其他特定的filters。
通过查询 Core Image来得到一个filters的列表和他们的属性
Core Image为它的filters提供了“内置”的参考文档。你可以查询系统以找出哪些filters是可用的。然后,对于每个filters,你都可以检索包含它的属性的字典,例如它的输入参数、默认参数值、最小值和最大值、显示名称等等。
Core Image 可以实现实时视频优化
如果你的应用需要实时处理视频,你可以做一些事情来优化性能。
使用Image Accumulator 来支持基于feedback的处理 Use an Image Accumulator to Support Feedback-Based Processing
CIImageAccumulator类是为高效的基于反馈的图像处理而设计的,如果你的应用需要图像动力系统,你可能会觉得有用。
创建一个分布式自定义核心和filters Create and Distribute Custom Kernels and Filters
如果没有内置的filters适合你的需要(即使是已经链接在一起的时候),这时也可以考虑创建一个定制filters。你需要了解内核(在像素级别上运行的程序),因为它们是每个filters的核心。 在macOS中,你可以将一个或多个自定义filters打包成一个图像单元,这样其他应用就可以加载和使用它们。
处理图片
处理图像意味着应用filters – 图像filters是一种软件,逐个像素地检查输入图像,并在算法上应用一些效果以创建输出图像。 在Core Image中,图像处理依赖于CIFilter和CIImage类,它们描述了filters及其输入和输出。 要应用filters并显示或导出结果,可以使用Core Image和其他system framework之间的集成,或使用CIContext类创建自己的render workflow渲染工作流。 下面介绍使用这些类来应用filters和渲染结果的关键概念。
app中有很多使用Core Image来处理图片的方式。下图展示了一个基本的例子,基本的为image应用一个filter:
import CoreImage
|
let context = CIContext() // 1
|
let filter = CIFilter(name: “CISepiaTone”)! // 2
|
filter.setValue(0.8, forKey: kCIInputIntensityKey)
|
let image = CIImage(contentsOfURL: myURL) // 3
|
filter.setValue(image, forKey: kCIInputImageKey)
|
let result = filter.outputImage! // 4
|
let cgImage = context.createCGImage(result, from: result.extent) // 5
|
以上代码主要做了这几件事:
- 创建了一个CIContext 对象(用默认options)。你并不总是需要你自己的Core Image Context(通常你可以与其他管理绘制的系统框架集成)。创建你自己的context可以让你更精确地控制绘制过程和绘制所涉及的资源。context是重量级的对象,所以如果你创建了一个,那么应该尽早创建,并在每次需要处理图像时重用它。
- 实例化一个CIFilter对象表示要应用的filter,并为他的参数赋值。
- 创建一个CIImage对象表示要处理的图片,把它当成filter的输入参数。通过URL读取图片数据是创建图片对象的方法之一。
- 得到一个CIImage对象,它是filter的输出。这时filter还没有执行(这个图片对象是一个“配方”说明如何用指定filter、参数、和输入创建一个图片)。Core Image只在你请求绘制的时候才调用这个配置。
- 把输出image绘制到一个你可以显示或者存储到一个文件的Core Graphic图像。
图像是Filters的输入和输出
CIFilter类的一个实例是一个表示图像处理效果的可变对象和控制该效果行为的参数。要使用filter,你可以创建CIFilter对象,设置其输入参数,然后访问其输出图像。调用filterWithName:方法以使用系统已知的filter名称实例化filter对象。
大多数filter都有一个或多个输入参数,可让你控制处理过程的完成方式。每个输入参数都有一个指定其数据类型的属性类,如NSNumber。输入参数可以有其他属性,例如默认值,允许的最小值和最大值、参数的显示名称以及CIFilter类参考中描述的其他属性。例如,CIColorMonochrome滤镜具有三个输入参数——要处理的图像、monochrome color和color intensity.
filter参数被定义为键值对。要使用参数,通常使用valueForKey:和setValue:forKey:方法或基于KVC(和Core Animation一样)的其他功能。key是一个标识属性的常量,value是与key相关的设置。Core Image属性值通常使用属性值数据类型中列出的数据类型之一。
下表是参数的数据类型:
Data Type | Object | Description |
Strings | NSString | Text, typically for display to the user |
Floating-point values | NSNumber | A scalar value, such as an intensity level or radius |
Vectors | CIVector | A set of floating-point values that can specify positions, sizes, rectangles, or untagged color component values |
Colors | CIColor | A set of color component values, tagged with a color space specifying how to interpret them |
Images | CIImage | An image; see Images are the Input and Output of Filters |
Transforms | NSData, NSAffineTransform | A coordinate transformation to apply to an image |
重要:CIFilter对象是可变的,所以你不能在线程件安全的共享它。每个线程必须创建自己的CIFilter对象。然而,一个filter的输出和输入 CIImage对象是不可变的,可以在线程间传递。
为负责效果链接filter
每个Core Image filter产生一个output CIImage对象,你可以用这个对象作为输入传给给另一个filter。比如,下图显示应用一个色彩效果到一个image,然后添加一个glow效果,最后裁剪一部分作为结果。
通过链接filter的输入和输出,组建一个filter chain:
Core Image优化了filter chain的使用。比如上面这种filter chain以快速高效地渲染结果。 链中的每个CIImage对象都不是完全渲染的图像,而仅仅是渲染的“配方”。 Core Image 不需要单独执行每个滤镜,浪费时间和内存渲染中间像素缓冲区这种事永远不会发生。 相反,Core Image 将filter组合成单个操作,甚至可以在以不同顺序应用filter时重新组织filter,从而更有效地生成相同的结果。下面显示了上图中示例filter链的更精确的变换。
请注意,在上面中,裁剪操作已从最后移至第一。 该filter会导致原始图像的大部分区域被裁剪出最终输出。 因此,不需要应用颜色并锐化这些像素的滤镜。 通过首先执行裁剪,Core Image可确保昂贵的图像处理操作仅适用于在最终输出中可见的像素。
下图显示了如何设置如上所示的filter chain:
func applyFilterChain(to image: CIImage) -> CIImage {
|
// The CIPhotoEffectInstant filter takes only an input image
let colorFilter = CIFilter(name: “CIPhotoEffectProcess”, withInputParameters:
[kCIInputImageKey: image])!
// Pass the result of the color filter into the Bloom filter
// and set its parameters for a glowy effect.
let bloomImage = colorFilter.outputImage!.applyingFilter(“CIBloom”,
withInputParameters: [
kCIInputRadiusKey: 10.0,
kCIInputIntensityKey: 1.0
])
// imageByCroppingToRect is a convenience method for
// creating the CICrop filter and accessing its outputImage.
let cropRect = CGRect(x: 350, y: 350, width: 150, height: 150)
let croppedImage = bloomImage.cropping(to: cropRect)
return croppedImage
|
}
|
上表显示了几个不同的配置和方位filter 和 他们结果的快捷方法。总之你可以用这些方法之一来应用一个filter,独立使用或者作为一个filter chain的一部分使用都可以:
- 用filterWithName方法来创建CIFilter实例,使用setValue:forKey方法(包括为处理图片的kCIIputImageKey)和访问输出图片的 outputImage 属性。
- 调用filterWithName:withInputParameters:方法来创建一个CIFilter实例和设置参数(包括输入图片),然后使用 outputImage属性来访问输出。
- 给CIImage对象使用imageByApplyingFilter:withInputParameters:方法,不用创建CIFilter实例就可以应用一个filter。
- 对于某些常用的过滤操作,如cropping, clamping, 和 applying coordinate transforms,使用其他的CIImage实例方法,通过修改现有的图像来创建图像。
使用特殊的filter 类型来获取更多的options
大部分内建的Core Image filters都在一个主 输入 image(可能还有影响处理的附加输入图像)上操作和创建一个单独的输出image。但是你可可以使用许多附加的类型来创建有趣的效果或者组合其他filter来产生更多复杂的workflows。
- 一个compositing filter(或者混合的)通过一个预规则绑定两个图片,比如:
- CISourceInCompositing filter组合图片,只有两个图片中不透明的区域在输出图片中是可见的
- CIMultiplyBlendMode filter 从两个图片中会和像素颜色,产生一个暗色的输出图像
这两个组合 filters的完整列表,请查询 CICategoryCompositeOperation category
注意:你可以在组合图片前,通过给每个图应用 几何调整(geometry adjustments)来安排输入图片。请查看 CICategoryGeometryAdjustment filter category 或者 imageByApplyingTransform: 方法.
- 一个 generator filters 不接受输入图片。相反,这种filter使用其他输入参数来从头创建新的图像。某些 gegerators 产生的输出可以被用在自己身上,其他的能被组合到filter chain中来生成更有趣的图片。在内置的Core Image filter中,有一些例子包括:
-
- 像CIQRCodeGenerator和CICode128BarcodeGenerator这样的filter会生成对指定的输入数据进行编码的条形码图像。
- 像CIConstantColorGenerator,CICheckerboardGenerator和CILinearGradient这样的filter可以从指定的颜色生成简单的过程图像。 你可以将这些滤镜与其他滤镜结合使用以获得有趣的效果 – 例如,CIRadialGradient滤镜可以创建用于CIMaskedVariableBlur滤镜的遮罩。
- 像CILenticularHaloGenerator和CISunbeamsGenerator这样的filter可以创建独立的视觉效果 – 将这些与合成滤镜结合使用,为图像添加特殊效果。
更多generator filters,查询 CICategoryGenerator and CICategoryGradient categories.
- reduction filter 操作一个输入图像,它的输出描述的是输入图像的信息,而不是传统意义上的输出图像。比如:
-
- CIAreaMaximum filter 输出一个单独的颜色值,用来表示图片中特定区域的所有最亮的像素颜色。
- CIAreaHistogram filter 输出关于图片指定区域中每个强度值的像素数量的信息。
所有Core Image filter都必须生成一个CIImage对象作为它们的输出,所以还原 filter生成的信息仍然是一个图像。 但是,通常不显示这些图像 – 相反,你可以从单像素或单行图像读取颜色值,或将它们用作其他滤镜的输入。
更多 reduction filter,查询 CICategoryReduction category.
- transition filter 有两个输入图片并根据一个独立变量改变它们之间的输出,通常,这个变量是时间,因此你可以使用 transition filter创建一个动画,以一个图像开始,结束于另一个图像,并使用有趣的视觉效果逐步从一个变化到另一个。 Core Image提供了几个内置的transition filter,包括:
-
- CIDissolveTransition filter产生一个简单的cross-dissolve(淡入淡出),从一个图像到另一个图像渐变。
- CICopyMachineTransition filter模拟复印机,在一幅图像上划过一道明亮的光线以显示另一幅图像。
有关转换 filter的完整列表,请查询CICategoryTransition category。
与其他框架集成 Integrating with Other Frameworks
Core Image在iOS macOS和tvOS中与很多其他技术集成。因为这些紧密的集成,你可以使用Core Image在你的app的界面中方便的给游戏,视频或者图片添加视觉效果,而不需要构建复杂的绘制代码。下面介绍几个普遍的在app中使用Core Image的方式,和为他们提供的便捷的系统框架。
在UIKit和AppKit中处理定格图片
UIKit和AppKit提供了一种简单的方法,可以将Core Image处理过程添加到静态图像中,无论这些图像出现在你的应用程序的UI中、还是它的工作流的一部分。例如:
- 旅行应用程序可能会在列表中显示目的地的照片,然后对这些图像应用滤镜以为每个目的地的详细信息页面创建细微的背景。
- 社交应用可能会将 filter应用于用户头像图片以指示每篇帖子的心情。
- 摄影应用程序可能允许用户在捕捉时使用 filter自定义图像,或者提供一个照片应用程序插件,用于在用户的照片库中为照片添加效果。
注意:不要使用Core Image来创建用户界面设计的模糊效果(比如在半透明的侧栏、工具栏和macOS、iOS和tvOS系统接口的背景中看到的)。相反,请参阅NSVisualEffectView(macOS)或UIVisualEffectView(ios/tvos)类,它自动匹配系统外观并提供高效的实时绘制。
在iOS和tvOS中,你可以在使用UIImage对象的任何地方应用Core Image filter。下面展示了使用图像视图的filter的简单方法:
class ViewController: UIViewController {
let filter = CIFilter(name: “CISepiaTone”,
withInputParameters: [kCIInputIntensityKey: 0.5])!
@IBOutlet var imageView: UIImageView!
func displayFilteredImage(image: UIImage) {
// Create a Core Image image object for the input image.
let inputImage = CIImage(image: image)!
// Set that image as the filter’s input image parameter.
filter.setValue(inputImage, forKey: kCIInputImageKey)
// Get a UIImage representation of the filter’s output and display it.
imageView.image = UIImage(CIImage: filter.outputImage!)
}
}
|
在macOS中,使用initWithBitmapImageRep:方法从位图图像和NSCIImageRep类创建CIImage对象,以创建可以在NSImage对象支持的任何地方使用的图像。
使用AVFoundation处理视频
AVFoundation框架提供了许多用于处理视频和音频内容的高级实用程序。 其中包括AVVideoComposition类,你可以使用该类将视频和音频轨道合并或编辑为单个播放文件。 可以使用AVVideoComposition对象在播放或导出过程中将Core Image滤镜应用于视频的每一帧,如下图:
let filter = CIFilter(name: “CIGaussianBlur”)!
|
let composition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
|
// Clamp to avoid blurring transparent pixels at the image edges
|
let source = request.sourceImage.clampingToExtent()
|
filter.setValue(source, forKey: kCIInputImageKey)
|
// Vary filter parameters based on video timing
|
let seconds = CMTimeGetSeconds(request.compositionTime)
|
filter.setValue(seconds * 10.0, forKey: kCIInputRadiusKey)
|
// Crop the blurred output to the bounds of the original image
|
let output = filter.outputImage!.cropping(to: request.sourceImage.extent)
|
// Provide the filter output to the composition
|
request.finish(with: output, context: nil)
|
})
|
当你使用videoCompositionWithAsset:applyingCIFiltersWithHandler: 方法创建合成时,你提供了一个负责将滤镜应用于每帧视频的处理程序。 AVFoundation会在播放或导出过程中自动调用你的处理程序。 在处理程序中,首先使用提供的AVAsynchronousCIImageFilteringRequest对象来检索要过滤的视频帧(以及补充信息,如帧时间),然后提供过滤后的图像以供合成使用。
要使用创建的视频合成对象进行回放,请从用作合成源的相同资源创建AVPlayerItem对象,然后将合成分配给player item的videoComposition属性。 要将合成输出到新的电影文件,请从相同的素材资源创建AVAssetExportSession对象,然后将合成分配给导出会话的videoComposition属性。
注意:上图还显示了另一种有用的Core Image技术。 默认情况下,模糊滤镜blur filter还会通过将图像像素与(在滤镜的图像处理空间中)透明像素一起模糊图像周围来柔化图像的边缘。 在某些情况下,如过滤视频时,此效果可能不理想。
为了避免这种效果,可以使用imageByClampingToExtent方法(或CIAffineClamp滤镜)在模糊之前无限扩展图像的边缘像素。这种操作(Clamping)会创建无限大小的图像,因此你还应该在模糊后裁剪图像。
用SpriteKit和SceneKit处理游戏内容
SpriteKit是一款用于构建2D游戏和其他类型应用程序的技术,其中包含高度动态的动画内容; SceneKit用于处理3D asset,渲染和动画3D场景,以及构建3D游戏。 这两种框架都提供了高性能的实时渲染,并且可以轻松地将Core Image处理添加到全部或部分场景。
在SpriteKit中,你可以使用SKEffectNode类添加Core Image filter。 要查看正在使用的此类的示例,请使用游戏模板(适用于iOS或tvOS)创建一个新的Xcode项目,选择SpriteKit作为游戏技术,并修改GameScene类中的touchesBegan:withEvent:方法以使用下面的代码:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
|
for touch in touches {
|
let sprite = SKSpriteNode(imageNamed:”Spaceship”)
|
sprite.setScale(0.5)
|
sprite.position = touch.location(in: self)
|
sprite.run(.repeatForever(.rotate(byAngle: 1, duration:1)))
|
let effect = SKEffectNode()
|
effect.addChild(sprite)
|
effect.shouldEnableEffects = true
|
effect.filter = CIFilter(name: “CIPixellate”,
|
withInputParameters: [kCIInputScaleKey: 20.0])
|
self.addChild(effect)
|
}
|
}
|
注意SKSence类自身是一个 SKEffectNode子类,所以你可以给整个SpriteKit Sence应用Core Image filter。在 ScenceKit中,SCNNode类的filters属性可以给任意3D场景应用 Core Image filter。示例代码如下:
// Find this line in the template code:
|
let ship = rootNode.childNode(withName: “ship”, recursively: true)!
|
// Add these lines after it:
|
let pixellate = CIFilter(name: “CIPixellate”,
|
withInputParameters: [kCIInputScaleKey: 20.0])!
|
ship.filters = [ pixellate ]
|
你还可以在SceneKit节点上设置动画效果——有关详细信息,请参阅filters属性的参考文档。
在SpriteKit和SceneKit中,你都可以使用transitions来添加视觉效果来更改视图的场景。 (See the presentScene:transition: method for SpriteKit and the presentScene:withTransition:incomingPointOfView:completionHandler: method for SceneKit.)使用SKTransition类及其transitionWithCIFilter:duration:initializer从任何Core Image转换筛选器创建转场动画。
处理 Core Animation Layers
In macOS, you can use the filters property to apply filters to the contents of any CALayer-backed view, and add animations that vary filter parameters over time. See Filters Add Visual Effects to OS X Views and Advanced Animation Tricks in Core Animation Programming Guide.
用 Core Image Context构建你自己的workflow
当你使用上一节中列出的技术使用Core Image filter时,这些framework将自动管理Core Image,用于处理图像并呈现结果以供显示需要的底层资源。 这种方法可以最大限度地提高这些workflow的性能,并使其更易于设置。 但是,在某些情况下,使用CIContext类自己管理这些资源更为小心合理。 通过直接管理Core Image context,你可以精确地控制应用程序的性能特征,或将Core Image与更低级别的渲染技术集成。
Core Image context 代表了CPU和GPU的运算技术、资源和需要执行filter和声场图片的设置。许多种类的context都是可用的,你应该选择最适合你的app的选项和可能需要协同处理的其他技术。
重要:Core Image Context是一个重量级对象,管理了很大数量的资源和状态。重复创建和销毁它会带来很大的性能开销,多亿如果计划执行多个图片处理操作,只创建一个context,将来重用它。
使用自动的Context绘制
如果你不局限于你的app如何和其他图形技术交互,那么创建一个 Core Image context就很简单:是需要用 init和initWithOptions方法。这么做,Core Image自动管理内部资源,基于当前设备和你指定的选项来选择最合适或最佳的可用的CPU或者GPU绘制技术。对于绘制或者输出图片到一个文件这种任务,次方法非常适用。(比如使用 writeJPEGRepresentationOfImage:toURL:colorSpace:options:error: 方法)
注意: 没有明确指定的绘制目标的context不能用drawImage:inRect:fromRect: 方法,因为此方法的行为给予使用的绘制目标而改变。使用名称前缀为render或者create的CIContext方法来指定一个明确的目标。
当你想实时绘制Core Image结果时,小心使用这个方式——因为,filter参数的动态变化、产生一个动态变换效果或者处理视频或者其他视觉内容时,这些操作已经每秒绘制很多次了。尽管用此方式创建的CIContext对象可以自动使用GPU绘制,但是表达绘制内容这个操作可能在CPU和GPU之间调用昂贵的copy 操作。
用Metal实时绘制
Metal framework 提供低消耗访问GPU、允许高性能图形绘制和平行计算workflows。这种workflow是对图像处理不可或缺的,所以Core Image在任何可能的情况下都是建立在Metal上的。如果你正在构建一个使用Metal绘制图形的app,或者你想要利用Metal来为animating filter out put或者 filtering animated input (such as live video)实时优化,应该使用 Metal device来创建你的Core Image context。
下图显示了使用MetalKit绘制Core Image output的例子。
class ViewController: UIViewController, MTKViewDelegate { // 1
// Metal resources
var device: MTLDevice!
var commandQueue: MTLCommandQueue!
var sourceTexture: MTLTexture! // 2
// Core Image resources
var context: CIContext!
let filter = CIFilter(name: “CIGaussianBlur”)!
let colorSpace = CGColorSpaceCreateDeviceRGB()
override func viewDidLoad() {
super.viewDidLoad()
device = MTLCreateSystemDefaultDevice() // 3
commandQueue = device.newCommandQueue()
let view = self.view as! MTKView // 4
view.delegate = self
view.device = device
view.framebufferOnly = false
context = CIContext(mtlDevice: device) // 5
// other setup
}
}
|
- 这个例子使用iOS或者tvOS的子类 UIViewController。macOS上,继承NSViewController。
- sourceTexture 属性持有一个Metal 纹理 包含了filter处理的图像。这个例子不设计加载纹理内容因为有很多填充纹理的方式,比如,你可以使用MTKTextureLoader类加载一个图片文件,或者使用纹理作为你先前绘制的输出。
- 为绘制创建Metal 对象——一个MTLDevice 对象表示了要使用GPU,一个命令queue来执行在GPU上的绘制和计算命令(这个命令queue可以处理被Core Image和其他你额外传入的绘制和计算命令)
- 配置MetalKit view
- 重要:使用MetalView,layer或者作为Core Image绘制目标的纹理时,总是设置 framebufferOnly属性为NO
- 创建一个使用与视图相同的Metal device的Core Image context。 通过共享Metal 资源,Core Image可以处理纹理内容并渲染到视图中,而不需要将图像数据复制到单独的CPU或GPU内存缓冲区,也不需要复制图像数据的性能成本。 CIContext对象创建起来非常昂贵,因此你每次处理图像时只做一次并重复使用它。
MetalKit在视图要显示时每次调用 drawInMTKView: 方法(默认的,MetalKit可能每秒60次的调用这个方法,详情参考View’s preferredFramesPerSecond property )。下图显示此方法基本的从Core Image context绘制的实现:
public func draw(in view: MTKView) {
if let currentDrawable = view.currentDrawable { // 1
let commandBuffer = commandQueue.commandBuffer()
let inputImage = CIImage(mtlTexture: sourceTexture)! // 2
filter.setValue(inputImage, forKey: kCIInputImageKey)
filter.setValue(20.0, forKey: kCIInputRadiusKey)
context.render(filter.outputImage!, // 3
to: currentDrawable.texture,
commandBuffer: commandBuffer,
bounds: inputImage.extent,
colorSpace: colorSpace)
commandBuffer.present(currentDrawable) // 4
commandBuffer.commit()
}
}
|
- 获取要呈现的Metal可绘制纹理和命command buffer以对渲染命令进行编码。
- 配置filter的输入参数,包括来自Metal纹理的输入图像。 本示例使用常量参数,但请记住,此方法每秒最多运行60次 – 你可以使用此机会随时间改变filter参数以创建流畅的动画。
- 告诉Core Image Context将滤镜输出渲染到视图的可绘制纹理中。 bounds参数告诉Core Image要绘制图像的哪个部分 – 本例使用输入图像的尺寸。
- 当command buffer结束执行时,通知 Metal显示渲染图像。
此示例仅显示使用Metal渲染Core Image所需的最少代码。 在真实的应用程序中,你可能会在Core Image管理的之前或之后执行其他渲染过程,或者将Core Image输出渲染为辅助纹理,并在另一个渲染过程中使用该纹理。 有关使用Metal绘图的更多信息,请参阅Metal Programming Guide。
用OpenGL和OpenGL ES实时绘制
Core Image还可以使用OpenGL(macOS)或OpenGL ES(iOS和tvOS)进行基于GPU的高性能渲染。如果你需要支持Metal不可用的旧硬件,或者要将Core Image集成到现有的OpenGL或OpenGL ES工作流程中,请使用此选项。
- 如果你使用OpenGL ES(iOS或tvOS)进行绘制,请使用contextWithEAGLContext:options:方法,从你用于渲染的EAGLContext创建一个Core Image Context。
- 如果使用OpenGL(在macOS中)绘制,请使用contextWithCGLContext:pixelFormat:colorSpace:options:方法,从你用于渲染的OpenGL Context创建一个Core Image Context。
在任一情况下,使用imageWithTexture:size:flipped:colorSpace:方法来从OpenGL或OpenGL ES纹理创建CIImage对象。使用GPU内存中已有的图像数据可通过消除冗余复制操作来提高性能。
要在OpenGL或OpenGL ES中渲染Core Image输出,请使你的GL Context为 current 状态并设置一个destination frame-buffer,然后调用drawImage:inRect:fromRect:方法。
用Quartz 2D进行给予CPU的绘制
如果您的应用不需要实时性能并使用CoreGraphics绘制视图内容(例如,在UIKit或AppKit视图的drawRect:方法中),请使用contextWithCGContext:options:方法来创建直接工作的Core Image Context 与您已经用于其他绘图的Core Graphics Context相关联。 (在macOS中,改为使用当前NSGraphicsContext对象的CIContext属性。)有关CoreGraphics上下文的信息,请参阅Quartz 2D Programming Guide.。
查询系统的filters
Core Image提供了让你查询系统内建filters的方法和关于每个filter的关联信息——display name, input parameters, parameter types, defaults values等等。查询提供了大部分可用的关于现有filter的信息。
拿到filter和属性的列表
使用filterNamesInCategory: and filterNamesInCategories:方法来发现你要的可用的filters。filters是分类组织的以便更方便的管理。如果你已知一个filter的category,通过使用filterNamesImCategory方法找到category中可用的filters,下面的表格中列出了类别常量。
如果你想为一个categories列表找到所有可用filters,你可以使用 filterNamesInCategories方法,此方法提供一个category 常量数组,内有filter names。如果此方法参数为nil,你将会得到一个所有category的所有filters列表。
一个filter可以是多个category的成员。一个category能指定以下信息:
- filter产生的效果类型(color adjustment, distortion, and so forth)
- filter的作用(still image, video, high dynamic range, and so forth)
- 是否是Core Image提供的(内建的)
效果类型常量:
Effect type
|
Indicates
|
kCICategoryDistortionEffect
|
Distortion effects, such as bump, twirl, hole,变形效果,比如隆起,旋转,凹陷
|
kCICategoryGeometryAdjustment
|
Geometry adjustment, such as affine transform, crop, perspective transform 几何调整,比如 affine transform,裁剪,透视变换
|
kCICategoryCompositeOperation
|
组合、合并,比如作浮在上面,最小化,色彩减淡模式 Compositing, such as source over, minimum, source atop, color dodge blend mode
|
kCICategoryHalftoneEffect
|
半色调效果Halftone effects, such as screen, line screen, hatched
|
kCICategoryColorAdjustment
|
Color adjustment, such as gamma adjust, white point adjust, exposure
|
kCICategoryColorEffect
|
色彩效果Color effect, such as hue adjust, posterize
|
kCICategoryTransition
|
图片间变换 Transitions between images, such as dissolve, disintegrate with mask, swipe
|
kCICategoryTileEffect
|
珊格效果 ?Tile effect, such as parallelogram, triangle
|
kCICategoryGenerator
|
图片生成器 Image generator, such as stripes, constant color, checkerboard
|
kCICategoryGradient
|
渐变Gradient, such as axial, radial, Gaussian
|
kCICategoryStylize
|
风格化?Stylize, such as pixellate, crystallize
|
kCICategorySharpen
|
锐化Sharpen, luminance
|
kCICategoryBlur
|
虚化Blur, such as Gaussian, zoom, motion
|
filter用法常量:
Use
|
Indicates
|
kCICategoryStillImage
|
定格图片Can be used for still images
|
kCICategoryVideo
|
用于视频Can be used for video
|
kCICategoryInterlaced
|
交错图片Can be used for interlaced images
|
kCICategoryNonSquarePixels
|
非方形像素 Can be used for nonsquare pixels
|
kCICategoryHighDynamicRange
|
高动态范围 Can be used for high-dynamic range pixels
|
内建 filter常量:
Filter origin
|
Indicates
|
kCICategoryBuiltIn
|
Core Image内建filter A filter provided by Core Image
|
获得filter名称列表之后,你可以创建一个CIFilter对象、调用 attribute 方法来处理filter的属性,如下:
CIFilter *myFilter = [CIFilter filterWithName:@”<# Filter Name Here #>”];
|
NSDictionary *myFilterAttributes = [myFilter attributes];
|
属性包括name, categories, class, minimum, and maximum等,更多列表参考 CIFilter Class Reference 。
构建一个filters字典
如果你的app有一个用户界面,它能尝试得到一个filter字典来创建和更新用户界面。比如,filter 属性如果是Boolean 那么将会需要一个checkbox或者类似元素,如果属性会持续修改那么需要一个slider。你可以用text label来使用最大最小值。默认属性设置会决定用户界面的初始设置。
filter name和属性提供了构建允许用户选择filter和控制filter输入参数的、所有你需要的信息。一个filter的attribute告诉你filter有多少参数,参数名,数据类型和最大、最小、默认值。
注意:如果你对构建Core Image filter用户界面有兴趣,参考:IKFilterUIView Class Reference ,它提供了Core Image filter的输入参数控件。
下表显示了获得filter名称并通过功能类别构建filter词典的代码。code在kCICategoryGeometryAdjustment, kCICategoryDistortionEffect, kCICategorySharpen, and kCICategoryBlur这些类别中处理filter(但是基于app构建dictionary),使用这些类别的函数category,比如 Distortion和Focus。Functional categories对于在一个对用户有意义的菜单中组织filter名称非常有用。下面代码不迭代所有可能的filter categories,但是你可以轻易的扩展这个代码。
通过 functional categories构建dictionary:
NSMutableDictionary *filtersByCategory = [NSMutableDictionary dictionary];
|
NSMutableArray *filterNames = [NSMutableArray array];
|
[filterNames addObjectsFromArray:
|
[CIFilter filterNamesInCategory:kCICategoryGeometryAdjustment]];
|
[filterNames addObjectsFromArray:
|
[CIFilter filterNamesInCategory:kCICategoryDistortionEffect]];
|
filtersByCategory[@”Distortion”] = [self buildFilterDictionary: filterNames];
|
[filterNames removeAllObjects];
|
[filterNames addObjectsFromArray:
|
[CIFilter filterNamesInCategory:kCICategorySharpen]];
|
[filterNames addObjectsFromArray:
|
[CIFilter filterNamesInCategory:kCICategoryBlur]];
|
filtersByCategory[@”Focus”] = [self buildFilterDictionary: filterNames];
|
下表显示了上面的 buildFilterDictionary 方式调用。这个方式为functional category内的每个filter构建一个属性dictionary。
通过functional名,构建一个filter的dictionary:
– (NSMutableDictionary *)buildFilterDictionary:(NSArray *)filterClassNames // 1
|
{
|
NSMutableDictionary *filters = [NSMutableDictionary dictionary];
|
for (NSString *className in filterClassNames) { // 2
|
CIFilter *filter = [CIFilter filterWithName:className]; // 3
|
if (filter) {
|
filters[className] = [filter attributes]; // 4
|
} else {
|
NSLog(@”could not create ‘%@’ filter”, className);
|
}
|
}
|
return filters;
|
}
|
- 将一组filter名称作为输入参数。
- 遍历filter名称数组。
- 检索filter名称的filter对象。
- 检索filter的属性字典并将其添加到例程返回的字典中。
注意,10.5以上可以用 CIFilter Image Kit additions来通过一个filter的浏览和设置filter的视图。参考 ImageKit Programming Guide.
太长了,分成上下两部。