Main

WWDC23: Lift subjects from images in your app | Apple

Discover how you can easily pull the subject of an image from its background in your apps. Learn how to lift the primary subject or to access the subject at a given point with VisionKit. We’ll also share how you can lift subjects using Vision and combine that with lower-level frameworks like Core Image to create fun image effects and more complex compositing pipelines. For more information about the latest updates to VisionKit, check out “What’s new in VisionKit." And for more information about person segmentation in images, watch "Explore 3D body pose and person segmentation in Vision" from WWDC23. More Apple Developer resources: Video sessions: https://apple.co/VideoSessions Documentation: https://apple.co/DeveloperDocs Forums: https://apple.co/DeveloperForums App: https://apple.co/DeveloperApp

Apple Developer

2 days ago

♪ ♪ Lizzy:大家好!我是 Lizzy Apple VisionKit 团队的 一名工程师 很高兴今天能和大家谈谈 如何在你的 App 中 添加主题提取功能 主题提取功能 是在 iOS 16 中引入的 允许用户选择、 提取和共享图像主题 首先 我将回顾一下 主题提取的基础知识 然后 我将向你介绍如何使用 新的 VisionKit API 添加主题提取功能 最后 我的同事 Saumitro 将详细介绍 新的底层 Vision API 那到底什么是主题呢? 主题是指照片的前景对象或对象 主题不总是一个人或一只宠物 它可以是任何东西 比如一座建筑、一盘食物或几双鞋子 图像可以有多个主题 就像这里的三杯咖啡一样 重要的是要注意 主题并不总是一个单独的对象 在本例中 这个男人和他的狗一起 成为图像的焦点 使其成为一个组合主题 那么如何才能将此功能 加入到一个 App 中呢? 有两个可用的独立 API 可帮你 将主题提取添加到你的 App 中 VisionKit 和 Vision VisionKit 让你能非常轻松地 采用系统性主题提取行为 开箱即用 你只需几行代码 即可轻松重建 我们熟知和
喜爱的主题提取 UI VisionKit 还公开了一些 关于这些主题的基本信息 因此你可以为人们提供 与图像主题交互的新方法 所有这些都发生在进程外 这具有性能优势 但图像大小也因此受限 Vision 是一个底层框架 没有开箱即用的 UI 这意味着其不与视图绑定 赋予了你更多的灵活性 图像分析发生在进程内 而且不像 VisionKit 那样 受到图像分辨率的限制 最后 这个 API 可以成为 更高级的图像编辑管线的一部分 比如那些使用 CoreImage 的管线 首先 让我们深入了解 VisionKit 中的主题提取 API 要使用 VisionKit 添加主题提取 只需初始化一个 ImageAnalysisInteraction 并将其添加到包含图像的视图中 这可以是 UIImageView 但不需要是 就是这么简单 现在 你的图像将具有 系统主题提取交互功能 同样地 在 macOS 上创建 一个 ImageAnalysisOverlayView 并把它添加为包含你图像的 NSView 的一个子视图 你可以设置 ImageAnalysisInteraction 或 ImageAnal
ysisOverlayView 的 首选交互类型 以选择支持 哪种类型的 VisionKit 交互 默认交互类型为 .automatic 它可以反映为系统行为 如你想要主题提取、实时文本 和数据检测器功能 请使用此类型 新的 imageSubject 类型 仅包括主题提取功能 适用于你不想要文本交互的情况 除了这些 UI 交互之外 VisionKit 还允许你 使用 ImageAnalysis 以编程方式访问图像主题 要生成图像分析 你只需创建一个 ImageAnalyzer 然后调用 analyze 函数 传入所需的图像和分析器配置 使用 ImageAnalysis 的 subjects 属性 你可异步访问所有图像主题列表 该属性使用新的 Subject 结构 其中包含图像及其边界 highlightedSubjects 属性 返回一组凸显的主题 在本例中 底部的两个主题已被凸显 用户可长按图像来凸显某主题 但你也可在代码中 更新 highlightedSubjects 集合 来更改选择状态 你可使用异步 subject(at:) 方法 按指向点查找某主题 在本例中 轻点这里 会返回
中间的主题 如果该指向点没有主题 则此方法将返回 nil 最后 你可以使用两种方式 来生成主题图像 对于单个主题 只需访问主题的图像属性 如你需要由多个主题组成的图像 请使用异步 image(for:) 方法 并传递你想要包括的主题 在本例中 如果我只想要 底部的两个主题的图像 我可以使用这个方法生成此图像 让我们在演示中看看 这一切如何组合在一起 我正在开发一个拼图 App 我想将拼图碎片拖动到拼图中 但却无法提起任何一块拼图碎片 让我们来解决这个问题 首先 我需要在该图像中 启用主题提取交互 这样就可以和拼图碎片进行交互了 为此 我可以创建一个 ImageAnalysisInteraction 并简单地将其添加到我的视图中 我在这里使用了 imageSubject 交互类型 因为我不需要包含实时文本 太棒了! 现在我可以像这样 选择拼图碎片并与之进行交互 该图像没有经过任何预处理 只需通过主题提取即可完成 我添加了一些代码 将拼图碎片拖入拼图中 甚至可以将拼图碎片归位 这看起来很酷 但我想让我的 App 更有吸引力 当我悬停在每个拼图碎片上方时 我想在下面添加一个投影 以呈现微弱的
3D 特效 此处我已有一个悬停手势处理器 我只需添加阴影 我不能轻易地编辑图像 所以我将用图像分层技巧来代替 首先 我会调用 imageAnalysis.subject(at point:) 检查我是否悬停在某个主题上 我有一个 addShadow(for subject:) 方法 该方法插入主题图像的副本 将其变成灰色 并将原主题位置略微进行偏移 然后 我在阴影上方 添加主题图像的副本 所以它看起来是三维的 最后 如果悬停点未与主题相交 我会清除阴影 让我们来试试吧 太棒了 现在当我悬停在拼图碎片上面时 这些拼图碎片会产生阴影特效 使用 VisionKit 我能 在我的 App 中设置主题提取 甚至只需用几行代码 就能添加有趣的主题特效 接下来的内容 我将交给我的同事 Saumitro 他将介绍一些新的 Vision API 以及如何将其集成到你的 App 中 Saumitro:谢谢 Lizzy! 大家好 我是 Saumitro Vision 团队的一名工程师 VisionKit 的 API 是开始使用主题提取的最简单方法 对于需要更高级功能的 App Vision 也可满足其需求
主题提取加入了 Vision 现有的图像分割 API 集合 如显著性检测和人像分割 让我们快速回顾一下其优点 并看看主题提取如何进行 显著性检测请求 比如视线注视点和目标检测 最适用于粗略的、基于区域的分析 请注意 生成的显著图分辨率相当低 因此不适合进行分割 相反 你可使用显著区域来完成 诸如自动裁剪图像之类的任务 人像分割 API 在场景中为人像生成 详细的分割遮罩 如你特别想专注于 分割人像 请使用此功能 新的人像实例分割 API 通过为场景中的每个人 提供单独的遮罩来进一步提供服务 想进一步了解 请查看人像分割相关讲座 与人像分割相反 新引入的主题提取 API 是“不可知目标检测” 任何前景对象 无论其语义类别如何 都有可能被分割 比如 注意在这张图中除了人之外 是如何拣选出汽车的 现在让我们来看一下 涉及的一些关键概念 你开始输入图像 主题提取请求处理此图像 并生成相同分辨率的软分割遮罩 将此遮罩应用于源图 会生成遮罩图像 每个不同的分割对象都是一个实例 Vision 还为你提供有关这些实例的 像素级信息 该实例遮罩将源图中的像素 映射到其实例索引中 0 索引是为背景保留的
然后将每个前景实例 按顺序标记 从 1 开始 实例被连续标记 但不能保证这些 ID 的顺序 你可以使用这些索引来分割源图中的 前景对象子集 如果你正在设计一个交互式 App 此实例遮罩对于点击测试也很有用 稍后 我将演示 如何完成这两个任务 让我们深入了解 API 主题提取采用 Vision 中熟悉的 基于图像的请求模式 你首先要实例化前景实例遮罩请求 接着是一个图像请求处理器 用于对你输入的图像进行请求处理 然后执行请求 Vision 会在这时分析图像 以找到主题 虽然该过程已经过优化 可利用 Apple 的硬件优势来提高效率 但这仍是一项资源密集型任务 最好将其延迟到 一个后台线程 以免阻塞 UI 一个常见的方法是 在一个单独的 DispatchQueue 上 异步执行该步骤 如果在输入图像中 检测到一个或多个主题 结果数组将被一个单独观察填充 从这里开始 你可以查询观察结果 以获取遮罩和分割图像 让我们来仔细看下一两个参数 这两个参数控制着哪些实例会被分割 以及结果如何被裁剪 实例参数 是一个 IndexSet 用于控制 哪些对象会被 从最终分割图像或遮罩中提取 比如 此图像包
含两个前景实例 不包括背景实例 分割所有检测到的 前景实例是很常见的操作 因此 Vision 提供了一个 方便的 allInstances 属性 该属性返回包含 所有前景实例索引的 IndexSet 针对此处的图像 返回的索引中包括索引 1 和 2 但注意 其中不包括背景实例索引 0 你也可只提供这些索引的一个子集 此处只有实例 1 而此处只有实例 2 你还可控制 最终遮罩图像的裁剪方式 如果将此参数设置为 false 则输出图像分辨率 与输入图像相匹配 当你想保留分割对象的相对位置时 这点很有用 比如 用于下游合成操作 如果将其设置为 true 则你可得到所选实例的严格裁剪 在这个例子中 到目前为止 我一直在使用完全遮罩的图像输出 但是 对于某些操作 比如应用遮罩特效 仅使用分割遮罩可能更为方便 你可调用观察中的 createScaledMask 方法 来生成这些遮罩 参数的表现与之前相同 输出是一个包含软分割遮罩的 单通道浮点像素缓冲区 我刚刚生成的遮罩 非常适合与 CoreImage 一起使用 Vision 与 VisionKit 很像 也会产生标准动态范围输出 然而 在 Cor
eImage 中执行遮罩操作 可以保留输入的高动态范围 想进一步了解 请考虑查看讲座 “将高动态范围添加到你的 App 中” 执行此遮罩的一种方法 是使用 CIBlendWithMask 滤镜 我将从需要 进行遮罩处理的源图开始 通常这也就是 你要传入 Vision 的图像 调用 Vision 的 createScaledMask 获取遮罩 最后是设置新背景图像 主题将在该背景图像上合成 给新背景图像 设个空图像值可生成透明背景 或者 如果你计划 将结果合成在新背景之上 你可直接在此传参 差不多就是这样 输出将是保留高动态范围的 遮罩和合成图像 现在让我们将所有内容组合在一起 构建一个很酷的主题提取视效 App 你可以删除背景 并显示其下面的视图 或将其替换为其他内容 此外 你还可以 应用其中一个预设特效 特效即与所选背景合成 你甚至可以轻点前景实例 以有选择性地提取它 让我们来大概看一下 我将如何创建这个 App 我们 App 的核心依赖于一个特效管线 该管线接受来自 UI 的输入 并执行生成最终输出 所需的全部工作 我将从对源图执行主题提取开始 你可以通过可选轻点 选择单个实例 将
生成的遮罩应用于源图 最后 应用并合成 所选的背景和视觉特效 以生成最终输出图像 最后这两个步骤 将通过 CoreImage 来完成 我们的顶层函数接收到输入图像、 所选背景图像和特效、 还有可能是用户 选取其中一个实例的轻点位置 这里的 Effect 类型 只是我们预设的一个简单枚举 其输出是 准备在 UI 中显示的最终合成图像 该任务可分解为两步 首先 为所选实例生成主题遮罩 然后 使用该遮罩应用所选特效 让我们从第一步开始 此阶段的输入 是源图和可选的用户轻点位置 我们已经见过这里的大部分代码 这步只是 执行 Vision 请求并返回遮罩 有趣的是这一行 它使用了标签遮罩 将用户轻点位置映射到一组索引中 让我们来仔细看一下 如果用户没有轻点 则我将默认使用所有实例 我想将轻点位置 映射为实例遮罩中的一个像素 此处有两个相关信息 首先 UI 将轻点位置参数值 标准化为 0 和 1 然后传递该参数 这样做的好处是我无需担心 显示分辨率和缩放比例等细节问题 其次 它使用默认的 UIKit 坐标系统 其原点位于左上角 这与我们像素缓冲区的 图像空间坐标对齐 因此 我可以使用现有的 Vis
ion 帮助函数来执行此转换 我现在有了查找 已轻点的实例标签所需的全部信息 这涉及直接访问像素缓冲区的数据 我将向你展示如何执行此操作 一旦我有了标签 就会检查其是否为 0 请记住 0 标签意味着 用户轻点了背景像素 在此情况下 我将回退 选择所有实例 否则 我将返回一个 仅包含所选标签的单例集 这段代码填充了 如何执行实例标签查找的内容 与任意像素缓冲区相同 我首先要在访问其数据之前锁定该缓冲区 就我们的目的而言 只读访问就足够了 像素缓冲区的行 可被填充以进行对齐 因此 计算像素字节偏移量 最可靠的方法 就是使用其 bytesPerRow 值 由于 instanceMask 是单通道 UInt8 缓冲区 所以我不必担心任何进一步的缩放 我已从实例遮罩中读取完毕 因此我可以解锁缓冲区 这样一来 我就将 所选实例和遮罩分离出来 现在我可以继续应用特效 这里的第一步 是将所选特效用于背景 完成后 我会使用 CoreImage 将已遮罩主题 合成到转换后的背景上 前几个特效非常简单 直接应用了 现有的 CoreImage 滤镜 比如 为了凸显主题 我会使用曝光调整滤镜来调暗背景 我还稍用
了一些焦外成像效果 除了模糊背景外 我还希望有一个光晕 凸显我们所选的主题 只需裁出主题白底图 再进行模糊处理即可实现 快速完成此操作的方法 是重用我们当前的函数 并为主题传递一个纯白的图像参数 这样 我就有了合成的基底层 最后 我将放入之前的 CoreImage 合成片段 这样即可将合成的 提取主题放在新背景之上 有了最后一步特效 App 现在就已完成了 希望这能让你初步了解使用新的主题 提取 API 可能实现的一些功能 总之 VisionKit 是将主题提取功能 集成到你的 App 中的最快方法 对于更高级的 App 你可移步查看 Vision 的 API 最后 CoreImage 是使用主题提取 执行高动态范围支持 图像处理的完美工具 Lizzy 和我希望你喜欢本视频 我们非常期待看到你的作品 ♪ ♪

Comments