Core Image框架详细解析(九) —— 子类化CIFilter:自定义效果的配方 Subclassing CIFilter: Recipes for Custom Effects(一)

版本记录

版本号 时间
V1.0 2018.01.28

前言

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

子类化CIFilter:自定义效果的配方

您可以使用一个图像滤镜的输出作为另一个图像滤镜的输入来创建自定义效果,并根据需要链接尽可能多的滤镜。 当您通过多次使用这种方式创建效果时,请考虑继承CIFilter并封装过滤器的效果。

本章介绍Core Image如何继承自CIFilter类并创建为CIColorInvert滤镜。 然后它描述了链接在一起的各种过滤器的recipes,以实现有趣的效果。 按照Subclassing CIFilter to Create the CIColorInvert Filter的子类化过程,您应该能够从本章中的配方创建过滤器,或者创建自己感兴趣的Core Image提供的内置过滤器组合。


Subclassing CIFilter to Create the CIColorInvert Filter - 子类CIFilter创建CIColorInvert过滤器

当您对CIFilter进行子类化时,您可以通过使用预设值对其进行编码或将它们链接在一起来修改现有的过滤器。 Core Image使用这种技术实现了一些内置的过滤器。

要创建一个过滤器的子类,您需要执行以下任务:

  • 声明过滤器输入参数的属性。 您必须在每个输入参数名称前添加input,如inputImage。

  • 如有必要,重写setDefaults方法。 (这个例子中没有必要,因为输入参数是设定值。)

  • 重写outputImage方法。

Core Image提供的CIColorInvert滤镜是CIColorMatrix滤镜的变体。 顾名思义,CIColorInvert将矢量提供给CIColorMatrix,以反转输入图像的颜色。 按照Listing 5-1和Listing 5-2所示的简单示例构建自己的过滤器。

// Listing 5-1  The interface for the CIColorInvert filter

@interface CIColorInvert: CIFilter {
    CIImage *inputImage;
}
@property (retain, nonatomic) CIImage *inputImage;
@end
// Listing 5-2  The outputImage method for the CIColorInvert filter

@implementation CIColorInvert
@synthesize inputImage;
- (CIImage *) outputImage
{
      CIFilter *filter = [CIFilter filterWithName:@"CIColorMatrix"
                            withInputParameters: @{
            kCIInputImageKey: inputImage,
        @"inputRVector": [CIVector vectorWithX:-1 Y:0 Z:0],
        @"inputGVector": [CIVector vectorWithX:0 Y:-1 Z:0],
        @"inputBVector": [CIVector vectorWithX:0 Y:0 Z:-1],
        @"inputBiasVector": [CIVector vectorWithX:1 Y:1 Z:1],
      return filter.outputImage;
}

Chroma Key Filter Recipe - 色度键过滤配方

从源图像中移除颜色或颜色范围,然后将源图像与背景图像进行合成。

Figure 5-1 The Chroma Key filter processing chain

创建色度键过滤器:

  • 创建映射您想要移除的颜色值的数据的立方体贴图,使其透明(alpha值为0.0)。
  • 使用CIColorCube过滤器和立方体贴图从源图像中删除色度键颜色。
  • 使用CISourceOverCompositing过滤器将处理后的源图像混合到背景图像上

以下部分显示如何执行每个步骤。

1. Create a Cube Map - 创建一个多维数据图

彩色立方体是3D颜色查找表。Core Image过滤器CIColorCube将颜色值作为输入,并将查找表应用于这些值。 CIColorCube的默认查找表是一个单位矩阵,意思是它对提供的数据没有任何作用。但是,这个recipe要求你从图像中删除所有的绿色。 (如果您愿意,可以移除不同的颜色。)

你需要通过设置绿色到alpha = 0.0来清除图像中的所有绿色,这使得透明。 “Green”包含一系列的颜色。最直接的方法是将图像中的颜色值从RGBA转换为HSV值。在HSV中,hue色调表示为围绕圆柱的中心轴的角度。在该表示中,可以将颜色可视化为饼图切片,然后简单地删除表示色度键颜色的切片。

要去除绿色,您需要定义包含绿色色调的中央通道周围的最小和最大角度。那么,对于任何绿色的东西,您将其alpha值设置为0.0。纯绿色的数值相当于120º。最小和最大角度需要以该值为中心。

立方体贴图数据必须预乘alpha,所以创建立方体贴图的最后一步是将RGB值乘以刚刚计算的alpha值,对于绿色的色调为0.0,否则为1.0。Listing 5-3显示了如何创建此过滤器recipe所需的颜色立方体。

// Listing 5-3  The color cube in code

// Allocate memory
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float rgb[3], hsv[3], *c = cubeData;

// Populate cube with a simple gradient going from 0 to 1
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
            // Convert RGB to HSV
            // You can find publicly available rgbToHSV functions on the Internet
            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.0f: 1.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
        }
    }
}

// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
                       length:cubeDataSize
                       freeWhenDone:YES];
CIColorCube *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:@(size) forKey:@"inputCubeDimension"];

// Set data for cube
[colorCube setValue:data forKey:@"inputCubeData"];

2. Remove green from the source image -

从源图像中删除绿色

