29-WWDC 2018:理解 ARKit 的追踪和检测

583 阅读13分钟

ARKit系列文章目录 2018年的 Session 610 - Understanding ARKit Tracking and Detection 内容如下(原发表于《WWDC18 内参》): 主要内容速览:

  • ARKit基础
  • 朝向追踪
  • 世界追踪
  • 平面检测
  • 保存和加载世界地图
  • 图片追踪
  • 物体检测

本session主要讲的是原有的三种追踪和检测方式:朝向追踪,世界追踪,平面检测

还有本次添加的三种新的追踪和检测方式:保存和加载地图,图片追踪,物体检测.

ARKit基础

ARKit运行的主要结构如下:

首先是创建一个ARSession,它综合处理AR中的一切,包括加载配置,运行AR技术,返回结果等.

接着是指定一个ARConfiguration,说明你到底要运行哪种AR技术,是图片追踪,还是平面检测.并通过调用ARSession实例的run(_ configuration)来运行.

然后ARSession会在内部自动开始接收AVCaptureSession传来的图像信息和CMMotionManager传来的传感器数据.

经过ARKit处理后,会得到一秒60帧的ARFrame.它不仅包含了从摄像头得到的图像也就是AR的场景背景图像,还包括了摄像机的运动情况可用于渲染虚拟场景中的摄像机,此外还有外部环境信息,比如已探测到的平面.

朝向追踪

只追踪朝向(3 DoF):可用于模拟头部的旋转

一般用于球形虚拟场景,或者远处物体的渲染.

但是不能用来在不同位置观察对现实世界的增强结果(即不能移动到不同的位置).

幕后的秘密

技术很简单,只用到了旋转信息,因为运动传感器的更新频率很快,所以当相机传感器的图像信息可用时,取最新的运动状态,将两者在ARFrame中返回出去.

需要注意:在这种模式下,相机获取的数据并没有经过任何计算机视觉系统处理,也就是说没有特征点之类的视觉信息,只是单纯的图像+传感器姿态信息.

朝向追踪API

API也很简单,transform中只包含了旋转信息,或者也可以使用欧拉角,作用是一样的.

AROrientationTrackingConfiguration
open class ARCamera : NSObject, NSCopying {
    open var transform: simd_float4x4 { get }
    open var eulerAngles: simd_float3 { get } ...
}

世界追踪

世界追踪是6 DoF追踪,可以在场景中旋转和移动.

幕后的秘密

ARKit使用的是视觉惯性里程计(VIO),它结合了惯性里程计和视觉里程计的优点:
惯性里程计:利用运动传感器高频采样速度快精度高,可准确捕捉快速和剧烈的运动,但长期会有累积误差;
视觉里程计:利用摄像头图像+计算机视觉(CV)处理,无时间累积误差,但处理速度慢,无法在快速和剧烈运动时使用;

视觉惯性里程计(VIO)综合了它们的优点,不仅精度高,无长期累积误差,甚至在短时间无视频画面时仍能保持追踪.

整体过程是:提取场景中的鲁棒性特征(即明显的特征点)-->匹配特征点-->三角定位创建世界地图-->AR初始化-->通过ARAnchor放置增强现实物体-->继续追踪更多特征点-->优化追踪的健壮性(画面上的虚拟物体可能会轻微调整位置)
在三角定位中,必须有真实的移动,纯粹的旋转无法得到三角定位信息.

在最后一步中,完成了视觉惯性SLAM(即时定位与地图构建)操作,提高了对整个场景的认识.

6869ac4c9a030f2b1a606c7426534763.gif

最后一步,完成地图构建后,还会利用已经构建的地图和特征点来优化相机定位的准确度,因此会有轻微的跳动出现.

世界追踪 API

ARWorldTrackingConfiguration
open class ARCamera : NSObject, NSCopying {
   open var transform: simd_float4x4 { get }
   
   //以下两个提供了追踪质量的反馈
   open var trackingState: ARTrackingState { get }
   open var trackingStateReason: ARTrackingStateReason { get }
   ...
}

世界追踪的质量

对追踪质量产生影响的有以下因素:

不间断的传感器数据:ARKit可以容忍短时间的数据中断,但长时间中断会让session变成受限状态(Limited).
富有纹理的环境:环境需要富有纹理细节,并有充分光照.因为三角定位需要明显的特征点和复杂的纹理细节.环境太暗,或面对一面白色墙壁会让追踪质量变差.
静止的场景:世界追踪需要场景是静止的,如果在移动的电梯中或车辆中使用,运动传感器得出的数据和摄像头画面分析得出的结果会不一致.

ARKit使用了机器学习技术来增强对图像和传感器数据的分析.根据不同条件使用不同的方案来追踪.如下,在摄像头被遮挡时,追踪的"健康度"会下降,画面恢复后,"健康度"上升.

664010a136b0c0ff7c1fc36ce38b0094.gif

