ARKit系列文章目录swift
本文是Ray Wenderlich上《ARKit by Tutorials》的读书笔记,主要讲内容概要和读后感 session
ARKit by Tutorials中讲到了图像识别触发AR场景交互的一种特殊方法:利用Vision Framework来识别一些物体,而后在上面展现一些图片或动画.框架
还有利用地理定位和iBeacon触发AR交互的方法.async
可能你会以为奇怪:为何不用ARKit自带的图片检测功能? 只要把参考图片的素材放好,设置好物理尺寸,ARKit就能够检测到图片,在WWDC2018上ARKit 2更是增长了图片追踪功能,效果很是好,识别率高,追踪稳定.post
那是由于,ARKit目前自带的图片检测和追踪功能,有几点要求不太好知足:测试
好比下面的图片就不知足要求,虽然也能检测到,但追踪效果会差不少. 动画
更麻烦的是:二维码.ui
而Vision框架能够识别的内容就不少,能够识别矩形,二维码等等.咱们能够把它们两个结合起来使用,达到神奇的效果.spa
Vision框架的使用自己并不难,在AR项目中,写个touchesBegan()
方法,在其中写上:3d
// 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.
let anchor = ARAnchor(transform: plane.center)
sceneView.session.add(anchor: anchor)
复制代码
同时还能够建立四个SCNBox来标识矩形的四个角,效果以下
但这样建立出的平面有个问题,朝向不正确
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
属性.这个属性有三个值:
若是咱们采用第三种配置,那么建立出的平面是平行于x-y平面的,即平行于手机屏幕的.可是因为正常状况下,识别过程当中手机是正对着要识别对象的,因此获得的结果就是几乎是正确的.
configuration.worldAlignment = .camera
复制代码
我的认为:这种作法很扯蛋,根本没有解决问题,只是当用户垂直于矩形进行识别时,效果较好(远远算不上完美)而已.
我认为能够这样解决,欢迎你们讨论:
// 能够先求出从法线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还能识别出其它物体:
VNDetectHorizonRequest
类能够获得画面的水平角度.VNDetectFaceRectanglesRequest
类能够实现人脸识别;VNDetectTextRectanglesRequest
类能够识别文本和区域;VNTrackRectangleRequest
和VNTrackObjectRequest
类能够追踪识别出的物体.例如,上面的例子想改为识别二维码,并在二维码上显示图片或视频,只须要更改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
}
...
}
复制代码
效果以下:
后续还能够在识别出二维码的内容后,在上面展现图片,打开网页或播放视频等
除了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) {
}
}
复制代码
具体业务逻辑没有什么太大的难点,再也不赘述了.
须要注意的是,提到了地理定位的测试方法:
第三部分读书笔记结束!