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) {
}
}
複製代碼
具體業務邏輯沒有什麼太大的難點,再也不贅述了.
須要注意的是,提到了地理定位的測試方法:
第三部分讀書筆記結束!