版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.10.08 星期五 |
前言
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(一)
15. Core Image框架详细解析(十五) —— 创建自定义滤波器 Creating Custom Filters(二)
16. Core Image框架详细解析(十六) —— 包装和加载图像单元 Packaging and Loading Image Units
17. Core Image框架详细解析(十七) —— 一个简单说明和示例(一)
开始
首先看下主要内容:
学习使用
Metal Shading Language
创建您自己的Core Image
过滤器,以构建提供像素级图像处理的内核。内容来自翻译。
接着看下写作环境:
Swift 5, iOS 14, Xcode 12
下面就是原文了。
Core Image 是一个强大而高效的图像处理框架。 您可以使用框架提供的内置过滤器创建漂亮的效果,也可以创建自定义过滤器和图像处理器。 您可以调整颜色、几何形状并执行复杂的卷积。
制作漂亮的滤镜是一门艺术,最伟大的艺术家之一是列奥纳多·达·芬奇 (Leonardo da Vinci)
。 在本教程中,您将为达芬奇的名画添加一些有趣的元素。
在此过程中,您将:
- 了解
Core Image
的类和内置过滤器。 - 使用内置过滤器创建过滤器。
- 使用自定义颜色内核
(color kernel)
转换图像的颜色。 - 使用自定义扭曲内核转换图像的几何形状。
- 学习调试
Core Image
问题。
注意:由于 Apple问题,本教程不适用于
Xcode 13
和iOS 15
。您现在必须使用Xcode 12
。
准备好你的画笔,哎呀,我的意思是你的 Xcode 准备好了。 是时候潜入 Core Image
的奇妙世界了!
打开下载项目。 在 starter
中打开 RayVinci
项目。 构建并运行。
您将看到达芬奇最著名的四部作品。点击一幅画会打开一个sheet
,但图像的输出是空的。
在本教程中,您将为这些图像创建过滤器,然后查看在输出中应用过滤器的结果。
向下滑动以关闭sheet
。接下来,点击右上角的Filter List。
该按钮应显示可用内置过滤器的列表。但是等等,它目前是空的。你接下来会解决这个问题。
Introducing Core Image Classes
在填充过滤器列表之前,您需要了解 Core Image
框架的基本类。
-
CIImage:表示准备好进行处理或由
Core Image
过滤器生成的图像。CIImage
对象包含图像的所有数据,但实际上不是图像。这就像一个食谱,它包含了做一道菜的所有成分,但不是这道菜本身。
您将在本教程后面看到如何渲染要显示的图像。
CIFilter:获取一张或多张图像,通过应用转换来处理每张图像,并生成一个
CIImage
作为其输出。您可以链接多个过滤器并创建有趣的效果。CIFilters
的对象是可变的,不是线程安全的。CIContext:渲染过滤器的处理结果。例如,
CIContext
帮助从CIImage
对象创建Quartz 2D
图像。
要了解有关这些类的更多信息,请参阅Core Image Tutorial: Getting Started。
现在您已经熟悉了 Core Image
类,是时候填充过滤器列表了。
Fetching the List of Built-In Filters
打开 RayVinci
并选择 FilterListView.swift
。 将 FilterListView
中的 filterList
替换为:
let filterList = CIFilter.filterNames(inCategory: nil)
在这里,您通过使用 filterNames(inCategory:)
并传递 nil
作为类别来获取 Core Image
提供的所有可用内置过滤器的列表。 您可以在 CIFilter
的开发人员文档developer documentation中查看可用类别列表。
打开 FilterDetailView.swift
。 将正文中的Text("Filter Details")
替换为:
// 1
if let ciFilter = CIFilter(name: filter) {
// 2
ScrollView {
Text(ciFilter.attributes.description)
}
} else {
// 3
Text("Unknown filter!")
}
在这里,你:
- 1) 使用过滤器名称初始化过滤器
ciFilter
。 由于名称是一个字符串并且可能拼写错误,因此初始化程序返回一个可选的。 因此,您需要检查过滤器是否存在。 - 2) 您可以使用属性检查过滤器的各种属性。 在这里,如果过滤器存在,您将创建一个
ScrollView
并在文本视图中填充属性的描述。 - 3) 如果过滤器不存在或未知,您将显示一个
Text
视图来解释情况。
构建并运行。 点击Filter List
。 哇,过滤器太多了!
点按任何过滤器以查看其属性。
很了不起,不是吗?你才刚刚开始!在下一节中,您将使用这些内置过滤器来使对“蒙娜丽莎”的阳光下熠熠生辉。
Using Built-In Filters
现在,你已经看到可用过滤器列表中,您将使用这些来创建一个有趣的效果。
打开ImageProcessor.swift
。在顶部,在类声明之前,添加:
enum 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
])
}
在这里,你:
- 1) 声明一个将
CIImage
作为输入并应用内置过滤器的私有方法。 - 2) 您首先使用
CIPhotoEffectNoir
创建一个变暗、喜怒无常的黑色效果。CIFilter
以一个字符串作为名称和字典形式的参数。您从outputImage
获取生成的过滤图像。 - 3) 接下来,您使用
CISunbeamsGenerator
创建一个生成器过滤器。这将创建一个阳光遮罩。在参数中,您设置:-
inputStriationStrength
:表示阳光的强度。 -
inputSunRadius
:代表太阳的半径。 -
inputCenter
:阳光中心的 x 和 y 位置。在这种情况下,您将位置设置在图像的右上角。
-
- 4) 在这里,您使用
CIBlendWithMask
创建风格化效果。您可以通过将CIPhotoEffectNoir
的结果设置为背景图像并将sunGenerate
设置为蒙版图像来对输入input
应用过滤器。这种组合的结果是一个CIImage
。
ImageProcessor
有输出output
,一个发布的属性,它是一个 UIImage
。您需要将合成结果转换为 UIImage
以显示它。
在 ImageProcessor
中,在@Published var output = UIImage()
下面添加以下内容:
let context = CIContext()
在这里,您创建了一个所有过滤器都将使用的 CIContext
实例。
将以下内容添加到 ImageProcessor
:
private func renderAsUIImage(_ image: CIImage) -> UIImage? {
if let cgImage = context.createCGImage(image, from: image.extent) {
return UIImage(cgImage: cgImage)
}
return nil
}
在这里,您使用上下文从 CIImage
创建一个 CGImage
实例。
然后使用 cgImage
创建一个 UIImage
。 用户将看到此图像。
1. Displaying a Built-In Filter’s Output
将以下内容添加到 applyBuiltInEffect(input:)
的末尾:
if let outputImage = renderAsUIImage(compositeImage) {
output = outputImage
}
这将使用 renderAsUIImage(_:)
将compositeImage(CIImage)
转换为 UIImage
。 然后将结果保存到输出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("Unsupported effect")
}
}
在这里,你:
- 1) 创建一个方法作为
ImageProcessor
的入口点。 它需要一个Painting
和一个effect
实例来应用。 - 2) 检查有效图像。
- 3) 如果效果是
.builtIn
类型,则调用applyBuiltInEffect(input:)
来应用过滤器。
打开 PaintWall.swift
。 在 Button
的 action
闭包中 selectedPainting = Painting[index]
下方,添加:
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
设置为 .builtIn
。 您还将其设置为默认效果。 然后通过在 ImageProcessor
上调用 process(painting:, effect:)
来应用过滤器。
构建并运行。 点击“Mona Lisa”
。 您将看到在输出中应用了一个内置过滤器!
让蒙娜丽莎阳光普照的伟大工作。难怪她在笑!现在是使用 CIKernel
创建过滤器的时候了。
Meet CIKernel
使用 CIKernel
,您可以放置自定义代码,称为内核(kernel)
,以逐个像素地操作图像。 GPU 处理这些像素。您使用 Metal Shading Language
编写内核,与较旧的 Core Image Kernel Language
(自 iOS 12
起已弃用)相比,它具有以下优势:
- 支持
Core Image
内核的所有强大功能,如连接和平铺。 - 在构建时预编译,带有错误诊断。这样,您无需等待运行时出现错误。
- 提供语法高亮和语法检查。
有不同类型的内核:
- CIColorKernel:改变像素的颜色但不知道像素的位置。
- CIWarpKernel:改变像素的位置但不知道像素的颜色。
- CIBlendKernel:以优化的方式混合两个图像。
要创建和应用内核,您需要:
- 1) 首先,向项目添加自定义构建规则。
- 2) 然后,添加
Metal
源文件。 - 3) 加载内核。
- 4) 最后,初始化并应用内核。
接下来,您将实施这些步骤中的每一个。 准备好享受有趣的旅程吧!
Creating Build Rules
您需要编译 Core Image Metal
代码并将其与特殊标志链接。
在项目导航器中选择 RayVinci target
。 然后,选择Build Rules
选项卡。 单击 +
添加新的构建规则。
然后,设置第一个新的构建规则:
- 1) 将
Process
设置为Source files with name matching:
。 然后将*.ci.metal
设置为值。 - 2) 取消选中
Run once per architecture
。 - 3) 添加以下脚本:
xcrun metal -c -I $MTL_HEADER_SEARCH_PATHS -fcikernel "${INPUT_FILE_PATH}" \
-o "${SCRIPT_OUTPUT_FILE_0}"
这会使用所需的 -fcikernel
标志调用 Metal
编译器。
- 4) 在输出文件
Output Files
中添加以下内容
$(DERIVED_FILE_DIR)/${INPUT_FILE_BASE}.air
这会产生一个以 .ci.air
结尾的输出二进制文件。
接下来,再次单击 +
添加另一个新的构建规则。
对于第二个新构建规则,请按照以下步骤操作:
- 1) 将
Process
设置为Source files with name matching:
。 然后将*.ci.air
设置为值。 - 2) 取消选中
Run once per architecture
。 - 3) 添加以下脚本:
xcrun metallib -cikernel "${INPUT_FILE_PATH}" -o "${SCRIPT_OUTPUT_FILE_0}"
这将使用所需的 -cikernel
标志调用 Metal
链接器。
- 4) 在输出文件
Output Files
中添加以下内容:
$(METAL_LIBRARY_OUTPUT_DIR)/$(INPUT_FILE_BASE).metallib
这会在应用程序包中生成一个以 .ci.metallib
结尾的文件。
接下来,是时候添加 Metal
源了。
Adding the Metal Source
首先,您将为颜色内核创建一个源文件。 在项目导航器中,突出显示 RayVinci
项目正下方的 RayVinci
。
右键单击并选择New Group
。 将此新组命名为 Filters
。 然后,突出显示该组并添加一个名为 ColorFilterKernel.ci.metal
的新 Metal
文件。
打开文件并添加:
// 1
#include <CoreImage/CoreImage.h>
// 2
extern "C" {
namespace coreimage {
// 3
float4 colorFilterKernel(sample_t s) {
// 4
float4 swappedColor;
swappedColor.r = s.g;
swappedColor.g = s.b;
swappedColor.b = s.r;
swappedColor.a = s.a;
return swappedColor;
}
}
}
下面是代码分解:
- 1) 包含
Core Image
的header
可让您访问框架提供的类。 这会自动包含Core Image Metal
内核库CIKernelMetalLib.h
。 - 2) 内核需要位于
extern "C"
闭包内,以便在运行时可以通过名称访问它。 接下来,指定coreimage
的命名空间。 您在coreimage
命名空间中声明所有扩展以避免与Metal
发生冲突。 - 3) 在这里,您声明
colorFilterKernel
,它接受类型为sample_t
的输入。sample_t
表示来自输入图像的单个颜色样本。colorFilterKernel
返回一个表示像素的RGBA
值的float4
。 - 4) 然后,您声明一个新的
float4
、swappedColor
,并交换来自输入样本的RGBA
值。 然后返回具有交换值的样本。
接下来,您将编写代码来加载和应用内核。
Loading the Kernel Code
要加载和应用内核,首先要创建 CIFilter
的子类。
在过滤器Filters
组中创建一个新的 Swift
文件。 将其命名为 ColorFilter.swift
并添加:
// 1
import CoreImage
class 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? Data(contentsOf: url) else {
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? {
guard let inputImage = inputImage else { return nil }
return ColorFilter.kernel.apply(
extent: inputImage.extent,
roiCallback: { _, rect in
return rect
},
arguments: [inputImage])
}
}
在这里,你:
- 1) 首先导入
Core Image
框架。 - 2) 子类化
CIFilter
包括两个主要步骤:- 指定输入参数。在这里,您使用
inputImage
。 - 重写
outputImage
。
- 指定输入参数。在这里,您使用
- 3) 然后,您声明一个静态属性
kernel
,它加载ColorFilterKernel.ci.metallib
的内容。这样,库只加载一次。然后使用ColorFilterKernel.ci.metallib
的内容创建CIColorKernel
的实例。 - 4) 接下来,您
override outputImage
。在这里,您通过使用apply(extent:roiCallback:arguments:)
来应用内核。extent
决定了有多少输入图像被传递到内核。
您传递了整个图像,因此过滤器将应用于整个图像。 roiCallback
确定在 outputImage
中渲染矩形所需的输入图像的矩形。在这里, inputImage
和 outputImage
的 rect
没有改变,所以你返回相同的值并将参数数组中的 inputImage
传递给内核。
现在您已经创建了颜色内核过滤器,您将把它应用到图像上。
Applying the Color Kernel Filter
打开 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:)
。 这需要一个 CIImage
作为输入。 您可以通过创建 ColorFilter
的实例来创建自定义过滤器。
过滤器的 outputImage
应用了颜色内核。 然后使用 renderAsUIImage(_:)
创建 UIImage
实例并将其设置为输出。
接下来,在 process(painting:effect:)
中处理 .colorKernel
,如下所示。 在default
之上添加这个新case
:
case .colorKernel:
applyColorKernel(input: input)
在这里,您调用 applyColorKernel(input:)
来应用您的自定义颜色内核过滤器。
最后,打开 PaintingWall.swift
。 在 Button
的 action
闭包中 case 0
正下方的 switch
语句中添加以下内容:
case 1:
effect = .colorKernel
这将第二幅画的效果设置为 .colorKernel
。
构建并运行。 现在点击第二幅画“The Last Supper”
。 您将看到应用的颜色内核过滤器和图像中交换的 RGBA
值。
很好! 接下来,您将对达芬奇的神秘Salvator Mundi创建酷炫的扭曲效果。
Creating a Warp Kernel
与颜色内核类似,您将从添加 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;
float x = dest.coord().x + tan(dest.coord().x/ 10) * 20;
return float2(x,y);
}
}
}
这是您添加的内容:
- 1) 就像在颜色内核
Metal
源中一样,您包含Core Image header
并将该方法包含在extern "C"
括号中。 然后指定coreimage
命名空间。 - 2) 接下来,您使用
destination
类型的输入参数声明warpFilter(_:)
,允许访问您当前正在计算的像素的位置。 它返回输入图像坐标中的位置,然后您可以将其用作源。
您可以使用 coord()
访问目标像素的 x
和 y
坐标。 然后,您应用简单的数学运算来转换坐标并将它们作为源像素坐标返回,以创建有趣的平铺效果。
注意:尝试在
warpFilter(_:)
中用sin
替换tan
,你会得到一个有趣的失真效果!
Loading the Warp Kernel
与您为颜色内核创建的过滤器类似,您将创建一个自定义过滤器来加载和初始化扭曲内核。
在过滤器Filters
组中创建一个新的 Swift
文件。 将其命名为 WarpFilter.swift
并添加:
import CoreImage
// 1
class 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? Data(contentsOf: url) else {
fatalError("Unable to load metallib")
}
guard let kernel = try? CIWarpKernel(
functionName: "warpFilter",
fromMetalLibraryData: data) else {
fatalError("Unable to create warp kernel")
}
return kernel
}()
// 3
override var outputImage: CIImage? {
guard let inputImage = inputImage else { return .none }
return WarpFilter.kernel.apply(
extent: inputImage.extent,
roiCallback: { _, rect in
return rect
},
image: inputImage,
arguments: [])
}
}
在这里,你:
- 1) 创建
WarpFilter
作为CIFilter
的子类,以inputImage
作为输入参数。 - 2) 接下来,您声明静态属性
kernel
以加载WarpFilterKernel.ci.metallib
的内容。 然后使用.metallib
的内容创建CIWarpKernel
的实例。 - 3) 最后,您通过重写
outputImage
来提供输出。 在override
中,您使用apply(extent:roiCallback:arguments:)
将内核应用于inputImage
并返回结果。
Applying the Warp Kernel Filter
打开 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
应用了扭曲内核。 然后使用 renderAsUIImage(_:)
创建 UIImage
实例并将其保存到输出。
接下来,在 process(painting:effect:)
中添加以下 case
,在 case .colorKernel
下:
case .warpKernel:
applyWarpKernel(input: input)
在这里,您处理 .warpKernel
的case
并调用 applyWarpKernel(input:)
来应用扭曲内核过滤器。
最后,打开 PaintingWall.swift
。 在action
中 case 1
正下方的 switch
语句中添加以下 case
:
case 2:
effect = .warpKernel
这将第三幅画的效果设置为 .warpKernel
。
构建并运行。 点击 Salvator Mundi
的画作。 你会看到一个有趣的基于扭曲的瓷砖效果应用。
恭喜! 您将自己的风格应用于杰作!
1. Challenge: Implementing a Blend Kernel
CIBlendKernel
针对混合两个图像进行了优化。 作为一个有趣的挑战,为 CIBlendKernel
实现一个自定义过滤器。 一些提示:
- 1) 创建一个
CIFilter
的子类,它接收两个图像:输入图像和背景图像。 - 2) 使用内置的可用
CIBlendKernel
内核。 对于此挑战,请使用built-in multiply混合内核。 - 3) 在
ImageProcessor
中创建一个方法,将混合内核过滤器应用于图像并将结果设置为输出。 您可以使用项目资产中提供的multi_color
图片作为滤镜的背景图片。 另外,处理.blendKernel
的case
。 - 4) 将此过滤器应用于
PaintWall.swift
中的第四张图像。
您将在下载的材料中找到在最终项目中实施的解决方案。 祝你好运!
Debugging Core Image Issues
了解 Core Image
如何渲染图像可以帮助您在图像未按预期显示时进行调试。 最简单的方法是在调试时使用 Core Image Quick Look
。
1. Using Core Image Quick Look
打开 ImageProcessor.swift
。 在 applyColorKernel(input:)
中设置output
的行上放置一个断点。 构建并运行。 点击“The Last Supper”
。
当您遇到断点时,将鼠标悬停在 outputImage
上。 你会看到一个显示地址的小弹出框。
单击眼睛符号。 将出现一个窗口,显示制作图像的图形。 很酷吧?
2. Using CI_PRINT_TREE
CI_PRINT_TREE
是基于与 Core Image Quick Look
相同的基础架构的调试功能。 它有多种模式和操作。
选择并编辑 RayVinci
的scheme
。 选择 Run
选项卡并添加 CI_PRINT_TREE
作为值为 7 pdf
的新环境变量。
CI_PRINT_TREE
的值采用 graph_type output_type
选项的形式。
graph_type
表示Core Image
渲染的阶段。 以下是您可以指定的值:
-
1
:显示颜色空间的初始图形。 -
2
:一个优化的图表,显示了Core Image
是如何优化的。 -
4
:显示您需要多少内存的连接图。 -
7
:详细日志记录。 这将打印所有上述图表。
对于 output_type
,您可以指定 PDF
或 PNG
。 它将文档保存到一个临时目录。
构建并运行。 在模拟器中选择“The Last Supper”
。 现在,通过使用终端导航到 /tmp
来打开 Mac
上的临时目录。
您将看到所有图形为 PDF
文件。 打开以 _initial_graph.pdf
作为后缀的文件之一。
输入在底部,输出在顶部。 红色节点代表颜色内核(color kernels)
,而绿色节点代表扭曲内核(warp kernels)
。 您还将看到每个步骤的ROI
和范围。
要了解有关可以为 CI_PRINT_TREE
设置的各种选项的更多信息,请查看此 WWDC 会议:Discover Core Image debugging techniques。
在本教程中,您学习了使用基于 Metal
的 Core Image
内核创建和应用自定义过滤器。 要了解更多信息,请查看这些 WWDC 视频:
- Advances in Core Image: Filters, Metal, Vision and More
- Build Metal-based Core Image kernels with Xcode
您还可以参考 Apple 的指南,Metal Shading Language for Core Image Kernels。
后记
本篇主要讲述了使用
Metal Shading Language
创建你自己的Core Image
滤波器进行像素级图像处理,感兴趣的给个赞或者关注~~~~