16-《ARKit by Tutorials》读书笔记3:交互操作

栏目: IOS · 发布时间: 7年前

内容简介:本文是Ray Wenderlich上《ARKit by Tutorials》的读书笔记,主要讲内容概要和读后感ARKit by Tutorials中讲到了图像识别触发AR场景交互的一种特殊方法:利用

ARKit系列文章目录

本文是Ray Wenderlich上《ARKit by Tutorials》的读书笔记,主要讲内容概要和读后感

ARKit by Tutorials中讲到了图像识别触发AR场景交互的一种特殊方法:利用 Vision Framework 来识别一些物体,然后在上面展示一些图片或动画.

还有利用地理定位和iBeacon触发AR交互的方法.

为什么用Vision

可能你会觉得奇怪:为什么不用ARKit自带的图片检测功能? 只要把参考图片的素材放好,设置好物理尺寸,ARKit就可以检测到图片,在WWDC2018上ARKit 2更是增加了图片追踪功能,效果非常好,识别率高,追踪稳定.

那是因为,ARKit目前自带的图片检测和追踪功能,有几点要求不太好满足:

  • 图片不能过于相似;
  • 图片的色彩直方图分布要均匀(不能是黑白的);
  • 图片不能有大片相同颜色的区域;
  • 图片的物理尺寸必须是已知且准确的.

比如下面的图片就不满足要求,虽然也能检测到,但追踪效果会差很多.

16-《ARKit by Tutorials》读书笔记3:交互操作

更麻烦的是:二维码.

  • 虽然不同内容的二维码图片本身并不相同,但是仍然太相似了,尤其是文本很长的时候;
  • 二维码一般是黑白的;
  • 二维码中有大片相同颜色的区域;
  • 二维码的尺寸往往是不同的;

Vision 框架可以识别的内容就很多,可以识别矩形,二维码等等.我们可以把它们两个结合起来使用,达到神奇的效果.

识别任意矩形

Vision框架的使用本身并不难,在AR项目中,写个 touchesBegan() 方法,在其中写上:

// 1
guard let currentFrame = sceneView.session.currentFrame else {
  return 
}
// 2
DispatchQueue.global(qos: .background).async {
  // 3
  do {
    // 4
    let request = VNDetectRectanglesRequest {(request, error) in
                                 
      // Access the first result in the array,
      // after converting to an array
      // of VNRectangleObservation
      // 5
      guard
        let results = request.results?.compactMap({ $0 as? VNRectangleObservation }),
        // 6                                 
        let result = results.first else {
          print ("[Vision] VNRequest produced no result")
          return
      }
      // 得到识别结果,稍后在这里添加处理代码.
    }


let handler = VNImageRequestHandler(cvPixelBuffer: currentFrame.capturedImage)

try handler.perform([request])

  } catch(let error) {
    print("An error occurred during rectangle detection: \(error)")
  }
}
复制代码

可以看到,在上面第6步之后,已经得到了识别出的矩形的结果,继续通过hitTest方法,根据二维的屏幕坐标上矩形的四个角的位置(二维坐标),找到三维空间里矩形的四个角的位置(三维坐标).

// 1
let coordinates: [matrix_float4x4] = [
  result.topLeft, 
  result.topRight, 
  result.bottomRight, 
  result.bottomLeft
].compactMap {
  // 2
  guard let hitFeature = currentFrame.hitTest($0, types: .featurePoint).first else { return nil }

  // 3
  return hitFeature.worldTransform
}

// 4
guard coordinates.count == 4 else { return }

// 5
DispatchQueue.main.async {
  // 6
  self.removeBillboard()

  let (topLeft, topRight, bottomRight, bottomLeft) = (coordinates[0], coordinates[1], 
      coordinates[2], coordinates[3])

  // 7
  self.createBillboard(topLeft: topLeft, topRight: topRight,
    bottomRight: bottomRight, bottomLeft: bottomLeft)
}
复制代码

利用hitTest方法得到了四个featurePoint,后创建一个三维的平面Billboard.

16-《ARKit by Tutorials》读书笔记3:交互操作

三维平面的位置由锚点决定,锚点位置则由4个点的中心点确定:

let anchor = ARAnchor(transform: plane.center)
sceneView.session.add(anchor: anchor)
复制代码

同时还可以创建四个SCNBox来标识矩形的四个角,效果如下

16-《ARKit by Tutorials》读书笔记3:交互操作

坐标系的处理

但这样创建出的平面有个问题,朝向不正确

16-《ARKit by Tutorials》读书笔记3:交互操作
这是因为,我们是根据4个点来创建的平面,这4个点是在世界坐标下的点,本身只有位置坐标,没有旋转和缩放信息,打印 print(coordinates[0])

结果如下:

simd_float4x4([
  [1.0, 0.0, 0.0, 0.0)], 
  [0.0, 1.0, 0.0, 0.0)], 
  [0.0, 0.0, 1.0, 0.0)],
  [-0.0293431, -0.238044, -0.290515, 1.0)]
])
复制代码

用这样的4个点去创建平面,过程如下:

