21-苹果官方3D物体扫瞄及检测Demo解读

说明

ARKit文章目录

Scanning and Detecting 3D Objects的使用及注意点

ARKit的物体检测功能可用于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.世界地图重定位

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

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

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

代码解析

下面我们来看看整个项目的代码,先从代码整体逻辑开始.

整体逻辑

主要的类,及其主要作用如下

各个类之间的逻辑如下

可以看到以ViewController类为流程控制中枢,以State来控制整个流程,利用其Set方法来自动处理相应刷新.

enum State {
    case startARSession
    case notReady
    case scanning
    case testing
}

我们可以看到最主要的流程是Scan流程,由Scan类来管理.在这个流程:

  1. 我们要根据ARSession的回调,来不断更新被扫瞄物体,被扫瞄的点云,还有其坐标轴和边界盒;
  2. 要根据手势的拖动,来不断更新被扫瞄物体,被扫瞄的点云,还有其坐标轴和边界盒;
    Scan类中,也是依靠一个State来控制扫瞄流程中的各个步骤的,同样也利用Set方法来自动处理相应刷新.
enum State {
   case ready
   case defineBoundingBox
   case scanning
   case adjustingOrigin
}

其次还有TestRun流程,但相应要简单一些,没有了复杂的3D UI处理,也没有再使用State来控制流程.

几个有意思的方法

BoundingBox类中的fitOverPointCloud方法

告诉我们应该如何过滤识别出的特征点,太远的点不要,太稀疏的点不要.最后转换坐标系并更新边界盒的大小:
```swift
func fitOverPointCloud(_ pointCloud: ARPointCloud, focusPoint: float3?) {
var filteredPoints: [vector_float3] = []

for point in pointCloud.points {
    if let focus = focusPoint {
        // Skip this point if it is more than maxDistanceToFocusPoint meters away from the focus point.
        // 如果该点距离焦点大于maxDistanceToFocusPoint,忽略该点.
        let distanceToFocusPoint = length(point - focus)
        if distanceToFocusPoint > maxDistanceToFocusPoint {
            continue
        }
    }

    // Skip this point if it is an outlier (not at least 3 other points closer than 3 cm)
    // 如果是异常点,跳过该点(某个点周围3cm内至少要有3个点的其他点,否则不要该点)
    var nearbyPoints = 0
    for otherPoint in pointCloud.points {
        if distance(point, otherPoint) < 0.03 {
            nearbyPoints += 1
            if nearbyPoints >= 3 {
                filteredPoints.append(point)
                break
            }
        }
top Created with Sketch.