处理图像
处理图像意味着应用滤镜 - 图像滤镜是逐像素检查输入图像,在算法上应用某种效果以创建输出图像。 在Core Image中,图像处理依赖于CIFilter和CIImage类,它们描述了滤镜及其输入和输出。 要应用滤镜并显示或导出结果,可以使用Core Image和其他系统框架之间的集成,或使用CIContext类创建您自己的渲染工作流程。 本章介绍使用这些类来应用滤镜和渲染结果的关键概念。
CoreImage拥有三个类来支持图像处理:
CIFilter,是个可变对象,它代表了一种效果。一个滤镜对象至少有一个输入参数并且产生一个输出图像。
CIImage,是一个不可变对象,它表示一个图像。一个CIImage所需的图像数据,你可以直接合成,或者从一个文件中提供出来,也可以从一个CIFilter对象的输出来获取。
CIContext,是Core Image绘制滤镜产生的结果的地方。Core Image上下文可以基于CPU或者GPU。
概览
在您的应用程序中使用Core Image进行图像处理有很多方法。基本示例
// 创建一个CIContext对象
CIContext *context = [CIContext contextWithOptions:nil];
// 创建一个CIImage对象。你可以通过各种资源创建CIImage
CIImage *image = [CIImage imageWithContentsOfURL:myURL];
// 创建一个滤镜,然后设置它的输入参数
CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"];
[filter setValue:image forKey:kCIInputImageKey];
[filter setValue:@0.8f forKey:kCIInputIntensityKey];
// 获取输出图像,该图像只是如何产生图像的一种方式,它还没有被渲染
CIImage *result = [filter valueForKey:kCIOutputImageKey];
CGRect extent = [result extent];
// 将CIImage渲染成CoreGraphics图像用来显示或者保存为文件
CGImageRef cgImage = [context createCGImage:result fromRect:extent];
图像是滤镜的输入和输出
Core Image滤镜处理和生成Core Image图像。 CIImage实例是表示图像的不可变对象。这些对象不直接表示图像位图数据 - 相反,CIImage对象是用于生成图像的“方法”。一个方法可能需要从文件加载图像;另一个可能代表滤镜或滤镜链的输出。Core Image只有在您要求呈现图像以供显示或输出时才执行这些ff。
要应用滤镜,请创建一个或多个表示要由滤镜处理的图像的CIImage对象,并将它们分配给滤镜的输入参数(例如kCIInputImageKey)。你几乎可以从任何图像数据源创建一个Core Image图像对象,其中包括:
- 引用要加载的图像文件的URL或包含图像文件数据的NSData对象
Quartz2D,UIKit或AppKit图像表示(CGImageRef,UIImage或NSBitmapImageRep对象) - Metal,OpenGL或OpenGL ES纹理
- CoreVideo图像或像素缓冲区(CVImageBufferRef或CVPixelBufferRef)
- 在进程之间共享图像数据的IOSurfaceRef对象
- 内存中的图像位图数据(指向此类数据的指针或按需提供数据的CIImageProvider对象)
有关创建CIImage对象的完整列表,请参阅CIImage类参考。
由于CIImage对象描述如何生成图像(而不是包含图像数据),因此它也可以表示滤镜输出。当你访问CIFilter对象的outputImage属性时,Core Image仅会识别并存储执行滤镜所需的步骤。这些步骤仅在你请求将图像渲染以供显示或输出时执行。你可以使用CIContextrender或绘图方法之一(请参阅使用Core Image上下文构建自己的工作流程)或通过使用与Core Image一起工作的许多系统框架之一显示图像来显式请求渲染(请参阅集成与其他框架)。
延迟处理直至渲染时间使Core Image快速高效。在渲染时,Core Image可以查看是否需要将多个滤镜应用于图像。如果是这样,它会自动连接多个“配方”并组织它们以消除冗余操作,因此每个像素只处理一次而不是多次。
滤镜描述图像处理效果
CIFilter类的一个实例是一个表示图像处理效果的可变对象以及控制该效果行为的任何参数。要使用滤镜,您可以创建CIFilter对象,设置其输入参数,然后访问其输出图像(请参阅图像是以下滤镜的输入和输出)。调用filterWithName:initializer以使用系统已知的滤镜名称实例化滤镜对象(请参阅查询滤镜或Core Image滤镜参考的系统)。
大多数滤镜都有一个或多个输入参数,可让你控制处理的完成方式。每个输入参数都有一个指定其数据类型的属性类,如NSNumber。输入参数可以有其他属性,例如默认值,允许的最小值和最大值,参数的显示名称以及CIFilter类参考中描述的其他属性。例如,CIColorMonochrome滤镜具有三个输入参数 - 要处理的图像,单色和颜色强度。
滤镜参数被定义为键值对;要使用参数,通常使用valueForKey:和setValue:forKey:方法或基于键值编码(例如Core Animation)的其他功能。key是标识属性的常数,value是与key关联的设置。Core Image属性值通常使用属性值数据类型中列出的数据类型之一。
重要提示:CIFilter对象是可变的,因此你不能在不同线程之间安全地共享它们。 每个线程都必须创建自己的
CIFilter对象。 但是,滤镜的输入和输出CIImage对象是不可变的,因此可以安全地在线程之间传递。
复杂效果的滤镜链
每个Core Image滤镜都会生成一个输出CIImage对象,因此您可以将此对象用作另一个滤镜的输入。 例如,图中所示的滤镜序列将颜色效果应用于图像,然后添加发光效果,最后从结果中裁剪出一部分。
图中通过连接滤镜输入和输出来构建滤镜链
1-1.pngCore Image优化了滤镜链的应用,例如这种滤镜链以快速高效地渲染结果。 链中的每个CIImage对象都不是完全渲染的图像,而仅仅是渲染的“方式”。Core Image不需要单独执行每个滤镜,浪费时间和内存渲染永远不会看到的中间像素缓冲区。 相反,Core Image将滤镜组合成单个操作,甚至可以在以不同顺序应用滤镜时重新组织滤镜,从而更有效地生成相同的结果。 图1-2更精确的展示了图1-1中示例的滤镜链。
图1-2 Core Image将滤镜链优化为单个操作
1-2.png
请注意,在图1-2中,裁剪操作已从上一次移至第一次。 该滤镜会导致原始图像的大部分区域被裁剪成最终输出。 因此,不需要应用颜色并锐化这些像素的滤镜。 通过执行裁剪,Core Image确保了高成本的只有像素级的图像处理操作在最终输出中可见。
下面示例如何设置如上所示的滤镜链。
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
}
以上代码还展示了一些用于配置滤镜和访问其结果的不同方法。总之,你可以使用这些方法中的任何一种来单独或作为滤镜链的一部分应用滤镜:
- 使用filterWithName:initializer创建CIFilter实例,使用setValue:forKey:方法(包括要处理的图像的kCIInputImageKey)设置参数,并使用outputImage属性访问输出图像。
- 使用filterWithName:withInputParameters:initializer创建一个CIFilter实例并将其参数(包括输入图像)设置为一次调用,然后使用outputImage属性访问输出。
- 通过将imageByApplyingFilter:withInputParameters:方法应用于CIImage对象,应用滤镜而不创建CIFilter实例。
- 对于某些常用的滤镜操作(如剪切,夹紧和应用坐标变换),请使用通过修改现有图像创建图像中列出的其他CIImage实例方法。
使用特殊滤镜类型获取更多选项
大多数内置的Core Image滤镜都在主输入图像上运行(可能会附加影响处理的输入图像)并创建单个输出图像。但还有其他几种类型可用于创建有趣的效果,或与其他滤镜结合使用以生成更复杂的工作流程。
-
合成(或混合)滤镜根据预设公式组合两个图像。例如:
CISourceInCompositing滤镜组合图像,使得只有输入图像中不透明的区域在输出图像中可见。 CIMultiplyBlendMode滤镜将两个图像的像素颜色相乘,产生一个变暗的输出图像。 有关合成滤镜的完整列表,请查询CICategoryCompositeOperation类别。
注意:您可以在合成之前安排输入图像,方法是对每个图像应用几何调整。请参阅CICategoryGeometryAdjustment筛选器类别或imageByApplyingTransform:方法。
-
generator 滤镜不输入图像。相反,这些滤镜使用其他输入参数从头开始创建新图像。一些发生器产生的输出可以独立使用,其他发生器可以组合成滤波器链以产生更有趣的图像。内置Core Image滤镜中的一些示例包括:
像CIQRCodeGenerator和CICode128BarcodeGenerator这样的滤镜会生成对指定的输入数据进行编码的条形码图像。 像CIConstantColorGenerator,CICheckerboardGenerator和CILinearGradient这样的滤镜可以从指定的颜色生成简单的过程图像。 您可以将这些滤镜与其他滤镜结合使用以获得有趣的效果 - 例如,CIRadialGradient滤镜可以创建用于CIMaskedVariableBlur滤镜的遮罩。 像CILenticularHaloGenerator和CISunbeamsGenerator这样的滤镜可以创建独立的视觉效果 - 将这些与合成滤镜结合使用,为图像添加特殊效果。
要查找生成器滤镜,请查询CICategoryGenerator和CICategoryGradient类别。
-
缩减滤镜对输入图像进行操作,但不是以传统意义创建输出图像,而是其输出描述关于输入图像的信息。例如:
CIAreaMaximum滤镜输出表示图像指定区域中所有像素颜色中最亮的单个颜色值。 CIAreaHistogram滤镜输出有关图像指定区域中每个强度值的像素数量的信息。
所有Core Image滤镜都必须生成一个CIImage对象作为它们的输出,所以由还原滤镜生成的信息仍然是一个图像。但是,通常不显示这些图像 - 相反,您可以从单像素或单行图像读取颜色值,或将它们用作其他滤镜的输入。
有关减少滤镜的完整列表,请查询CICategoryReduction类别。
-
一个转换滤镜接受两个输入图像,并根据一个独立变量改变它们之间的输出 - 通常,这个变量是时间,因此您可以使用转换滤镜创建一个动画,以一个图像开始,结束于另一个图像,并前进从一个到另一个使用有趣的视觉效果。Core Image提供了几个内置的过渡滤镜,包括:
CIDissolveTransition滤镜产生一个简单的交叉消解,从一个图像到另一个图像渐变。 CICopyMachineTransition滤镜模拟复印机,在一幅图像上划过一道明亮的光线以显示另一幅图像。
有关转换滤镜的完整列表,请查询CICategoryTransition类别。
网友评论