func addBillboardNode() -> SCNNode? {
  guard let billboard = billboard else { return nil }
  
  // 1 宽和高是从4个点的位置计算出来的
  let rectangle = SCNPlane(width: billboard.plane.width,
    height: billboard.plane.height)
  
  // 2 无法得到transform信息,不能正确显示方向,而SCNPlane的默认方向是在x-y平面上,也就是垂直于地面(沿y轴方向),与手机的初始化方向平行(x-y平面方向平行)
  let rectangleNode = SCNNode(geometry: rectangle)
  self.billboard?.billboardNode = rectangleNode

  return rectangleNode
}
复制代码

这里就能看出问题:创建平面只利用了4个点的宽高信息,朝向信息没有设置使用了默认方向.

书中给出了一种处理方式:更改ARKit配置项 ARConfiguration 中的 worldAlignment 属性.这个属性有三个值:

  • gravity :坐标系的y轴是与重力方向平行的,坐标原点及x-z轴则是设备初始化时的位置和朝向.也就是默认设置项
    16-《ARKit by Tutorials》读书笔记3:交互操作
  • gravityAndHeading :y轴与重力方向平行,而x轴指向东,z轴指向南.和现实世界保持一致.
    16-《ARKit by Tutorials》读书笔记3:交互操作
  • camera :坐标系始终跟随摄像机(也就是手机)的位置和朝向,伴随移动.
    16-《ARKit by Tutorials》读书笔记3:交互操作

如果我们采用第三种配置,那么创建出的平面是平行于x-y平面的,即平行于手机屏幕的.但是由于正常情况下,识别过程中手机是正对着要识别对象的,所以得到的结果就是几乎是正确的.

configuration.worldAlignment = .camera
复制代码
16-《ARKit by Tutorials》读书笔记3:交互操作

个人认为:这种做法很扯蛋,根本没有解决问题,只是当用户垂直于矩形进行识别时,效果较好(远远算不上完美)而已.

我认为可以这样解决,欢迎大家讨论:

  1. 利用4个点,计算所在平面的法线A,法线A的方向就取指向摄像机(手机)方向为正.考虑到4个点可能不共面(毕竟立体几何中3点确定一个平面),可以用排列组合的方式轮流取3个点求法向量,总共求出4个法向量再求平均值做为法线A;(求平面的法向量可以用两条边向量的叉乘)
  2. 将创建出的平面的法线B对准刚才计算出的法线A.如何对准呢?变换矩阵是什么? 这就是一个数学问题:已知初始法线B,和目标法线A,求变换矩阵;可以借助四元数进行求解,或者直接设置四元数来处理旋转;
  3. 将创建出的平面SCNNode的矩阵属性设置为求出的变换矩阵就可以了;
// 可以先求出从法线B到法线A的四元数
extension simd_quatf {
    /// A quaternion whose action rotates the vector `from` onto the vector `to`.
    public init(from: float3, to: float3)
}
// 从四元数中得到变换矩阵或直接使用四元数
extension simd_float4x4 {
    /// Construct a 4x4 matrix from `quaternion`.
    public init(_ quaternion: simd_quatf)
}
复制代码

Vision能实现的其它功能

除了矩形之外,Vision还能识别出其它物体:

  • Horizon : VNDetectHorizonRequest 类可以得到画面的水平角度.
  • Faces : VNDetectFaceRectanglesRequest 类可以实现人脸识别;
  • Text : VNDetectTextRectanglesRequest 类可以识别文本和区域;
  • Rectangleobject 追踪: VNTrackRectangleRequestVNTrackObjectRequest 类可以追踪识别出的物体.

例如,上面的例子想改成识别二维码,并在二维码上显示图片或视频,只需要更改Vision部分的代码就行了:

let request = VNDetectBarcodesRequest { (request, error) in
  // Access the first result in the array,
  // after converting to an array
  // of VNBarcodeObservation
  guard let results = request.results?.compactMap({
    $0 as? VNBarcodeObservation }),
    let result = results.first else {
      print ("[Vision] VNRequest produced no result")
      return
    }

  ...
}
复制代码

效果如下:

16-《ARKit by Tutorials》读书笔记3:交互操作

后续还可以在识别出二维码的内容后,在上面展示图片,打开网页或播放视频等

16-《ARKit by Tutorials》读书笔记3:交互操作
16-《ARKit by Tutorials》读书笔记3:交互操作

地理定位相关

除了Vision识别来触发场景外,还讲到了利用地理定位和iBeacon来触发AR场景,其实核心代码非常简单,如果你做过地图开发或iBeacon开发的话,就知道其实就是下面几个代理方法:

// MARK: - LocationManagerDelegate
extension AdViewController: LocationManagerDelegate {
  // MARK: Location
  func locationManager(_ locationManager: LocationManager, 
    didEnterRegionId regionId: String) {
  }

  func locationManager(_ locationManager: LocationManager, 
    didExitRegionId regionId: String) {
  }

  // MARK: Beacons
  func locationManager(_ locationManager: LocationManager, 
    didRangeBeacon beacon: CLBeacon) {
  }

  func locationManager(_ locationManager: LocationManager, 
    didLeaveBeacon beacon: CLBeacon) {
  }
}
复制代码

具体业务逻辑没有什么太大的难点,不再赘述了.

需要注意的是,提到了地理定位的测试方法:

  • .gpx 文件来做虚拟定位测试;
  • 用lightblue等蓝牙软件来模拟iBeacon定位;

第三部分读书笔记结束!


以上所述就是小编给大家介绍的《16-《ARKit by Tutorials》读书笔记3:交互操作》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

复杂网络理论及其应用

复杂网络理论及其应用

汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元

国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器