ARKit会把追踪状态规范成四种不同的状态:没启动时是Not Available,启动中是Limited,启动成功Normal,被遮挡是Limited,恢复后还是Normal:

状态改变会在下面的回调方法中通知:

 // Called when tracking state changed
func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
    if case .limited(let reason) = camera.trackingState {
        // Guide user to improve tracking state
        ...
    }
}

平面检测

平面检测用到了前面创建的世界地图来检测存在的水平和竖直平面,需要注意的是可能会有多个平面,而且平面会合并;
ARKit 2引用了一个新特性:竖直平面和水平平面的交界处可以互相截断,更加真实.

幕后的秘密

平面检测的原理就是根据得到的3D特征点,构成平面,并不断更新扩展平面/合并平面;
需要注意的是:平面检测无法立即得到,因此需要开发者引导用户适当移动,检测更多的3D特征点以便构成平面.只在原地旋转同样是不行的.

平面检测API

平面检测用的配置和世界追踪一样,只是多个属性设置.

// Create a world tracking configuration
let configuration = ARWorldTrackingConfiguration()
// Enable plane detection
configuration.planeDetection = [.horizontal, .vertical]
// Run the session
session.run(configuration)

平面检测得到的结果是ARPlaneAnchor,并会在代理中持续得到它的添加/移除/更新信息

open class ARPlaneAnchor : ARAnchor {
  // 位置和方向
  open var transform: simd_float4x4 { get }
  // 中心点位置和边界盒尺寸
  open var center: simd_float3 { get }
  open var extent: simd_float3 { get }
  // 平面几何体形状
  open var geometry: ARPlaneGeometry { get }
    ...
}

边界盒尺寸和几何体形状到底是什么见下图:

得到平面后,你就可以放置虚拟物体或者进行命中测试(hitTest)了.

保存和加载世界地图

这个功能可以完成:

  • 获取高质量的世界地图
  • 共享这个世界地图
  • 重定位到世界地图中

世界地图数据

世界地图中包含的数据有:

  • 内部追踪数据
    • 3D特征点地图
    • 本地外观
  • 锚点名称列表
  • 可序列化

获取一个高质量的世界地图

流程如下图

注意事项:

  • 地图上需要有稠密的特征点
  • 环境必须是静态的,无移动,无变化
  • 有多个视点(即从多个不同位置获取场景)
  • 世界地图构建状态:Not Available,Limited,Extending,Mapped

在地图构建的过程中,开发者需要及时提示用户:

  • 显示地图构建状态
  • 提醒追踪状态受限
  • 引导重定位

共享世界地图

基本过程是这样:

// Retrieve world map from ARSession
session.getCurrentWorldMap { worldMap, error in
    guard let worldMap = worldMap else {
        showAlert(error)
    return
}
    // Serialize
    let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
}

如果需要共享给他人----使用MultipeerConnectivity framework
具体用法见示例程序“Creating a Multiuser AR Experience”<见本文末尾>

重定位到世界中

// Receive / Load world map
let worldMap : ARWorldMap = ...

// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.initialWorldMap = worldMap

// Run the session
session.run(configuration)

在重定位完成前:

  • 追踪状态是Limited
  • 原因为Relocalizing
  • 世界坐标原点将会是你session启动后第一个摄像机位置

在重定位完成后:

  • 追踪状态为Normal
  • 世界坐标原点是世界地图中的初始化原点
  • 只允许环境出现极少量变化(变化太大就无法识别了)

也就是说,当你启动AR后,如果没有对着指定目标,原点就是AR启动时的手机位置,指向目标完成地图重定位后,原点会发生改变,需要特别注意.

图片追踪

图片追踪可识别并追踪场景中出现的图片,可同时追踪多个图片

幕后的秘密

先是本地化图片(Localize the image)
参考图片和场景中当前画面会被转为灰度图像,然后提取双方的特征点信息,然后对特征进行匹配,得出场景中的图片位置和朝向;

接着是稠密追踪(Dense tracking)
从当前画面中截取出重建过的图片(下图右上角),通过与参考图片(下图左上)对比,可以得到一个错误画面(下图中下方),优化这个错误图像,让错误值最小化,就能提高追踪精度.

这样,在场景中的图片被部分遮挡时,也能持续追踪.如下图片被手指和左侧边缘遮挡,左侧是无效部分不会显示在错误画面中,只需要将手指遮挡引起的不匹配优化到最小,就能得到最精确结果.

图片追踪API

首先创建参考图像,然后创建配置,可选世界追踪或图片追踪,最后从ARSession中得到ARFrame,再得到ARImageAnchor.

将图片添加为素材(Assets)

创建参考图片最简单的方式就是将图片拖拽到Xcode中添加为素材.

  • 创建AR Resource Group
  • 拖拽需要检测的图片
  • 设置图片的物理尺寸(单位:米)

设置图片的物理尺寸

