数百个 Core Image 过滤器包装为 CIImage 修饰符,以便与 SwiftUI 轻松集成

SwiftUICoreImage

帮助在 SwiftUI 的上下文中使用 Core Image。即使没有 SwiftUI 也很有用。

介绍

Core Image 是 macOS 和 iOS 中出色的图像处理工具包,但使用起来有点笨拙。即使在 Apple 将 Swift API 添加到许多过滤器 ( CoreImage.CIFilterBuiltins ) 之后,将过滤器链接到图像仍然非常乏味。

这个包的目的是提供一种更简单的方法来将多个过滤器链接到 CIImage 实例,然后将它们渲染到 SwiftUI(或任何其他上下文——不需要 SwiftUI)。

    Image(ciImage: CIImage("Bernie.jpeg")
        .sepiaTone(intensity: sepia)
        .recropping { image in
            image
                .clampedToExtent(active: clamped)
                .gaussianBlur(radius: gaussianBlurRadius)
        }
    )
        .resizable()
        .aspectRatio(contentMode: .fit)

显现

此包中包含:

  • CIImage-Filters.swift
    • 208 修饰符CIImage返回一个新的修改CIImage(如果未修改则返回原始)
    • 返回新生成的 20 个静态函数CIImage
  • CIImage-Extensions.swift
    • CIImage来自资源名称和NSImage/的便利初始值设定项UIImage
    • 用于返回裁剪、缩放等的修饰符CIImage,以便更容易与 SwiftUI 一起使用
    • 重载几个带有布尔参数CIImage的内置修饰函数active
  • 图像扩展.swift
    • Image从一个创建 SwiftUI 的便利初始化器CIImage

这是如何运作的

类似于 SwiftUI 视图修改器如何返回一个修改后的View实例,这些修改器CIImage通过创建相应的CIFilter、为您连接inputImage并返回结果来处理核心图像链接outputImage

在创建 SwiftUI 代码时,我认为您可以使用惰性修饰符,您可以在其中传入一些导致修饰符无效的参数,这一点很重要。(例如,为视图指定 1.0 的不透明度或 0.0 的填充。)

在这段代码中,我确保我们的每个图像修改器都带有惰性修改器:在某些情况下,它传递的参数显然没有效果(例如零强度、零半径);或者与另一张图片组合时为零背景图片;或布尔active参数。如果指定的参数不会导致图像发生任何变化,则立即返回身份(自我)。

CIImage-Filters.swift 的内容是生成的源代码,使用我包含在此存储库中的代码(CIImage-Generation.swift,不包含在包导入中)。这会循环遍历 Apple 提供的核心图像元数据 ( CIFilter.filterNames(inCategories: nil))。不幸的是,这个列表有些过时并且包含一些我已尽力克服的不一致之处。有一些 JSON 文件提供额外的元数据,例如实际上有在线文档的函数列表——56 个函数没有记录,因此需要一些猜测——或者修复丢失或过时的文档。您可能不需要运行此代码,除非您有一些特殊要求或列表已在未来(Ventura 后,iOS-16 后)操作系统版本中更新。

与 SwiftUI 一起使用

请记住,Core Image 操作实际上只是处理步骤的“配方”;直到需要将图像渲染为位图时才执行实际工作。

这段代码提供了Image一个新的初始化程序CGImage. 当 SwiftUI 需要渲染图像时,将 Core Image 渲染到屏幕上。NSImageUIImageImageCIImage

那么,您的典型方法是创建一个Image,使用内置初始化程序CIImage之一或此处包含的便捷方法传入 created 以从资源名称或其他图像类型创建。

然后,只需将修饰符链接到它CIImage以指示要修改的内容。

许多修饰符都很简单。例如:

    Image(ciImage: CIImage("Halloween.jpeg")
        .xRay()
    )

如果您希望切换是否应用过滤器,请使用active参数(默认值为true):

    Image(ciImage: CIImage("Halloween.jpeg")
        .xRay(active: isMachineOn)
    )

链接在中找到的任意数量的修饰符CIImage-Filters.swift以构造所需的结果。

图像缩放

许多 Core Image 过滤器使用像素值作为参数。因此,可能需要在应用操作之前将图像缩放到适当的大小。例如,对 6000⨉4000 的图像应用 10 像素半径的模糊,然后将其缩小到 300⨉200 可能不会产生您想要的结果;也许您想先将图像缩放到 300⨉200,然后应用 10 像素半径模糊。

Core Image 提供了一个缩放操作(CILanczosScaleTransformlanczosScaleTransform()),但这个包还包括更方便的替代方法:scaledToFill()以及scaledToFit()您在其中传递所需尺寸的地方。

这个的典型用法与GeometryReader. 例如:

    GeometryReader { geo in
        let geoSize: CGSize = geo.frame(in: .local).integral.size
        // Resize image to double the frame size, assuming we are on a retina display
        let newSize: CGSize = CGSize(width: geoSize.width * 2,
                                    height: geoSize.height * 2)

        Image(ciImage: CIImage("M83.jpeg")
            .scaledToFit(newSize)
            .sharpenLuminance(sharpness: 1.0, radius: 5)
        )
            .resizable()    // Make sure retina image is scaled to fit
            .aspectRatio(contentMode: .fit)
    }

对比原图,未预缩放锐化,预缩放后锐化

在没有 SwiftUI 的情况下使用

根本不需要 SwiftUI。只需创建一个CIImage并执行操作。然后,渲染为位图。

    let tiledImage: CIImage = CIImage("HeyGoodMorning.png").
        .triangleTile(center: .zero, angle: 0.356, width: 2.0)

    imageView.image = UIImage(CIImage: tiledImage)

其他注意事项

如果您使用过 Core Image,您就会知道有时您需要调整图像的范围,例如在应用高斯模糊之前将图像限制为具有无限边缘,然后重新裁剪到图像的原始范围。为此,您可以使用recropping后跟闭包的修饰符。该操作保存图像的范围,应用闭包中的任何内容,然后重新裁剪到该范围。在下面的示例中,图像 inciImage被转换为沿其边缘的像素颜色在所有方向无限延伸的图像,然后它被模糊,然后在退出闭包时,返回的图像被重新裁剪。

    ciImage
        .recropping { image in
            image
                .clampedToExtent()
                .gaussianBlur(radius: 10)
        }

比较未模糊、不适当模糊和适当模糊

recropping如果您发现过滤器(例如,comicEffect()已经稍微增加了图像的范围并且您想将其限制为原始大小),修改器也很有用。

另一个有用的操作是replacing很像recropping,只是它不会影响图像的范围。你传入一个闭包,它以你正在处理的图像开始;你的关闭返回一个新图像。这在需要传入背景图像的 Core Image 中进行合成操作时非常有用。如果您的操作链在背景图像上,并且您想在上面叠加一些东西怎么办?只需将您的操作包装起来.replacing并返回合成图像。

    ciImage
        .replacing { backgroundImage in
            ciImage2
                .sourceAtopCompositing(backgroundImage: backgroundImage)
        }

在这种情况下,中的图像ciImage2是前景图像,放置在 之上backgroundImage,然后返回到操作链。

使用包

在 Xcode 中,选择 File > Add Packages… 然后在搜索栏中输入这个存储库的 URL,然后从那里继续。

在你的代码中:

import SwiftUICoreImage

就是这样!


如果您能想到对生成的过滤器的代码或文档进行改进,或者找到任何其他有用的实用程序来在此工具包中操作 Core Images,请提交问题或请求请求!

GitHub

查看 Github