现在,您已经有了彩色map数据,将前景图像(即,要从中去除绿色的图像)提供给CIColorCube滤镜并获取输出图像

[colorCube setValue:myInputImage forKey:kCIInputImageKey];
CIImage *result = [colorCube valueForKey:kCIOutputImageKey];

3. Blend the processed source image over a background image - 将处理的源图像混合到背景图像上

设置CISourceOverCompositing过滤器的输入参数如下:

  • inputImage设置为从CIColorCube过滤器生成的图像。
  • inputBackgroundImage设置为显示新背景的图像。 这个例子使用海滩图像。

现在,前景图像就好像在沙滩上一样。


White Vignette for Faces Filter Recipe - 面孔过滤Recipe的白色小插图

增加图像中检测到的脸部周围图像的亮度。

Figure 5-2 The White Vignette filter processing chain

要创建一个白色的小插图过滤器:

  • 在图像中找到人脸。
    使用以脸部为中心的CIRadialGradient创建基本阴影贴图。
  • 将基本阴影贴图与原始图像混合。

以下部分显示如何执行每个步骤。

1. Find the Face - 找到脸部

使用CIDetector类在图像中定位面部。 featuresInImage:options:返回数组中的第一个元素,就是滤波器要作用的面部。 在识别脸部后,从探测器提供的边界计算脸部的中心。 您需要中心值来创建阴影图。 Listing 5-4显示了如何使用CIDetector定位面部。

// Listing 5-4  Using CIDetector to locate one face

CIDetector *detector = [CIDector detectorOfType:CIDetectorTypeFace
                                        context:nil
                                        options:nil];
NSArray *faceArray = [detector featuresInImage:image options:nil];
CIFeature *face = faceArray[0];
CGFloat xCenter = face.bounds.origin.x + face.bounds.size.width/2.0;
CGFloat yCenter = face.bounds.origin.y + face.bounds.size.height/2.0;
CIVector *center = [CIVector vectorWithX:xCenter Y:yCenter];

2. Create a Shade Map - 创建一个阴影图

使用CIRadialGradient过滤器创建一个居中于人脸的阴影贴图。 阴影贴图的中心应该是透明的,以便图像中的面部保持不变。 图的边缘应该是不透明的白色。 两者之间的区域应该有不同程度的透明度。

要达到此效果,请将输入参数设置为CIRadialGradient,如下所示:

  • inputRadius0设置为大于图像最长尺寸的值。
  • inputRadius1设置为比face更大的值,例如face.bounds.size.height + 50
  • inputColor0设置为不透明的白色。
  • inputColor1设置为透明白色。
  • inputCenter设置为您使用Listing 5-4计算的面部边界的中心。

3. Blend the Gradient with the Face - 与脸部融合的渐变

设置CISourceOverCompositing过滤器的输入参数如下:

  • inputImage设置为原始图像。
  • inputBackgroundImage设置为上一步生成的阴影贴图。

Tilt-Shift Filter Recipe - 倾斜移位滤波器Recipe

选择性地聚焦图像以模拟微型场景。

Figure 5-3 The Tilt-Shift filter processing chain

要创建一个倾斜移位滤镜:

  • 创建图像的模糊版本。
  • 创建两个线性渐变。
  • 通过合成线性渐变创建一个蒙版。
  • 合成模糊的图像,蒙版和原始图像。

以下部分显示如何执行每个步骤。

1. Create a Blurred Version of the image - 创建图像的模糊版本

设置CIGaussianBlur过滤器的输入参数如下:

  • inputImage设置为要处理的图像。
  • inputRadius设置为10.0(这是默认值)。

2. Create Two Linear Gradients - 创建两个线性渐变

使用从上到下变化的单一颜色(例如绿色或灰色)创建线性渐变。 设置CILinearGradient的输入参数如下:

  • inputPoint0设置为(0,0.75 * h)
  • inputColor0设置为(0,1,0,1)
  • inputPoint1设置为(0,0.5 * h)
  • inputColor1设置为(0,1,0,0)

创建一个从下到上变化的绿色线性渐变。 设置CILinearGradient的输入参数如下:

  • inputPoint0设置为(0,0.25 * h)
  • inputColor0设置为(0,1,0,1)
  • inputPoint1设置为(0,0.5 * h)
  • inputColor1设置为(0,1,0,0)

3. Create a Mask from the Linear Gradients - 从线性渐变创建一个蒙版

要创建一个遮罩,请按如下方式设置CIAdditionCompositing滤镜的输入参数:

  • inputImage设置为您创建的第一个线性渐变。
  • inputBackgroundImage设置为您创建的第二个线性渐变。

4. Combine the Blurred Image, Source Image, and the Gradients - 结合模糊图像,源图像和渐变

最后一步是使用CIBlendWithMask过滤器,设置输入参数如下:

  • inputImage设置为图像的模糊版本。
  • inputBackgroundImage设置为原始未处理的图像。
  • inputMaskImage设置为蒙版,即组合的渐变。

蒙版只会影响图像的外部。 蒙版的透明部分将通过原始未处理的图像显示。 蒙版的不透明部分允许显示模糊的图像。

后记

本篇已结束,后面更精彩~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容