版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.01.29 |
前言
Core Image是IOS5中新加入的一个框架,里面提供了强大高效的图像处理功能,用来对基于像素的图像进行操作与分析。还提供了很多强大的滤镜,可以实现你想要的效果,下面我们就一起解析一下这个框架。感兴趣的可以参考上面几篇。
1. Core Image框架详细解析(一) —— 基本概览
2. Core Image框架详细解析(二) —— Core Image滤波器参考
3. Core Image框架详细解析(三) —— 关于Core Image
4. Core Image框架详细解析(四) —— Processing Images处理图像(一)
5. Core Image框架详细解析(五) —— Processing Images处理图像(二)
6. Core Image框架详细解析(六) —— 图像中的面部识别Detecting Faces in an Image(一)
7. Core Image框架详细解析(七) —— 自动增强图像 Auto Enhancing Images
8. Core Image框架详细解析(八) —— 查询系统中的过滤器 Querying the System for Filters
9. Core Image框架详细解析(九) —— 子类化CIFilter:自定义效果的配方 Subclassing CIFilter: Recipes for Custom Effects(一)
10. Core Image框架详细解析(十) —— 子类化CIFilter:自定义效果的配方 Subclassing CIFilter: Recipes for Custom Effects(二)
11. Core Image框架详细解析(十一) —— 获得最佳性能 Getting the Best Performance
12. Core Image框架详细解析(十二) —— 使用反馈处理图像 Using Feedback to Process Images
13. Core Image框架详细解析(十三) —— 在写一个自定义滤波器之前你需要知道什么?
14. Core Image框架详细解析(十四) —— 创建自定义滤波器 Creating Custom Filters(一)
Using Your Own Custom Filter - 使用你的自定义滤波器
使用自定义过滤器的过程与使用Core Image提供的任何过滤器的过程相同,只不过您必须初始化过滤器类。 您使用以下代码行初始化上一节中创建的haze removal filter
类:
[MyHazeFilter class];
Listing 9-8
显示了如何使用haze removal filter
。 请注意此代码与Processing Images中讨论的代码之间的相似性。
注意:如果您将过滤器打包为图像单元,则需要加载它。 详情请参阅Processing Images。
// Listing 9-8 Using your own custom filter
- (void)drawRect: (NSRect)rect
{
CGRect cg = CGRectMake(NSMinX(rect), NSMinY(rect),
NSWidth(rect), NSHeight(rect));
CIContext *context = [[NSGraphicsContext currentContext] CIContext];
if(filter == nil) {
NSURL *url;
[MyHazeFilter class];
url = [NSURL fileURLWithPath: [[NSBundle mainBundle]
pathForResource: @"CraterLake" ofType: @"jpg"]];
filter = [CIFilter filterWithName: @"MyHazeRemover"
withInputParameters:@{
kCIInputImageKey: [CIImage imageWithContentsOfURL: url],
kCIInputColorKey: [CIColor colorWithRed:0.7 green:0.9 blue:1],
}];
}
[filter setValue: @(distance) forKey: @"inputDistance"];
[filter setValue: @(slope) forKey: @"inputSlope"];
[context drawImage: [filter valueForKey: kCIOutputImageKey]
atPoint: cg.origin fromRect: cg];
}
Supplying an ROI Function - 提供ROI功能
感兴趣区域或ROI定义了采样器采用像素信息提供给内核进行处理的源中的区域。 回想一下在Querying the System for Filters讨论的感兴趣The Region of Interest过滤器的ROI和DOD的工作空间坐标是否完全一致,是相互依赖还是不相关。 Core Image总是假设ROI和DOD重合。 如果您所编写的过滤器属于这种情况,则不需要提供ROI功能。 但是,如果这个假设对于你写的过滤器来说不成立,那么你必须提供一个ROI函数。 此外,您只能为CPU可执行过滤器提供ROI功能。
Note: The ROI and domain of definition for a CPU nonexecutable filter must coincide. This is also the case for color kernels described by the CIColorKernel class. You can’t supply an ROI function for these types of filter. See Writing Nonexecutable Filters. 注意:CPU不可执行过滤器的ROI和定义域必须重合。 CIColorKernel类描述的颜色内核也是这种情况。 您无法为这些类型的过滤器提供ROI功能。 请参阅Writing Nonexecutable Filters。
您提供的ROI函数计算内核使用的每个采样器的感兴趣区域。Core Image调用您的ROI函数,将采样器索引,渲染区域的范围以及您的例程所需的任何数据传递给它。 在OS X v10.11及更高版本以及iOS 8.0和更高版本中,推荐的应用过滤器的方法是applyWithExtent:roiCallback:arguments:或aapplyWithExtent:roiCallback:inputImage:arguments:方法,将回调函数作为块 (Objective-C)或者关闭(Swift)。
Note: In OS X v10.10 and earlier, use the setROISelector: method to provide an ROI function before calling the filter’s apply: or apply:arguments:options:method. The discussions below assume the OS X v10.11 and iOS 8.0 API; however, the inner workings of each example ROI function are the same for both the newer and older APIs.For details on the selector form of an ROI function, see the reference documentation for the setROISelector: method. 注意:在OS X v10.10及更早版本中,在调用过滤器的
apply:
或apply:arguments:options:
方法之前,使用setROISelector:
方法提供一个ROI函数。 下面的讨论假设OS X v10.11和iOS 8.0 API,然而,每个示例ROI函数的内部工作对于较新和较旧的API都是相同的。有关ROI函数的选择器形式的详细信息,请参阅setROISelector:
方法的参考文档。
ROI回调是签名符合CIKernelROICallback类型的callback
或closure
。 该块有两个参数:第一个index
,指定方法计算ROI的采样器,第二个rect
,指定需要ROI信息的区域的范围。Core Image每次通过过滤器都会调用您的例程。 您的方法基于传递给它的矩形计算ROI,并返回指定为CGRect结构的ROI。
接下来的部分提供了ROI功能的例子。
1. A Simple ROI Function - 一个简单的ROI函数
如果您的ROI函数不需要在userInfo
参数中传递数据,则不需要包含该参数,如Listing 9-9所示。Listing 9-9中的代码将采样器置换一个像素,这是由边缘检测过滤器或任何3x3卷积使用的计算
// Listing 9-9 A simple ROI function
CIKernelROICallback callback = ^(int index, CGRect rect) {
return CGRectInset(rect, -1.0, -1.0);
};
请注意,这个函数忽略index
值。 如果你的内核只使用一个采样器,那么你可以忽略这个索引。 如果您的内核使用多个采样器,则必须确保您返回适用于指定采样器的ROI。 你会在后面的章节看到如何做到这一点。
2. An ROI Function for a Glass Distortion Filter - Glass Distortion滤波器的ROI函数
Listing 9-10显示了一个glass distortion
滤波器的ROI函数。这个函数为两个采样器返回一个ROI。采样器0表示要变形的图像,采样器1表示用于玻璃的纹理。
与块(Objective-C)或闭包(Swift)的其他用法一样,ROI回调可以从定义的上下文中获取状态。您可以使用此行为为您的例程提供附加参数,如本例所示:外部值scale
控制由ROI函数应用的插入。 (当使用较旧的setROISelector:API时,可以通过传递给apply:arguments:options:方法的option
字典中的kCIApplyOptionUserInfo键来提供这样的值。
所有的玻璃纹理(采样器1)都需要被引用,因为过滤器使用纹理作为矩形图案。结果,函数返回一个不确定的矩形作为ROI。一个不确定的矩形是一个约定,指示使用所有的采样器。 (常量CGRectInfinite在Quartz 2D API
中定义。)
Note: If you use an infinite ROI make sure that the sampler’s domain of definition is not also infinite. Otherwise, Core Image will not be able to render the image. 注意:如果您使用不确定的ROI,请确保采样器的定义域不是无限的。 否则,Core Image将无法呈现图像。
// Listing 9-10 An ROI function for a glass distortion filter
float scale = 1.0f;
CIKernelROICallback distortionCallback = ^(int index, CGRect rect) {
if (index == 0) {
CGFloat s = scale * 0.5f;
return CGRectInset(rect, -s,-s);
}
return CGRectInfinite;
}
3. An ROI Function for an Environment Map - 环境映射的ROI函数
Listing 9-11显示了一个ROI函数,该函数返回使用三个采样器的内核的ROI,其中一个是环境映射。 采样器0和采样器1的ROI与DOD一致。 出于这个原因,代码返回除采样器2以外的采样器传递给它的destination
矩形。
Sampler 2
使用捕获的值来指定环境贴图的大小,以创建指定感兴趣区域的矩形。
// Listing 9-11 Supplying a routine that calculates the region of interest
CGRect sampler2ROI = CGRectMake(0, 0, envMapWidth, envMapHeight);
CIKernelROICallback envMapROICallback = ^(int index, CGRect rect) {
if (samplerIndex == 2) {
return sampler2ROI;
}
return destination;
};
4. Specifying Sampler Order - 指定采样器顺序
正如你从前面的例子看到的,一个采样器有一个与之相关的索引。 当您提供ROI功能时,Core Image会将采样器索引传递给您。 采样器索引在传递给过滤器的apply
方法时根据其顺序进行分配。 您可以从过滤器的outputImage
例程中调用apply
,如Listing 9-12所示。
在这个清单中,请注意特别是设置采样器的编号的代码行,并显示如何将它们提供给内核。 Listing 9-12给出了每一行的详细解释
// Listing 9-12 An output image routine for a filter that uses an environment map
- (CIImage *)outputImage
{
int i;
CISampler *src, *blur, *env; // 1
CIVector *envscale;
CIKernel *kernel;
src = [CISampler samplerWithImage:inputImage]; // 2
blur = [CISampler samplerWithImage:inputHeightImage]; // 3
env = [CISampler samplerWithImage:inputEnvironmentMap]; // 4
envscale = [CIVector vectorWithX:[inputEMapWidth floatValue]
Y:[inputEMapHeight floatValue]];
i = [inputKind intValue];
if ([inputHeightInAlpha boolValue]) {
i += 8;
}
kernel = roundLayerKernels[i];
return [kernel applyWithExtent: [self extent] // 5
roiCallback: envMapROICallback // 6
arguments: @[ // 7
blur,
env,
@( pow(10.0, [inputSurfaceScale floatValue]) ),
envscale,
inputEMapOpacity,
]];
}
代码要做的事如下:
- 为内核所需的三个采样器中的每一个声明变量。
- 为输入图像设置采样器。这个采样器的ROI与DOD相符。
- 为用于输入高度的图像设置采样器。这个采样器的ROI与DOD相符。
- 为
environment map
设置采样器。这个采样器的ROI不符合DOD,这意味着你必须提供一个ROI函数。
- 为
- 使用以下选项将内核应用于生成Core Image图像(CIImage对象)。
- 需要使用内核的ROI函数。
-
- 内核函数的参数,必须与内核函数的函数签名类型兼容。 (内核函数源在这里没有显示,假设它们在这个例子中是类型兼容的)。
- 采样器参数的顺序决定了它的索引。提供给内核的第一个采样器是索引0,在这种情况下,这是
src
采样器。提供给kernel-blur
的第二个采样器分配索引1,第三个采样器env
分配索引2。检查您的ROI函数以确保为每个采样器提供适当的ROI是很重要的。
Writing Nonexecutable Filters - 编写不可执行的过滤器
保证CPU不可执行的过滤器是安全的。由于此类过滤器只能在GPU上运行,因此无法进行病毒或特洛伊木马活动或其他恶意行为。为了保证安全,CPU不可执行的过滤器有这些限制:
- 这种类型的过滤器是一个纯粹的内核,这意味着它完全包含在
.cikernel
文件中。因此,它没有过滤器类,并且限制了它可以提供的处理类型。以下形式的采样指令是对非可执行过滤器有效的唯一采样指令类型:
color = sample(someSrc,samplerCoord(someSrc));
- 必须将CPU不可执行的过滤器打包为图像单元的一部分。
- Core Image假设ROI与DOD相符。这意味着不可执行的滤镜不适合模糊或失真等效果。
CIDemoImageUnit示例在MyKernelFilter.cikernel
文件中包含一个不可执行的过滤器。加载图像单元时,MyKernelFilter
过滤器将与图像单元中的FunHouseMirror
过滤器一起加载。然而,FunHouseMirror
是一个可执行的过滤器。它有一个Objective-C部分以及一个内核部分。
当您编写不可执行的过滤器时,需要在Descriptions.plist
文件中为图像单元包提供所有过滤器属性。Listing 9-13
显示了CIDemoImageUnit
示例中MyKernelFilter
的属性。
// Listing 9-13 The property list for the MyKernelFilter nonexecutable filter
<key>MyKernelFilter</key>
<dict>
<key>CIFilterAttributes</key>
<dict>
<key>CIAttributeFilterCategories</key>
<array>
<string>CICategoryStylize</string>
<string>CICategoryVideo</string>
<string>CICategoryStillImage</string>
</array>
<key>CIAttributeFilterDisplayName</key>
<string>MyKernelFilter</string>
<key>CIInputs</key>
<array>
<dict>
<key>CIAttributeClass</key>
<string>CIImage</string>
<key>CIAttributeDisplayName</key>
<string>inputImage</string>
<key>CIAttributeName</key>
<string>inputImage</string>
</dict>
<dict>
<key>CIAttributeClass</key>
<string>NSNumber</string>
<key>CIAttributeDefault</key>
<real>8</real>
<key>CIAttributeDisplayName</key>
<string>inputScale</string>
<key>CIAttributeIdentity</key>
<real>8</real>
<key>CIAttributeMin</key>
<real>1</real>
<key>CIAttributeName</key>
<string>inputScale</string>
<key>CIAttributeSliderMax</key>
<real>16</real>
<key>CIAttributeSliderMin</key>
<real>1</real>
</dict>
<dict>
<key>CIAttributeClass</key>
<string>NSNumber</string>
<key>CIAttributeDefault</key>
<real>1.2</real>
<key>CIAttributeDisplayName</key>
<string> inputGreenWeight </string>
<key>CIAttributeIdentity</key>
<real>1.2</real>
<key>CIAttributeMin</key>
<real>1</real>
<key>CIAttributeName</key>
<string>inputGreenWeight</string>
<key>CIAttributeSliderMax</key>
<real>3.0</real>
<key>CIAttributeSliderMin</key>
<real>1</real>
</dict>
</array>
</dict>
<key>CIFilterClass</key>
<string>MyKernelFilter</string>
<key>CIHasCustomInterface</key>
<false/>
<key>CIKernelFile</key>
<key> MyKernelFilter </key>
Kernel Routine Examples - 内核例程
任何图像处理滤镜的本质是执行像素计算的内核。 本节中的代码清单显示了这些过滤器的一些典型内核例程:增亮,乘法和空洞失真。 通过查看这些内容,您可以了解如何编写自己的内核例程。 但是,请注意,这些例程是例子。 不要以为这里显示的代码是Core Image用于它所提供的过滤器的内容。
在编写自己的内核例程之前,您可能需要阅读Expressing Image Processing Operations in Core Image,以查看哪些操作在Core Image中构成挑战。 你也想看看Core Image Kernel Language Reference。
您可以在Image Unit Tutorial中找到有关编写内核的深入信息以及更多示例。
1. Computing a Brightening Effect - 计算一个明亮的效果
Listing 9-14计算了一个增亮效果。 列表后面会出现每行代码的详细说明。
// Listing 9-14 A kernel routine that computes a brightening effect
kernel vec4 brightenEffect (sampler src, float k)
{
vec4 currentSource = sample (src, samplerCoord (src)); // 1
currentSource.rgb = currentSource.rgb + k * currentSource.a; // 2
return currentSource; // 3
}
代码作用如下:
- 在采样器中查找与当前输出位置关联的源像素。
- 为像素值添加一个偏差。 偏移量由像素的alpha值进行缩放,以确保像素值是预乘。
- 返回已更改的像素。
2. Computing a Multiply Effect - 计算乘法效应
Listing 9-15显示了计算乘法效果的内核例程。 代码在采样器中查找源像素,然后将其乘以传递给例程的值。
// Listing 9-15 A kernel routine that computes a multiply effect
kernel vec4 multiplyEffect (sampler src, __color mul)
{
return sample (src, samplerCoord (src)) * mul;
}
3. Computing a Hole Distortion - 计算孔畸变
Listing 9-16显示了计算空洞失真的内核例程。 请注意,有许多方法可以计算空洞失真。 清单后面出现每行编号的详细说明。
Listing 9-16 A kernel routine that computes a hole distortion
kernel vec4 holeDistortion (sampler src, vec2 center, vec2 params) // 1
{
vec2 t1;
float distance0, distance1;
t1 = destCoord () - center; // 2
distance0 = dot (t1, t1); // 3
t1 = t1 * inversesqrt (distance0); // 4
distance0 = distance0 * inversesqrt (distance0) * params.x; // 5
distance1 = distance0 - (1.0 / distance0); // 6
distance0 = (distance0 < 1.0 ? 0.0 : distance1) * params.y; // 7
t1 = t1 * distance0 + center; // 8
return sample (src, samplerTransform (src, t1)); // 9
}
这里是代码的作用:
- 采用三个参数 - 采样器,指定孔失真中心的矢量和包含
(1/radius, radius)
的params
矢量。
- 采用三个参数 - 采样器,指定孔失真中心的矢量和包含
- 从中心创建矢量t1到当前的工作坐标。
- 将距离中心的距离平方,并将值赋予
distance0
变量。
- 将距离中心的距离平方,并将值赋予
- 规范化
t1
。 (使t1
成为单位向量)
- 规范化
- 从中心计算参数距离
(distance squared * 1/distance) * 1/radius
。 该值在中心为0,在距离等于半径的位置为1。
- 从中心计算参数距离
- 用适当的扭曲创建一个洞。
(x - 1 / sqrt(x))
- 用适当的扭曲创建一个洞。
- 确保孔内的所有像素都映射到中心的像素,然后按半径放大扭曲的距离函数。
- 缩放矢量以创建
distortion
,然后将中心添加回来。
- 缩放矢量以创建
- 从源纹理返回扭曲的样本。
后记
本篇已结束,后面更精彩~~~