图片的物理尺寸是必须设置项,主要作用是和世界追踪得到的数据保持一致.

适合追踪的图片

适合追踪的图片必须有以下特点:

  • 丰富的纹理
  • 高对比度
  • 良好的色彩直方图分布
  • 无重复结构(如左半边和右半边完全一样,或两个相同物体)

Xcode中的参考图片质量

所有这些图片质量信息都可以从Xcode中获得.同时,如果两幅图片十分相似也会给出提示.

Xcode现在可以创建多个AR资源组了.这样博物馆之类的AR应用可以在不同房间使用不同图片组.

  • 可以检测更多图片
  • 每组推荐,最多25张图片
  • 可以用代码切换分组

图片追踪配置项

ARImageTrackingConfigurationARWorldTrackingConfiguration这两个配置项在图片追踪上有什么不同呢?

也就是说,图片追踪配置项只有在对准图片时才有用,只给出图片的相对位置;

而世界追踪会建立世界坐标系,所以可以扩展到图片以外的3D世界中.
比如下图中识别到图片添加气球,就算摄像头离开图片,气球也可以继续在世界坐标中正常运动.

同时,因为ARImageTrackingConfiguration没有使用到运动传感器,所以这个配置项可以在车辆和电梯上使用.

代码使用:

// Load Images from Assets
let imageSet = ARReferenceImage.referenceImages(inGroupNamed: "Room1", bundle: Bundle.main)
    guard let imageSet = imageSet else {
    print("Error loading images")
    return
}
// Create a session configuration
let configuration = ARImageTrackingConfiguration()
configuration.trackingImages = imageSet
 // Run the session
 let session = ARSession() session.run(configuration)

获取结果

追踪结果在代理方法中获取

func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
    for anchor in anchors {
        if let imageAnchor = anchor as? ARImageAnchor {...}
    }
}
open class ARImageAnchor : ARAnchor, ARTrackable {
  open var transform: simd_float4x4 { get }
  open var referenceImage: ARReferenceImage { get }
  public var isTracked: Bool { get }
}

可用于共享的绝对坐标空间

借助图片追踪功能,我们可以根据图片位置来建立一个多人共享的坐标系.甚至可以在现实世界中,借助某个图片,实现AR导航功能.

物体检测

图片检测和图片追踪,只能用于平面物体,而物体检测可用于3D检测. 比如识别一个小玩具或Nefertiti奈费尔提蒂(公元前14世纪埃及王后)的雕像.

不过在检测之前,需要先对物体进行扫瞄.

如何获得一个物体?

扫瞄物体类似于建立世界地图.可使用“Scanning and Detecting 3D Objects”示例程序<下载链接见文章末尾>.物体扫瞄的质量会影响物体检测的质量.

打开“Scanning and Detecting 3D Objects”示例程序,点击开始扫瞄物体,会自动生成并不断调整边界盒子.你也可以手动拖动来调整边界盒子的大小,或者用双指旋转盒子以避免个别部位突出边界.

接着是对各个面的扫瞄,希望从哪个面进行识别,就对哪个面进行扫瞄,可以离近点以获得更好的扫瞄效果.

最后可以拖动调整物体坐标原点.

为了检测扫瞄效果,可以将app切换到"检测"模式,试试能不能检测到刚才扫瞄的物体.

试着将摄像头对准别处,再移动回来,看不能顺利检测到物体;
试着将物体移动到其他地方,比如灯光不同的地方,测试能否重新检测到物体;
如果不能顺利识别,应调整重新进行扫瞄.

物体扫瞄

如果对效果满意,就可以分享到Xcode中,以供检测识别.

适合追踪的物体

适合用于追踪的物体应有以下特点:

  • 刚性物体
  • 纹理细节丰富
  • 无表面反射
  • 无透明效果

物体追踪API

API类似于图片追踪

// Load Objects from Assets
let objects = ARReferenceObjects.referenceObjects(inGroupNamed: "Object", bundle: Bundle.main)
guard let objects = objects else {
    print("Error loading objects")
    return
}
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.detectionObjects = objects
 // Run the session
 let session = ARSession() session.run(configuration)

获取结果

检测结果也是在代理方法中获取

func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
    for anchor in anchors {
        if let objectAnchor = anchor as? ARObjectAnchor {
            let objectName = objectAnchor.referenceObject.name ?? "" 
            print("Object found: \(objectName)")
        } 
    } 
} 

物体追踪vs.世界地图重定位

你可能会觉得物体追踪世界地图重定位有点类似,但其实它们还是有一些不同的.

物体追踪中最重要的是给出识别到的物体,在世界坐标系中的位置;而在世界地图重定位中则是给出摄像机在世界地图中的位置;

另外,物体追踪中可以识别多个物体.最适用于识别追踪放在桌面上或家具上的物体,因为下面的物体可提供有效支持有助识别.


相关资源:

WWDC2018相关视频:

WWDC2017相关视频: