版本记录
版本号 | 时间 |
---|---|
V1.0 | 2022.05.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(一)
15. Core Image框架详细解析(十五) —— 创建自定义滤波器 Creating Custom Filters(二)
16. Core Image框架详细解析(十六) —— 包装和加载图像单元 Packaging and Loading Image Units
17. Core Image框架详细解析(十七) —— 一个简单说明和示例(一)
18. Core Image框架详细解析(十八) —— 使用Metal Shading Language创建你自己的Core Image滤波器进行像素级图像处理(一)
19. Core Image框架详细解析(十九) —— 使用Metal Shading Language创建你自己的Core Image滤波器进行像素级图像处理(二)
开始
首先看下主要内容:
使用
Core Image
和Swift
学习图像过滤效果的基础知识。内容来自翻译。
接着看下写作环境
Swift 5.5, iOS 15, Xcode 13
下面就是正文了
Core Image
是一个强大的框架,可让您将过滤器应用于图像。 它提供各种效果,例如修改活力、色调或曝光(vibrancy, hue or exposure)
。 它可以使用 CPU
或 GPU
快速处理图像数据——速度足以实时处理视频帧!
您可以将 Core Image
过滤器链接在一起,以一次将许多效果应用于图像或视频帧。 许多过滤器组合成一个过滤器并应用于图像。 与通过每个过滤器处理图像相比,这使得它非常有效,一次一个。
在本教程中,您将获得使用 Core Image
的实践经验。 您将应用一些不同的过滤器,并了解实时对图像应用炫酷效果是多么容易。
在开始之前,先看看 Core Image
框架中一些最重要的类:
-
CIContext。
CIContext
完成core image
的所有处理。 这有点像Core Graphics
或OpenGL
上下文。 -
CIImage。 此类保存图像数据。 一个
UIImage
、一个图像文件或像素数据都可以创建它。 -
CIFilter。
CIFilter
类有一个字典。 这定义了它所代表的特定过滤器的属性。 滤波器的示例包括活力、颜色反转、裁剪等等(vibrancy, color inversion, cropping)
。
您将在此项目中使用这些类中的每一个。
1. CoreImageFun
打开CoreImageFun.xcodeproj
并运行它。 这是一个简单的应用程序,一个带有图像和滑块的单一屏幕。 滑块还没有做任何事情,但我们将使用它来展示 CIFilter
的功能。 您还会注意到屏幕右上角的相机按钮。 您将在本教程后面使用它来调出图像选择器。
Image-Filtering Basics
您将首先通过 CIFilter
运行图像并将其显示在屏幕上。每次你想对图像应用 CIFilter
时,你需要做四件事:
- 1) 创建一个
CIImage
对象。一个CIImage
有几个初始化方法。在本教程中,您将使用CIImage(image:)
从UIImage
创建CIImage
。浏览文档documentation以了解更多创建 CIImage 的方法。 - 2) 创建一个
CIContext
。CIContext
可以是基于CPU
或GPU
的。CIContext
的初始化成本很高,因此您可以重用它而不是一遍又一遍地创建它。输出CIImage
对象时总是需要一个。 - 3) 创建一个
CIFilter
。创建过滤器时,您会在其上配置一些属性,这些属性取决于您使用的过滤器。 - 4) 获取过滤器输出。过滤器为您提供作为
CIImage
的输出图像。您可以使用CIContext
将其转换为 UIImage,如下所示。
1. Applying Filter
在理论信息之后,是时候看看它是如何工作的了。将以下代码添加到 ViewController.swift
:
func applySepiaFilter(intensity: Float) {
// 1
guard let uiImage = UIImage(named: "image") else { return }
let ciImage = CIImage(image: uiImage)
// 2
guard let filter = CIFilter(name: "CISepiaTone") else { return }
// 3
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(intensity, forKey: kCIInputIntensityKey)
// 4
guard let outputImage = filter.outputImage else { return }
// 5
let newImage = UIImage(ciImage: outputImage)
imageView.image = newImage
}
这是代码的作用。 它:
- 1) 创建一个
UIImage
并使用它来创建一个CIImage
。 - 2) 创建一个
CISepiTone
类型的CIFilter
。 这是棕褐色调的类型。 - 3)
CISepiaTone
过滤器有两个值。 首先是一个输入图像:kCIInputImageKey
,它是一个CIImage
实例。 其次,一个intensity:kCIInputIntensityKey
,一个介于 0 和 1 之间的浮点值。如果没有任何值,大多数过滤器都使用它们的默认值。CIImage
是一个例外。 这必须提供一个值,因为没有默认值。 - 4) 使用
outputImage
属性从过滤器中获取一个CIImage
。 - 5) 将
CIImage
转回UIImage
并将其显示在image view
中。
接下来,通过将以下内容添加到 viewDidLoad()
来调用添加的新方法:
applySepiaFilter(intensity: 0.5)
这会触发强度值为 0.5
的图像过滤。 在本教程的后面部分,您将使用滑块来尝试各种强度值。
构建并运行项目。 你会看到你的图像被sepia tone filter
过滤:
恭喜,你已经很好地使用了 CIImage
和 CIFilters
!
Putting it Into Context
在继续之前,您应该了解一项优化。
如上所述,您需要一个 CIContext
来应用 CIFilter
。 但是在上面的例子中没有提到这个对象。 事实证明 UIImage(CIImage:)
为您完成了所有工作。 它创建一个 CIContext
并使用它来过滤图像。 这使得 Core Image API
很容易使用。
有一个主要缺点:每次使用它都会创建一个新的 CIContext
。 CIContext
实例应该可重用以提高性能。 如果要使用滑块更新过滤器值,则每次更改过滤器时都必须创建一个新的 CIContext
。 这种方法会很慢。
首先,将以下属性添加到 ViewController
:
let context = CIContext(options: nil)
CIContext
接受一个可选字典。 它指定诸如颜色格式或上下文是否应该在 CPU 或 GPU 上运行等选项。 对于这个应用程序,默认值很好,所以你为那个参数传入 nil
。
接下来,从 applySepiaFilter(intensity:)
中删除 Step 5
并将其替换为以下内容:
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
imageView.image = UIImage(cgImage: cgImage)
在这里,您使用 CIContext
绘制一个 CGImage
并使用它来创建一个 UIImage
以显示在image view
中。
构建并运行。 确保它像以前一样工作。
在这个例子中,自己处理 CIContext
创建没有太大区别。您将了解为什么这样做对性能很重要,因为您在下一节中设置了更改过滤器的能力。
Changing Filter Values
这很棒,但这只是你可以使用 Core Image
滤镜做的事情的开始。是时候使用图像下方那个漂亮的滑块来改变滤镜效果了。
您已经为 CIContext
实例添加了一个属性。现在,您将添加一个属性来保存过滤器。
已经有一个 IBAction
连接到滑块的 Value Changed
操作。它被称为 sliderValueChanged(_:)
。在此方法中,您将在滑块值更改时重做图像过滤器。但是你不想重做整个过程。那将是非常低效的,并且会花费太长时间。您需要更改类中的一些内容,因此您需要保留在 applySepiaFilter(intensity:)
中创建的一些对象。
在context
声明的正下方添加以下属性:
let filter = CIFilter(name: "CISepiaTone")!
接下来,在调用 applySepiaFilter(intensity:)
之前将以下内容添加到 viewDidLoad()
中:
guard let uiImage = UIImage(named: "image") else { return }
let ciImage = CIImage(image: uiImage)
filter.setValue(ciImage, forKey: kCIInputImageKey)
在这里,您将图像设置给滤波器。 你在 applySepiaFilter(intensity:)
中的前面这个。 但最好将其移动到 viewDidLoad()
以防止对每个滑块值更改的调用。
您将一些代码移至 viewDidLoad()
,因此将 applySepiaFilter(intensity:)
替换为以下内容:
func applySepiaFilter(intensity: Float) {
filter.setValue(intensity, forKey: kCIInputIntensityKey)
guard let outputImage = filter.outputImage else { return }
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
imageView.image = UIImage(cgImage: cgImage)
}
最后,将以下内容添加到 sliderValueChanged(_:)
:
applySepiaFilter(intensity: slider.value)
当滑块值改变时,applySepiaFilter(intensity:)
将以新的强度值运行。
您的滑块设置为默认值:最小 0,最大 1,默认 0.5。 多么方便! 这些恰好是这个 CIFilter
的正确值。
构建并运行。 您应该有一个功能正常的实时滑块,可以实时更改图像的sepia
值。
Getting Photos From the Photo Album
现在您可以立即更改过滤器的值,事情变得有趣了! 但是,如果您不喜欢这种花的形象怎么办? 接下来,您将设置一个 UIImagePickerController
以从相册中获取图片并进入您的应用程序,以便您可以使用它们。
已经有一个 IBAction
连接到相机按钮的 Touch Up Inside
动作。 它被称为 loadPhoto()
。 将以下代码添加到 loadPhoto()
:
let picker = UIImagePickerController()
picker.delegate = self
present(picker, animated: true)
第一行代码实例化了一个新的 UIImagePickerController
。 将图像选择器的代理设置为 self(ViewController)
,然后展示选择器。
这里有一个编译器错误。 您需要声明 ViewController
符合 UIImagePickerControllerDelegate
和 UINavigationControllerDelegate
协议。
在 ViewController.swift
的底部添加以下扩展:
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
}
}
此代理方法返回所选图像以及info
字典中的一些相关信息。 有关此字典中各种数据的更多详细信息,请查看documentation。
构建并运行该应用程序,然后点击按钮,该按钮将显示图片选择器,其中包含您相册中的照片。
选择图像什么都不做。 你即将改变这一点。 将以下代码添加到 imagePickerController(_:didFinishPickingMediaWithInfo:)
:
//1
guard let selectedImage = info[.originalImage] as? UIImage else { return }
//2
let ciImage = CIImage(image: selectedImage)
filter.setValue(ciImage, forKey: kCIInputImageKey)
//3
applySepiaFilter(intensity: slider.value)
//4
dismiss(animated: true)
这是一个代码分解。 它:
- 1) 使用
originalImage
UIImagePickerController.InfoKey
键检索选择图像。 - 2) 将所选图像应用到滤镜。
- 3) 使用强度的当前滑块值调用
applySepiaFilter(intensity:)
。 这将更新image view
。 - 4) 完成对所选图像的过滤后关闭图像选择器。
构建并运行。 现在,您将能够更新相册中的任何图像。
What About Image Metadata?
当然,是时候谈谈图像元数据了。 在手机上拍摄的图像文件具有与之相关的各种数据,例如 GPS
坐标、图像格式和方向。
尤其是方向是您需要保留的东西。 将 UIImage
加载到 CIImage
中,渲染到 CGImage
并转换回 UIImage
会从图像中剥离元数据。 为了保留方向,你需要记录它,然后将它传回 UIImage
。
首先向 ViewController.swift
添加一个新属性:
var orientation = UIImage.Orientation.up
接下来,在调用 applySepiaFilter(intensity:)
之前,将以下行添加到 imagePickerController(_:didFinishPickingMediaWithInfo:)
:
orientation = selectedImage.imageOrientation
这会将选定的图像方向保存到属性中。
最后,更改 imageView
对象中设置的 applySepiaFilter(intensity:)
中的行:
imageView.image = UIImage(cgImage: cgImage, scale: 1, orientation: orientation)
现在,如果您以非默认方向拍摄照片,应用程序会保留它。
What Other Filters Are Available?
CIFilter
最大的优势之一是链接过滤器的能力。 为此,您将创建一个专用方法来处理 CIImage
并对其进行过滤,使其看起来像一张旧照片。
CIFilter API
在macOS
上有160
多个过滤器,其中大部分在 iOS 上也可用。 现在也可以创建自定义过滤器。要查看任何可用的过滤器或属性,请查看documentation。
1. Creating Old Photo Filter
在 ViewController
中添加以下方法:
func applyOldPhotoFilter(intensity: Float) {
// 1
filter.setValue(intensity, forKey: kCIInputIntensityKey)
// 2
let random = CIFilter(name: "CIRandomGenerator")
// 3
let lighten = CIFilter(name: "CIColorControls")
lighten?.setValue(random?.outputImage, forKey: kCIInputImageKey)
lighten?.setValue(1 - intensity, forKey: kCIInputBrightnessKey)
lighten?.setValue(0, forKey: kCIInputSaturationKey)
// 4
guard let ciImage = filter.value(forKey: kCIInputImageKey) as? CIImage else { return }
let croppedImage = lighten?.outputImage?.cropped(to: ciImage.extent)
// 5
let composite = CIFilter(name: "CIHardLightBlendMode")
composite?.setValue(filter.outputImage, forKey: kCIInputImageKey)
composite?.setValue(croppedImage, forKey: kCIInputBackgroundImageKey)
// 6
let vignette = CIFilter(name: "CIVignette")
vignette?.setValue(composite?.outputImage, forKey: kCIInputImageKey)
vignette?.setValue(intensity * 2, forKey: kCIInputIntensityKey)
vignette?.setValue(intensity * 30, forKey: kCIInputRadiusKey)
// 7
guard let outputImage = vignette?.outputImage else { return }
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return }
imageView.image = UIImage(cgImage: cgImage, scale: 1, orientation: orientation)
}
这是正在做的事情 :
- 1) 在您之前使用的
sepia-tone
中设置强度。 - 2) 建立一个过滤器,创建一个如下所示的随机噪声模式:
它不带任何参数。您将使用此噪点模式为最终的“旧照片”外观添加纹理。
-
3) 改变随机噪声发生器的输出。您想将其更改为灰度并使其变亮一点,因此效果不那么引人注目。输入图像键设置为随机过滤器的
outputImage
属性。这是一种将一个过滤器的输出作为下一个过滤器的输入传递的便捷方式。 -
4) 已
cropped(to:)
获取输出CIImage
并将其裁剪为提供的矩形。在这种情况下,您需要裁剪CIRandomGenerator
过滤器的输出,因为它会一直持续下去。如果你在某个时候不裁剪它,你会得到一个错误,说过滤器有“an infinite extent”
。CIImages
实际上并不包含图像数据;他们描述了创建它的“配方”。直到您调用CIContext
上的方法才处理数据。 -
5) 结合
sepia
和CIRandomGenerator
过滤器的输出。后者执行与Adobe Photoshop
图层中的“Hard Light”
设置相同的操作。Photoshop
中的大多数(如果不是全部)滤镜选项都可以使用Core Image
实现。 -
6) 在此合成输出上运行
vignette
滤镜,使照片的边缘变暗。您可以使用强度值来设置此效果的半径和强度。 -
7) 获取输出图像并将其设置给
image view
。
2. Applying Old Photo Filter
这就是这个过滤器链的全部内容。 您现在已经了解这些过滤器链可能变得多么复杂。 您可以将 Core Image
滤镜组合到这些类型的链中,这样您就可以实现无穷无尽的各种效果。
如果您想查看所有这些操作,请将所有对 applySepiaFilter(intensity:)
的调用替换为 applyOldPhotoFilter(intensity:)
。
构建并运行。 您应该获得更精致的旧照片效果,包括棕褐色、少许噪点和一些暗角。
亲爱的读者,这种噪音可能会更微妙,但如何完善取决于您。 现在,您可以使用 Core Image
的全部功能。 疯了!
这大约涵盖了使用 Core Image
过滤器的基础知识。 这是一种非常方便的技术,您应该能够使用它快速地对图像应用一些简洁的过滤器。
此外,此站点上还有更多 Core Image 教程,包括:
您可以查看 Core Image filter reference documentation以获取有关所有过滤器的说明。
后记
本篇主要讲述了使用
Core Image
和Swift
学习图像过滤效果的基础知识,感兴趣的给个赞或者关注~~~
网友评论