您将看到多·达·纸莱昂纳奇纳的四部作品。
在本教程中,您将有这些图像创建过滤器,然后在输出中查看应用过滤器的结果。
其实就是以关闭工作表来实现,点击关闭的过滤器列表。
介绍核心图像类
在填充了解过滤器列表之前,您需要 Core Image 框架的基本类。
-
CIImage:表示很好或由过滤器生成的对象中包含所有的图像
CIImage
,但实际上是这个像包含图像但包含制作图像但不是所有内容的准备。您将在本教程后面看到如何渲染图像以显示。
-
CIFilter:获取一个或多个图像,通过应用转换处理每个图像并生成一个
CIImage
作为输出。并创建有趣的效果CIFilters
。 -
CIContext:过滤器的处理结果。帮助从对象创建例如
CIContext
Quartz 2D 图像。CIImage
要了解有关这些类的更多信息,请参阅核心图像教程:入门。
你现在已经有了 Core Image 类,是时候填充过滤器列表了。
获取压缩器列表
打开RayVinci并选择FilterListView.swift。替换为:filterList``FilterListView
让 filterList = CIFilter .filterNames(inCategory: nil )
filterNames(inCategory:)
在这里,您可以通过使用并nil
作为类别传递来获取核心图像,提供所有可用的组合过滤器的列表。可以在开发人员文档中查看可用类别的CIFilter
列表。
打开FilterDetailView.swift。替换Text("Filter Details")
为body
:
// 1
if let ciFilter = CIFilter (name: filter) {
// 2
滚动视图 {
文本(ciFilter.attributes.description)
}
} 另外 { // 3 Text ( "未知过滤器!" )
}
在这里,你:
-
ciFilter
使用过滤器名称初始化器由于名称是一个过滤器字符串可能拼写错误,因此,初始化程序返回一个可选项。因此,需要检查过滤器是否存在。 - 您可以使用。检查过滤器的各种属性
attributes
。在这里,如果过滤器存在,您将在视图中创建并填充ScrollView
属性的描述。Text
- 如果过滤器不存在或未知,则显示
Text
情况的视图。
哇,点击过滤器啊!
点击过滤器以查看其属性。
很了不起,不是吗?你刚开始!在下一节将使用其中的一位美丽中让娜丽莎: “照在”蒙娜莎上。]
使用组合过滤器
现在您已经看到了可用过滤器的列表,您将使用其中一个来创建有趣的效果。
打开ImageProcessor.swift。在顶部,在类声明之前,添加:
枚举ProcessEffect {
case builtIn
case colorKernel
case warpKernel
case blendKernel
}
在这里,您声明ProcessEffect
为enum
. 它包含您将在本教程中使用的所有过滤器案例。
将以下内容添加到ImageProcessor
:
// 1
private func applyBuiltInEffect ( input : CIImage ) {
// 2
let noir = CIFilter (
name: "CIPhotoEffectNoir" ,
parameters: [ "inputImage" : input]
) ? .outputImage
// 3
let sunGenerate = CIFilter (
name: "CISunbeamsGenerator" ,
parameters: [ "inputStriationStrength" : 1 ,
"inputSunRadius" : 300 ,
“inputCenter”:CIVector(
x:input.extent.width - input.extent.width / 5,
y:input.extent.height - input.extent.height / 10)
])?
.outputImage // 4 let compositeImage = input.applyingFilter(
"CIBlendWithMask" ,
parameters: [
kCIInputBackgroundImageKey: noir as Any ,
kCIInputMaskImageKey: sunGenerate as Any
])
}
在这里,你:
- 声明一个将 a
CIImage
作为输入并应用内置过滤器的私有方法。 - 您首先使用. 以字典的形式将字符串作为名称和参数。您从.
CIPhotoEffectNoir``CIFilter``outputImage
- 接下来,您使用
CISunbeamsGenerator
. 这将创建一个阳光遮罩。在参数中,您设置:- inputStriationStrength:表示阳光的强度。
- inputSunRadius:表示太阳的半径。
- inputCenter:光束中心的 x 和 y 位置。在这种情况下,您将位置设置为图像的右上角。
- 在这里,您使用
CIBlendWithMask
.input
您可以通过将结果设置CIPhotoEffectNoir
为背景图像和sunGenerate
蒙版图像来应用过滤器。这个组合的结果是一个CIImage
.
ImageProcessor
has output
,一个已发布的属性,它是一个UIImage
. 您需要将合成的结果转换为 aUIImage
以显示它。
在ImageProcessor
中,添加以下内容@Published var output = UIImage()
:
让上下文= CIContext ()
在这里,您创建一个CIContext
所有过滤器都将使用的实例。
将以下内容添加到ImageProcessor
:
私有 函数 renderAsUIImage ( _image : CIImage ) -> UIImage?{
if let cgImage = context.createCGImage(image, from: image.extent) {
return UIImage (cgImage: cgImage)
} return nil
}
在这里,您用于context
创建CGImage
from的实例CIImage
。
使用cgImage
,然后创建一个UIImage
. 用户将看到此图像。
显示内置滤波器的输出
将以下内容添加到末尾applyBuiltInEffect(input:)
:
如果 让outputImage = renderAsUIImage(compositeImage) {
output = outputImage
}
这会将compositeImage
a转换CIImage
为UIImage
using renderAsUIImage(_:)
。然后将结果保存到output
.
将以下新方法添加到ImageProcessor
:
// 1
func process ( painting : Painting , effect : ProcessEffect ) {
// 2
guard
let paintImage = UIImage (named: painting.image),
let input = CIImage (image: paintImage)
else {
print ( "Invalid input image" )
return
} switch effect {
// 3 case .builtIn:
applyBuiltInEffect(input: input) default :
print (
“不支持的效果” )
}
}
在这里,你:
- 创建一个方法作为
ImageProcessor
. 它需要一个实例Painting
和一个effect
来应用。 - 检查有效的图像。
- 如果效果是 type
.builtIn
,则调用applyBuiltInEffect(input:)
以应用过滤器。
打开PaintingWall.swift。在下面selectedPainting = paintings[index]
的action
闭包中Button
,添加:
var effect = ProcessEffect .builtIn
if let painting = selectedPainting {
switch index {
case 0 :
effect = .builtIn
default :
effect = .builtIn
} ImageProcessor .shared.process(painting: painting, effect: effect)
}
在这里,您为第一幅画设置了effect
to 。.builtIn
您还将其设置为默认效果。然后通过调用来应用过滤process(painting:, effect:)
器ImageProcessor
。
构建并运行。点击“蒙娜丽莎”。您将在输出中看到一个内置过滤器!
伟大的工作让阳光照在蒙娜丽莎身上。难怪她会笑!现在是时候使用CIKernel创建过滤器了。
认识 CIKernel
使用CIKernel,您可以使用称为内核的自定义代码来逐个像素地操作图像。GPU 处理这些像素。您使用Metal Shading Language编写内核,与自 iOS 12 起已弃用的旧版Core Image Kernel Language相比,它具有以下优势:
- 支持 Core Image 内核的所有强大功能,如连接和平铺。
- 在构建时预编译并带有错误诊断。这样,您无需等待运行时出现错误。
- 提供语法高亮和语法检查。
有不同类型的内核:
- CIColorKernel:更改像素的颜色,但不知道像素的位置。
- CIWarpKernel:改变像素的位置,但不知道像素的颜色。
- CIBlendKernel:以优化的方式混合两个图像。
要创建和应用内核,您:
- 首先,将自定义构建规则添加到项目中。
- 然后,添加 Metal 源文件。
- 加载内核。
- 最后,初始化并应用内核。
接下来,您将实现这些步骤中的每一个。准备好有趣的旅程!
创建构建规则
您需要编译 Core Image Metal 代码并将其与特殊标志链接。
在项目导航器中选择RayVinci目标。然后,选择构建规则选项卡。通过单击+添加新的构建规则。
然后,设置第一个新的构建规则:
- 将进程设置为名称匹配的源文件: . 然后将*.ci.metal设置为值。
- 取消选中Run once per architecture。
- 添加以下脚本:
xcrun metal -c -fcikernel "${INPUT_FILE_PATH}" \
-o "${SCRIPT_OUTPUT_FILE_0}"
这会使用所需的-fcikernel标志调用 Metal 编译器。
- 在输出文件中添加以下内容:
$(DERIVED_FILE_DIR)/${INPUT_FILE_BASE}.air
这会生成一个以*.ci.air*结尾的输出二进制文件。
接下来,通过再次单击+添加另一个新的构建规则。
按照以下步骤获取第二个新构建规则:
- 将进程设置为名称匹配的源文件: . 然后将*.ci.air设置为值。
- 取消选中Run once per architecture。
- 添加以下脚本:
xcrun metallib -cikernel "${INPUT_FILE_PATH}" -o "${SCRIPT_OUTPUT_FILE_0}"
这会使用所需的-cikernel标志调用 Metal 链接器。
- 在输出文件中添加以下内容:
$(METAL_LIBRARY_OUTPUT_DIR)/$(INPUT_FILE_BASE).metallib
这会在应用程序包中生成一个以.ci.metallib结尾的文件。
接下来,是时候添加金属源了。
添加金属源
首先,您将为颜色内核创建一个源文件。在项目导航器中,突出显示RayVinci项目正下方的 RayVinci。
右键单击并选择新建组。将此新组命名为 Filters。然后,突出显示该组并添加一个名为ColorFilterKernel.ci.metal的新金属文件。
打开文件并添加:
// 1
#包含 <CoreImage/CoreImage.h>
// 2
extern "C" {
namespace coreimage { // 3
float4 colorFilterKernel(sample_ts) { // 4
float4 swappedColor;
交换颜色.r = sg;
swappedColor.g = 某人;
交换颜色.b = sr;
交换颜色.a = sa; 返回交换颜色;
}
}
}
下面是代码分解:
- 包含 Core Image 标头可以让您访问框架提供的类。这会自动包含核心映像金属内核库CIKernelMetalLib.h。
- 内核需要在一个
extern "C"
外壳内,以便在运行时通过名称访问它。接下来,您指定coreimage
. 您在命名空间中声明所有扩展coreimage
以避免与 Metal 冲突。 - 在这里,您声明
colorFilterKernel
,它接受类型的输入sample_t
。sample_t
表示来自输入图像的单个颜色样本。colorFilterKernel
返回float4
表示像素的 RGBA 值的 a。 - 然后,您声明一个新的
float4
,swappedColor
,并交换输入样本中的 RGBA 值。然后,您返回带有交换值的样本。
接下来,您将编写代码以加载和应用内核。
加载内核代码
要加载和应用内核,首先要创建一个CIFilter
.
在 Filters 组中创建一个新的 Swift 文件。将其命名为ColorFilter.swift并添加:
// 1
导入CoreImage类ColorFilter : CIFilter {
// 2 var inputImage: CIImage ? // 3 static var kernel: CIKernel = { () -> CIColorKernel in guard let url = Bundle .main.url(
forResource: "ColorFilterKernel.ci" ,
withExtension: "metallib" ),
let data = try? 数据(contentsOf:url)否则{
fatalError ( "Unable to load metallib" )
} guard let kernel = try? CIColorKernel (
functionName: "colorFilterKernel" ,
fromMetalLibraryData: data) else {
fatalError ( "Unable to create color kernel" )
} return kernel
}() // 4 override var outputImage: CIImage ? {
守卫让inputImage = inputImage else {返回
nil }
return ColorFilter .kernel.apply(
extent: inputImage.extent,
roiCallback: { _ , rect in
return rect
},
arguments: [inputImage])
}
}
在这里,你:
-
首先导入 Core Image 框架。
-
子类化
CIFilter
涉及两个主要步骤:- 指定输入参数。在这里,您使用
inputImage
. - 压倒一切
outputImage
。
- 指定输入参数。在这里,您使用
-
然后,您声明一个静态属性 ,
kernel
它加载ColorFilterKernel.ci.metallib的内容。这样,库只加载一次。然后,您使用ColorFilterKernel.ci.metallibCIColorKernel
的内容创建一个实例。 -
接下来,你
override outputImage
。在这里,您使用apply(extent:roiCallback:arguments:)
. 确定有extent
多少输入图像传递给内核。您传递整个图像,因此过滤器将应用于整个图像。
roiCallback
确定渲染inrect
所需的输入图像的。在这里,of和并没有改变,因此您返回相同的值并将参数数组中的 传递给内核。rect``outputImage``rect``inputImage``outputImage``inputImage
现在您已经创建了颜色内核过滤器,您将把它应用到图像上。
应用颜色内核过滤器
打开ImageProcessor.swift。将以下方法添加到ImageProcessor
:
private func applyColorKernel ( input : CIImage ) {
let filter = ColorFilter ()
filter.inputImage = input
if let outputImage = filter.outputImage,
let renderImage = renderAsUIImage(outputImage) {
output = renderImage
}
}
在这里,您声明applyColorKernel(input:)
. 这需要 aCIImage
作为输入。您可以通过创建ColorFilter
.
过滤器outputImage
应用了颜色内核。然后,您创建一个UIImage
using实例renderAsUIImage(_:)
并将其设置为输出。
接下来,.colorKernel
如下process(painting:effect:)
图处理。在上面添加这个新案例default
:
案例.colorKernel:
applyColorKernel(输入:输入)
在这里,您调用applyColorKernel(input:)
以应用您的自定义颜色内核过滤器。
最后,打开PaintingWall.swift。在's 闭包的switch
正下方的语句中添加以下内容:case 0``Button``action
案例 1:
效果= .colorKernel
这将效果设置.colorKernel
为第二幅画。
构建并运行。现在点击第二幅画“最后的晚餐”。您将看到应用了颜色内核过滤器并在图像中交换了 RGBA 值。
做得好!接下来,您将为达芬奇神秘的“ Salvator Mundi ”创建酷炫的经线效果。
创建 Warp 内核
与颜色内核类似,您将从添加 Metal 源文件开始。在 Filters 组中创建一个名为WarpFilterKernel.ci.metal的新 Metal 文件。打开文件并添加:
# include <CoreImage/CoreImage.h>
//1
extern "C" {
namespace coreimage { //2
float2 warpFilter(destination dest) { float y = dest.coord().y + tan(dest.coord().y / 10 ) * 20 ;
浮动x = dest.coord().x + tan(dest.coord().x/ 10 ) * 20 ;
返回浮动2(x,y);
}
}
}
这是您添加的内容:
-
就像在颜色内核 Metal 源中一样,您包含 Core Image 标头并将方法封装在一个
extern "C"
附件中。然后指定coreimage
命名空间。 -
接下来,您
warpFilter(_:)
使用 type 的输入参数声明destination
,允许访问您当前正在计算的像素的位置。它返回输入图像坐标中的位置,然后您可以将其用作源。您可以使用 访问目标像素的 x 和 y 坐标
coord()
。然后,您应用简单的数学来转换坐标并将它们作为源像素坐标返回,以创建有趣的平铺效果。注意:尝试用 in 替换
tan
,sin
你warpFilter(_:)
会得到一个有趣的失真效果!
加载 Warp 内核
与您为颜色内核创建的过滤器类似,您将创建一个自定义过滤器来加载和初始化扭曲内核。
在 Filters 组中创建一个新的 Swift 文件。将其命名为WarpFilter.swift并添加:
import CoreImage // 1类WarpFilter : CIFilter {
var inputImage: CIImage ?
// 2 static var kernel: CIWarpKernel = { () -> CIWarpKernel in guard let url = Bundle .main.url(
forResource: "WarpFilterKernel.ci" ,
withExtension: "metallib" ),
let data = try? 数据(contentsOf:url)else {
fatalError
( "无法加载 metallib" ) }
让内核=试试看?CIWarpKernel (
functionName: "warpFilter" ,
fromMetalLibraryData: data) else {
fatalError ( "Unable to create warp kernel" )
} return kernel
}() // 3 override var outputImage: CIImage ? {
守卫让inputImage = inputImage else { return .none }返回
WarpFilter .kernel.apply(
范围:inputImage.extent,
roiCallback:{ _,矩形返回矩形
},图像
:inputImage,
参数:[])
}
}
在这里,你:
- 创建
WarpFilter
为CIFilter
withinputImage
作为输入参数的子类。 - 接下来,您声明静态属性
kernel
以加载WarpFilterKernel.ci.metallib的内容。然后,您创建一个CIWarpKernel
使用.metallib
. - 最后,您通过覆盖提供输出
outputImage
。在override
中,您将内核应用于inputImage
使用apply(extent:roiCallback:arguments:)
并返回结果。
应用变形内核过滤器
打开ImageProcessor.swift。将以下内容添加到ImageProcessor
:
private func applyWarpKernel ( input : CIImage ) {
let filter = WarpFilter ()
filter.inputImage = input
if let outputImage = filter.outputImage,
let renderImage = renderAsUIImage(outputImage) {
output = renderImage
}
}
在这里,您声明applyColorKernel(input:)
,它将CIImage
作为输入。然后,您创建一个实例WarpFilter
并设置inputImage
.
过滤器outputImage
应用了扭曲内核。然后,您创建一个UIImage
using实例renderAsUIImage(_:)
并将其保存到输出。
接下来,将以下案例添加到process(painting:effect:)
以下case .colorKernel
:
案例.warpKernel:
applyWarpKernel(输入:输入)
在这里,您处理案例.warpKernel
并调用applyWarpKernel(input:)
以应用 warp 内核过滤器。
最后,打开PaintingWall.swift。switch
在下面的语句case 1
中添加以下案例action
:
案例 2:
效果= .warpKernel
这将效果设置.warpKernel
为第三幅画。
构建并运行。点击 Salvator Mundi 的画作。您会看到应用了一个有趣的基于扭曲的平铺效果。
恭喜!您将自己的风格应用于杰作!
挑战:实现混合内核
已CIBlendKernel
针对混合两个图像进行了优化。作为一个有趣的挑战,为CIBlendKernel
. 一些提示:
- 创建一个包含
CIFilter
两个图像的子类:一个输入图像和一个背景图像。 - 使用内置的可用
CIBlendKernel
内核。对于这个挑战,使用内置的乘法混合内核。 - 创建一个方法
ImageProcessor
,将混合内核过滤器应用于图像并将结果设置为输出。您可以使用项目资产中提供的多色图像作为滤镜的背景图像。此外,处理.blendKernel
. - 将此过滤器应用于PaintingWall.swift中的第四张图像。
您会在下载的材料中找到在最终项目中实施的解决方案。祝你好运!
调试核心映像问题
了解 Core Image 如何渲染图像可以帮助您在图像未按您预期的方式显示时进行调试。最简单的方法是在调试时使用Core Image Quick Look 。
使用核心图像快速查看
打开ImageProcessor.swift。output
在您设置的行上放置一个断点applyColorKernel(input:)
。构建并运行。点击“最后的晚餐”。
当你到达断点时,将鼠标悬停在outputImage
. 您会看到一个显示地址的小弹出框。
单击眼睛符号。将出现一个窗口,显示生成图像的图形。很酷吧?
使用 CI_PRINT_TREE
CI_PRINT_TREE是基于与 Core Image Quick Look 相同的基础架构的调试功能。它有几种模式和操作。
选择并编辑RayVinci方案。选择Run选项卡并将CI_PRINT_TREE添加为值为7 pdf的新环境变量。
CI_PRINT_TREE的值采用graph_type output_type options
.
graph_type
表示核心图像渲染的阶段。以下是您可以指定的值:
- 1:显示颜色空间的初始图。
- 图2:一个优化的图表,显示了 Core Image 是如何优化的。
- 4:显示您需要多少内存的串联图。
- 7:详细日志记录。这将打印所有上述图表。
对于output_type
,您可以指定PDF或PNG。它将文档保存到临时目录。
构建并运行。在模拟器中选择“最后的晚餐” 。现在,通过使用终端导航到/tmp打开 Mac 上的临时目录。
您会以 PDF 文件的形式看到所有图表。打开以_initial_graph.pdf作为后缀的文件之一。
输入在底部,输出在顶部。红色节点代表颜色内核,而绿色节点代表扭曲内核。您还将看到每个步骤的投资回报率和范围。
结论
在本教程中,您学习了使用基于 Metal 的 Core Image 内核创建和应用自定义过滤器。
本教程的最终项目资料下载地址
链接:https://pan.baidu.com/s/1bf3_325mYd-B7Cd7qPw3DQ
提取码:51ok
这里也推荐一些面试相关的内容,祝各位网友都能拿到满意offer!
GCD面试要点
block面试要点
Runtime面试要点
RunLoop面试要点
内存管理面试要点
MVC、MVVM面试要点
网络性能优化面试要点
网络编程面试要点
KVC&KVO面试要点
数据存储面试要点
混编技术面试要点
设计模式面试要点
UI面试要点
网友评论