CIKernel 介绍

本文深入介绍了 Core Image Kernel Language (CIKL) 和其在苹果平台图像处理中的应用。重点讨论了 CIColorKernel、CIWarpKernel 和 CIKernel 三种类型的内核,包括它们的使用场景、限制以及如何编写相应的内核函数。通过实例演示了如何实现 Vignette 和马赛克效果,并探讨了 CIKernel 开发中的注意事项和性能优化策略。此外,文章还提到了 Core Image 开发工具Quartz Composer在创建动态视觉效果和原型设计中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在此之前,我们先了解下它的一些背景知识。

CIKernel 需要使用 Core Image Kernel Language (CIKL) 来编写,CIKL 是 OpenGL Shading Language (GLSL) 的子集,如果你之前有过 OpenGL 着色器编写的经验,这部分你会感觉格外亲切。CIKL 集成了 GLSL 绝大部分的参数类型和内置函数,另外它还添加了一些适应 Core Image 的参数类似和函数。

一个 kernel 的处理过程,可以用下面伪代码表示:

for i in 1 ... image.width
    for j in 1 ... image.height
        New_Image[i][j] = CustomKernel(Current_Image[i][j])
    end
end

也就是说,每个需要处理的 fragment 都会调用一次 kernel 相关操作,每次操作的目的就是返回当前 fragment 对应的结果 fragment,这里 fragment 可以理解为像素点。

所以我们的 kernel,应该是针对一个点,而不是一张图片。

Core Image 内置了3种适用于不同场景的 Kernel,可以根据实际需求来选择。

CIColorKernel:用于处理色值变化的 Filter。
CIWarpKernel:用于处理形变的 Filter。
CIKernel:通用。
CIColorKernel,CIWarpKernel 是官方推荐使用的。某个 Filter,在使用它们能实现的情况下,应该使用它们,即使是一个 CIKernel 拆分成多个 CIColorKernel 以及 CIWarpKernel,也应该用这种方式。因为 Core Image 内部对这两张 Kernel 做了优化。

当然,它们的使用时有限制的。目的一定要很纯粹,比如 CIColorKernel 只能处理色值上的变化。否则就算定义为 CIColorKernel,如果实现上涉及了其他 CIColorKernel 不允许的操作,Core Image 也会当做普通的 CIFilter 处理。
另外,kernel 的入参只支持下面这么几种:

描述 类型
Kernel routine input parameter Object
sampler CISampler
__table sampler CISampler
__color CIColor
float NSNumber
vec2, vec3, or vec4 CIVector

简单说明一下:

sampler:可以理解成纹理,或者图片。外部以 CIImage 形式传入。
__table sampler:表示颜色查找表(lookup table),虽然它也是图片,但是添加该声明可以避免被修改。外部以 CIImage 形式传入。
__color:表示颜色。外部以 CIColor 形式传入。
float:kernel 内部处理都是 float 类型。外部以 NSNumber 形式传入。
vecN:表示一个多元向量。比如 vec2 可以表示一个点,vec4 可以表示一个色值。外部以 CIVector 形式传入

至于 kernel 中可以使用的函数,那就太多了。这里不一一枚举,在下面的具体讲解中,会说明几个常用的。如果想了解更多,可以参考 Core Image Kernel Language Reference,以及 OpenGL ES Shading Language Reference

下面我会通过一个 Demo,讲解这三种 Kernel 的具体用法。

PS:建议阅读之前,下载 源码 配合着看。

  1. CIColorKernel

首先看下官方的定义:

/*
 * CIColorKernel is an object that encapsulates a Core Image Kernel Language
 * routine that processes only the color information in images.
 *
 * Color kernels functions are declared akin to this example:
 *   kernel vec4 myColorKernel (__sample fore, __sample back, vec4 params)
 *
 * The function must take a __sample argument for each input image.
 * Additional arguments can be of type float, vec2, vec3, vec4, or __color.
 * The destination pixel location is obtained by calling destCoord().
 * The kernel should not call sample(), sampleCoord(), or samplerTransform().
 * The function must return a vec4 pixel color.
 */
NS_CLASS_AVAILABLE(10_11, 8_0)
@interface CIColorKernel : CIKernel

很重要的一点:processes only the color information in images,它只处理图片的颜色信息。

所以在使用它之前,一定要确保该 Filter 只涉及颜色处理。

CIKL 的语法和大多数 C 阵营一样,变量,运算符,控制结构,函数等都大同小异,所以它的学习成本是很低的。

真正的核心应该是:如果用这样的语言来实现这个滤镜,也就是我们经常说的算法。

下面我们以一个 Vignette 来实际讲解一下。

它的效果如下所示:

不难看出,Vignette 滤镜,它实际上就是一个FOV(Field of View) 的效果,即视野中央看的最清楚,清晰程度与到中心距离呈反比,与人类的视觉是类似的。

所以针对图片上的每个像素点 A,经过 Vignette 滤镜处理后得到的 B,应该满足:

Vignette(A)= A * Darken = B; 而 Darken 的计算依赖 A 与中心点的距离。

如此,我们可以很容易的写出对应的 kernel:

kernel vec4 vignetteKernel(__sample image, vec2 center, float radius, float alpha)
{
    // 计算出当前点与中心的距离
    float distance = distance(destCoord(), center) ;
    // 根据距离计算出暗淡程度
    float darken = 1.0 - (distance / radius * alpha);
    // 返回该像素点最终的色值
    image.rgb *= darken;

    return image.rgba;
}

和 C 语言的一样,函数需要具备:

返回类型:vec4
函数名:vignetteKernel
参数列表:__sample image, vec2 center, float radius, float alpha)
函数体:{}中的具体实现
有所不同的,kernel 函数需要带上 kernel 关键字,与其它普通函数做区分。一个 .cikernel 文件中,允许包括多个函数,甚至是多个 kernel 函数,不过函数调用要出现在函数定义之后!

另外,这里有个特别的参数类型,__sample ,和之前讲的 sampler 有所不同。因为这里我们使用的是 CIColorKernel,在得到高效性能的同时,也有一定的局限性。因为只是处理图片当前位置的颜色信息,所以 __sample 提供的 rgba 变量足够了,无法获取一些其它的信息。

比如在 CIKernel 中,可以通过 sample() 等函数获取其它位置的色值,而在 CIColorKernel 中,无法使用 sample(), sampleCoord() 以及 samplerTransform() 。

下面逐行解释这个 kernel。

// 计算出当前点与中心的距离
float distance = distance(destCoord(), center) ;

destCoord

  • varying vec2 destCoord ()
    返回当前正在处理的像素点所处坐标。(working space coordinates)
    这里使用的 CIKL 内置的函数 destCoord,它返回的坐标是基于 working space 的。所谓 working space,即工作空间,它的取值范围对应图片实际大小。比如 inputImage 的大小为 300 * 200,那么 destCoord() 返回坐标的取值范围在 (0, 0) - (300, 200)。

distance

  • float distance (vec2 p0, vec2 p1)
    计算向量p0,p1之间的距离

如此便能很容易得到当前点与中心的距离。

// 根据距离计算出暗淡程度
float darken = 1.0 - (distance / radius * alpha);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值