大多数浏览器和
Developer App 均支持流媒体播放。
-
认识 iOS 上的 Object Capture
探索如何直接在你的 iOS App 中提供端到端的 Object Capture 体验,以帮助人们将他们的对象变成随时可用的 3D 模型。了解如何使用我们的示例 App 创建完全自动化的 Object Capture 扫描流程,以及如何帮助人们为他们的模型自动捕获最佳内容。我们还将讨论激光雷达数据并提供扫描对象的最佳实践。
资源
相关视频
WWDC23
-
下载
♪ 悦耳的器乐嘻哈 ♪ ♪ Lei Zhou:嗨! 我是 Object Capture 团队的 Lei。 在今天的讲座中,我将和同事 Mona 一起 向你介绍适用于 iOS 的 Object Capture 功能。 在开始之前,首先来 回顾一下什么是 Object Capture, 以及它的运作原理。 Object Capture 采用尖端的 计算机视觉技术, 以不同角度拍摄的系列图片为基础, 创建栩栩如生的 3D 模型。 这些图片会被传送到 Mac, 在那里,Object Capture API 可以在几分钟之内 完成 3D 模型的重建。 在发布面向 Mac 的 API 之后, 我们看到了许多 采用 Object Capture 功能 制作高质量 3D 模型的 App。 也收到了大量开发者的反馈。 现在,我们向前更进一步, 为 iOS 带来完整的 Object Capture 体验。 这意味着现在我们可以通过 用户友好的界面, 在你的手掌心进行画面捕捉, 并在设备端完成模型重建。 我们也提供了一款 示例 App 来展示 iOS 上的 工作流。 让我们来看看示例 App 的操作。 我们将使用示例 App 来轻松地创建 这只精美花瓶的 3D 模型。 首先,打开示例 App 并将镜头对准物体。 你将在开始画面捕捉前 看到一个自动生成的边界框。 然后,绕着物体走一圈, Object Capture 将 自动为你拍摄合适的图像。 我们会在需要更多图像的区域 提供可视化指引, 并提供额外的反馈信息, 帮助你拍摄出最高质量的图像。 转完一圈之后, 就可以把物体翻过来,拍摄它的底部。 在三个部分的扫描均已完成后, 我们就可以进入重建阶段, 它将在你的 iOS 设备上本地运行。 只需几分钟时间 就可以生成 USDZ 格式的模型。 我们在开发者文件中 提供了这个 App 的源代码, 以便你能立即下载并试用。 你也可以将它当作 创建你自己 App 的基础。 我们已经在全新示例 App 中 看完了 Object Capture 的演示, 再来看看今年有哪些 令人兴奋的新功能吧。 我们首先介绍 Object Capture 的升级, 即使用激光雷达支持扫描更多物体。 接下来,我们将演示引导捕捉功能, 它能简化针对你物体的数据捕捉。 接下来,我们将介绍 如何在 iOS 的 Object Capture API 中 创建 Object Capture 工作流。 最后,我们将着重说明几项 模型重建能力方面的强化升级。 我们首先来看看 如何使用激光雷达支持多物体扫描。 要创建高质量的 3D 模型, 选择特征良好的物体 非常重要。 我们的 Object Capture 系统在面对 拥有足够纹理细节的物体时表现最佳, 但今年,我们进行了进一步的升级。 现在我们支持使用激光雷达扫描器 对纹理不足的物体进行重建。 我们以这张椅子为例。 它缺乏纹理细节, 因此 Object Capture 系统很难 创建令人满意的模型。 但如果使用激光雷达,我们就能重建出 质量更好的模型。 在捕捉过程中, 我们拍摄椅子的 RGB 图像。 然而,由于座部和椅背缺乏纹理, 我们无法重建完整的模型。 除了 RGB 图像, 我们的 API 也使用 激光雷达收集点云数据, 通过强化覆盖率和密度, 使物体的 3D 形态得到全面呈现。 最后,通过融合点云数据 生成完整的 3D 模型。 其他的一些低纹理度物品的 建模质量也通过我们的 激光雷达支持系统得到了提升。
尽管现在我们已经 支持低纹理度物体的建模, 但有些物体的建模仍然存在挑战。 最好避免对表面反光、 透明或包含纤细结构的物体进行建模。 了解完系统支持的物体之后, 我们来看看如何使用引导捕捉 轻松地对物体进行扫描。 我们提供的引导扫描功能 可以自动获取图像和激光雷达数据。 它也能在捕捉过程中 提供有用的反馈。 此外,它还为是否应当 翻转物体提供指引。 我们对数据获取体验 进行了自动化处理, 无需你手动选择 良好的视角并按下快门键。 在你围绕物体转圈时, 系统会自动选择高锐度、 高清晰度和高曝光度的图像, 并从不同视角收集激光雷达点数据。 为了捕捉到物体的全部细节, 我们强烈建议你 从尽可能多的角度拍摄图像。 我们提供了一个捕捉刻度盘, 标明哪些区域已获取了足够的图像。 我们建议你从各个角度进行扫描 以填满此刻度盘。 除了自动捕捉, 我们也在捕捉过程中 提供实时辅助性反馈。 首先,请确保拍摄环境有良好的照明, 以便获得准确的颜色表达。 如果环境过暗,你将收到 调整照明条件的提醒。 我们建议使用漫射光照明, 以最大程度上减少 物体表面的反射或高光。 接下来,请缓慢而流畅地绕物体移动, 保持摄像头稳定,防止出现模糊的图像。 如果你移动过快,自动捕捉将停止 并提醒你放慢速度。 下一步,请在摄像头和物体之间 保持适当的距离,使物体 在取景框内的位置恰到好处。 如果距离过远或过近, 屏幕上将出现文字提醒。 最后,请让物体始终位于取景框内。 如果物体离开了取景框, 自动拍摄将会暂停,并出现箭头标识, 提醒你调整视角方向。
从各个方向拍摄物体, 对获得完整的 3D 模型至关重要。 翻转物体可以帮助你做到这一点。 但是否应当翻转物体 取决于它本身的特性。 如果物体较为坚硬, 就适合进行翻转拍摄。 但如果物体容易变形, 最好还是不要进行移动, 因为它们很容易变形。 对于纹理丰富的物体,推荐你进行翻转 但很重要的一点是,你需要避免 翻转有对称或重复纹理的物体, 这样做可能对系统产生误导。 此外,翻转无纹理物体 在 Object Capture 系统中难以实现, 因为我们需要足够的纹理 来缝合不同区块。 我们也提供 API 来帮助你确认 物体是否有足够的纹理支持翻转拍摄。 在对物体进行翻转时, 推荐你以三种朝向进行扫描, 以便获取所有角度的图像。 最好使用漫射光进行照明, 以最大程度减少物体表面的 阴影和反光。 在多轮扫描中使图像重叠也很重要。 此轮扫描中捕捉的 部分物体也应当出现在 此前的扫描中。 我们也提供了可视化指引 帮助你恰当地翻转物体。 如果无法翻转物体, 我们建议你从三种不同高度进行拍摄, 以获取不同视角的图像。 拍摄大多数无纹理物体时,建议你使用 有纹理的背景,使物体显得突出。 Object Capture 可以在 iPhone 12 Pro、iPad Pro 2021 以及型号更新的设备上使用。 要确认设备是否支持 Object Capture, 你可以使用我们 提供的 API 进行轻松核查。 接下来,Mona 将更深入地向你介绍 Object Capture API。 Mona Yousofshahi:谢谢你,Lei! 了解示例 App 的操作之后, 现在来看看如何 使用 Object Capture API 创建此 App。 正如你在演示中所看到的, Object Capture 分为两步: 图像捕捉和模型重建。 我们首先来看图像捕捉。 Image Capture API 分为两部分: 一个会话和一个 SwiftUI 视图。 会话让你可以在图像捕捉过程中 观察和控制状态机工作流。 SwiftUI 视图展示拍摄素材, 并基于会话的状态,自动调整 它所呈现的 UI 元素。 我们的 SwiftUI 不带任何 2D 文本或按键。 这让你可以自定义 App 的外观 并更轻松将 Object Capture 整合到你现有的 App 中。 让我们仔细看一看 Object Capture 状态机。 会话创建时处于 初始化状态。 接下来你可以 执行函数调用让状态递进。 会话将经历就绪状态、识别状态、 捕捉状态和结束状态。 到达结束状态后, 会话将自动进入完成状态。 这时你就可以放心进行拆解 并继续进行设备端模型重建。 我们来看一下实际运用。 第一步是导入 RealityKit 和 SwiftUI。 然后创建 ObjectCaptureSession 实例。 由于它是一种引用类型, 所以我们推荐将会话 以持久状态存储在 基于真实数据模型中, 直至会话完成。 准备完成后,会话从初始化状态开始。 我们接着调用 start() 函数 设置目录以告知会话 捕捉到的图像存储在何处。 我们也可以在配置中提供检查点目录, 之后用于对重建过程进行加速。 这次调用之后,会话将进入就绪状态。 我们来看看如何配合使用 会话和 ObjectCaptureView。 ObjectCaptureView 和 任何其他的 SwiftUI 视图的用法一样。 我们将它放入另一个视图主体中, 将刚才创建的基准真相会话传递给它。 ObjectCaptureView 始终展示 与当前会话状态匹配的 UI。 正如你在就绪状态中看到的, 视图展示了带有标线框的摄像头画面, 引导你选择需要捕捉的物体。 要让状态更进一步, 你的 App 需要提供一个 UI 以指示会话开始识别物体。 现在我们在 ObjectCaptureView 顶部 堆叠一个 Continue 按键。 点击按键之后,它将 调用 startDetecting() 函数 进入包围盒识别状态。 在识别状态中,视图将自动更改 以展示当前识别到物体外部的包围盒。 如有必要,你可以手动调整包围盒的 尺寸和朝向。 我们的示例 App 也提供重置按钮, 允许在想要选择其他物体时 重新开始物体选择流程。 这将让会话回到就绪状态。 与就绪状态到识别状态的转变类似, 你也需要提供按钮来指示会话 在你对物体选择满意后开始拍摄。 我们使用 Start Capture 按键 调用 startCapturing() 函数。 调用后,会话进入拍摄状态。 在拍摄状态中,会话将自动 在慢速围绕物体 转动的过程中拍摄图像。 视图中显示了点云和拍摄刻度盘, 以标示出在哪些部分 已经收集了足够的物体图像, 以及哪些部分还需要拍摄更多。 拍摄刻度盘完全填满时, 一轮扫描就完成了。 在拍摄刻度盘填满后,会话将 userCompletedScanPass 属性设置为真。 此时,我们的示例 App 为你提供两种选择,可以结束会话 或者继续拍摄更多图片。 我们为每种选项分配一个按键。 要获得最佳的模型重建效果, 我们推荐进行三轮扫描。 现在来看看如何进入下一轮扫描。 你有两种方法可以开始新一轮扫描, 如何选择取决于你是否翻转物体。 翻转让你可以拍摄到 当前轮次中不可见的部分。 比如物体的底部。 要实现这一点, 我们调用 beginNewScanPassAfterFlip() 回到就绪状态。 你接下来必须在新的朝向中 执行盒框选择。 如果你决定不翻转物体, 可以选择在不同的高度拍摄更多图像。 为此,我们调用 beginNewScanPass()。 这将重置捕捉刻度盘, 但会话仍处于拍摄状态, 因为包围盒并未更改。 在所有扫描轮次完成后, 示例 App 会提供一个 Finish 按钮。 按键会调用 finish() 函数, 告知会话图像拍摄已完成, 可以开启结束流程了。 来到结束状态后, 会话将等待数据进行存储。 存储完成后,会话将自动移至 完成状态。 我们可以放心进行拆解 并开启设备端重建。 如果遇到不可恢复的错误, 比如出现图像目录突然不可用的情况, 会话将转入失败状态。 这时,就需要创建一个新会话。 最后一点,你也可以展示点云 来预览物体自初始位置 或上一次翻转后 哪些部分已经完成了扫描。 你可以通过把 ObjectCaptureView 替换为 ObjectCapturePointCloudView 实现。 它将暂停捕捉会话并让你 和点云进行互动,进行各角度的预览。 我们将这个视图 和一些文本及按钮组合起来进行呈现, 但你也可以选择全屏展示点云。 我们已经完成了物体的图像拍摄, 现在来看看如何创建它的 3D 模型。 从今年开始,你就可以 在 iOS 上运行此重建 API 了。 这让你能够在同一设备上完成 图像捕捉和重建。 我们快速回顾一下如何使用 这一异步重建 API。 它在 iOS 上运行的方式 与在 macOS 上相同。 首先将 task 修饰符添加到 我们创建的视图上。 在 task 修饰符中, 我们创建一个photogrammetry session 并将它指向图像。 还可以额外提供拍摄图像时使用的 相同检查点目录, 使重建过程速度更快。 然后,我们调用 process() 函数,请求 modelFile。 最后,我们在循环中等待消息流, 并在输出消息到达时进行处理。 请务必查看我们的 前序解说以获取更多信息。 为了优化移动设备上模型的 生成和预览, iOS 上仅支持简化这一细节等级。 重建的模型包含漫反射、环境光遮蔽 和法线的纹理映射, 均为移动设备显示而设计。 如果你想生成其他细节等级的模型, 可以将图像传送到 Mac 进行重建。 今年,Mac 重建同样可以使用 存储在图像中的激光雷达数据。 全新的 Object Capture 会话 同样支持此工作流。 我们来看看如何实现。 默认情况下,Object Capture 会话 将在达到 iOS 设备的 重建限制时,停止对图像的捕捉。 在进行 macOS 重建时, 你可以允许会话拍摄 比设备端重建所用更多的图像。 要实现这一点,你可以在会话的配置中 将 isOverCaptureEnabled 设置为真。 这些额外的图像将不会 在设备端重建中使用, 但都保存在 Images 文件夹中。 在 Mac 上重建图像, 甚至不需要你写任何代码。 Object Capture 已经 整合到了新 macOS App, 也就是 Reality Composer Pro 当中。 你可以简单地将图像导入 App, 选择细节等级并获得模型。 想进一步了解此 App 的更多信息, 请务必观看 Reality Composer Pro 相关讲座。 现在我们已经了解了 如何使用新的 iOS API 创建 3D 模型, 接下来我将简要介绍几项 开发者强烈要求的重建升级。 我们提升了 Mac 上的 模型质量和重建速度。 除了进度百分比, 我们现在也提供预计的重建时间。 让我们详细地看看另外两项新增升级: 位姿输出和自定义细节等级。 你现在可以要求每幅图像的 高质量位姿。 每一项位姿都包含了拍摄该图片时 预计的相机位置和朝向, 均基于计算机视觉算法生成。 要获取这些位姿,你可以在 process() 函数调取中添加位姿要求。 随后在位姿输出到达 输出消息流时进行处理。 在模型生成之前, 位姿将会在重建早期返回。 今年,我们还为 macOS 新增了 自定义细节等级,让你对重建模型 进行全面掌控。 从前,你可以选择简化、中等、完整 和原始四个等级。 有了自定义细节等级后, 你可以控制网格简化量、 纹理映射分辨率、格式 以及包含的纹理映射种类。 关于 iOS 上的 Object Capture 介绍就到这里。 现在,Object Capture 让你 能够在激光雷达的支持下 扫描更多物体。 我们向你展示了如何 完全地在你的 iOS 设备上 完整捕捉、重建以及预览你的模型。 iOS 上的 Object Capture 让各种各样的 App 例如电商、设计、教育和游戏 都实现了新的工作流。 我们期待看到你将 Object Capture 纳入到你的 App 中。 感谢你的观看! ♪
-
-
10:03 - Instantiating ObjectCaptureSession
import RealityKit import SwiftUI var session = ObjectCaptureSession()
-
10:25 - Starting the session
var configuration = ObjectCaptureSession.Configuration() configuration.checkpointDirectory = getDocumentsDir().appendingPathComponent("Snapshots/") session.start(imagesDirectory: getDocumentsDir().appendingPathComponent("Images/"), configuration: configuration)
-
10:50 - Creating ObjectCaptureView
import RealityKit import SwiftUI struct CapturePrimaryView: View { var body: some View { ZStack { ObjectCaptureView(session: session) } } }
-
11:20 - Transition to detecting state
var body: some View { ZStack { ObjectCaptureView(session: session) if case .ready = session.state { CreateButton(label: "Continue") { session.startDetecting() } } } }
-
11:36 - Showing ObjectCaptureView
var body: some View { ZStack { ObjectCaptureView(session: session) } }
-
12:04 - Transition to capturing state
var body: some View { ZStack { ObjectCaptureView(session: session) if case .ready = session.state { CreateButton(label: "Continue") { session.startDetecting() } } else if case .detecting = session.state { CreateButton(label: "Start Capture") { session.startCapturing() } } } }
-
12:27 - Showing ObjectCaptureView
var body: some View { ZStack { ObjectCaptureView(session: session) } }
-
12:50 - Completed scan pass
var body: some View { if session.userCompletedScanPass { VStack { } } else { ZStack { ObjectCaptureView(session: session) } } }
-
14:03 - Transition to finishing state
var body: some View { if session.userCompletedScanPass { VStack { CreateButton(label: "Finish") { session.finish() } } } else { ZStack { ObjectCaptureView(session: session) } } }
-
15:00 - Point cloud view
var body: some View { if session.userCompletedScanPass { VStack { ObjectCapturePointCloudView(session: session) CreateButton(label: "Finish") { session.finish() } } } else { ZStack { ObjectCaptureView(session: session) } } }
-
15:50 - Reconstruction API
var body: some View { ReconstructionProgressView() .task { var configuration = PhotogrammetrySession.Configuration() configuration.checkpointDirectory = getDocumentsDir() .appendingPathComponent("Snapshots/") let session = try PhotogrammetrySession( input: getDocumentsDir().appendingPathComponent("Images/"), configuration: configuration) try session.process(requests: [ .modelFile(url: getDocumentsDir().appendingPathComponent("model.usdz")) ]) for try await output in session.outputs { switch output { case .processingComplete: handleComplete() // Handle other Output messages here. }}}}
-
17:02 - Capturing for Mac
// Capturing for Mac var configuration = ObjectCaptureSession.Configuration() configuration.isOverCaptureEnabled = true session.start(imagesDirectory: getDocumentsDir().appendingPathComponent("Images/"), configuration: configuration)
-
18:40 - Pose output
// Pose output try session.process(requests: [ .poses .modelFile(url: modelURL), ]) for try await output in session.outputs { switch output { case .poses(let poses): handlePoses(poses) case .processingComplete: handleComplete() } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。