override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 確保可以取得當前session的frame
guard let currentFrame = sceneView.session.currentFrame else { return }
// 影像的處理是非常耗CPU,千萬不要放在main thread中
DispatchQueue.global(qos: .background).async {
do {
// VNDetectRectanglesRequest只有一個參數,那就是一個closure
// 這個closure負責當影像分析完畢的callback
// VNDetectRectanglesRequest最多只會回傳一個result, 因為他的一個屬性maximumObservations的預設值是1
let request = VNDetectRectanglesRequest { (request, error) in
// 利用compactMap將[Any]轉成[VNRectangleObservation]
guard let results = request.results?.compactMap({ $0 as? VNRectangleObservation }),
let result = results.first else {
// 沒有找到任何Rectangles形狀
print("[Vision] VNRequest produced no result ")
return
}
// 透過Vison只能取得2D image的特征
// 利用VNRectangleObservation的4個property(2D特徵), 透過ARKit中提供的hitTest轉化成3D空間的座標
let coordinates: [matrix_float4x4] = [
result.topLeft,
result.topRight,
result.bottomRight,
result.bottomLeft
].compactMap {
// currentFrame.hitTest(_:types:) projects a point to a 3D object
guard let hitFeature = currentFrame.hitTest($0, types: .featurePoint).first else { return nil }
return hitFeature.worldTransform
}
guard coordinates.count == 4 else { return }
DispatchQueue.main.async {
// 如果有先前的Billboard存在,那先移除掉
self.removeBillboard()
let (topLeft, topRight, bottomRight, bottomLeft) =
(coordinates[0], coordinates[1], coordinates[2], coordinates[3])
self.createBillboard(topLeft: topLeft, topRight: topRight,
bottomRight: bottomRight, bottomLeft: bottomLeft)
}
}
// 執行request
let handler = VNImageRequestHandler(cvPixelBuffer: currentFrame.capturedImage)
try handler.perform([request])
} catch(let error) {
print("An error occurred during rectangle detection: \(error)")
}
}
}