Показать ограничивающий прямоугольник при обнаружении объекта с помощью ARKit 2
Я отсканировал и обучил несколько объектов реального мира. У меня есть ARReferenceObject
и приложение обнаруживает их в порядке.
Проблема, с которой я сталкиваюсь, заключается в том, что когда у объекта нет отчетливых, ярких функций, требуется несколько секунд, чтобы вернуть результат обнаружения, что я могу понять. Теперь я хочу, чтобы приложение показывало ограничивающий прямоугольник и индикатор активности поверх объекта, пока оно пытается его обнаружить.
Я не вижу никакой информации по этому поводу. Кроме того, если есть какой-либо способ узнать время, когда начинается обнаружение, или процент достоверности обнаруживаемого объекта.
Любая помощь приветствуется.
1 ответ
Можно показать boundingBox
в отношении ARReferenceObject
до его обнаружения; хотя я не уверен, почему вы хотели бы сделать это (заранее в любом случае).
Например, если предположить, что ваш referenceObject находится на горизонтальной поверхности, вам сначала нужно поместить оценочную ограничивающую рамку на плоскость (или использовать какой-либо другой метод, чтобы разместить ее заранее), и за то время, которое потребовалось для обнаружения ARPlaneAnchor и размещения boundingBox скорее всего, ваша модель уже была бы обнаружена.
Возможный подход:
Поскольку вы, без сомнения, знаете ARReferenceObject
имеет center
, extent
а также scale
собственность, а также набор rawFeaturePoints
связанный с объектом.
Таким образом, мы можем создать наш собственный узел boundingBox на основе некоторого примера кода от Apple в разделе " Сканирование и обнаружение 3D-объектов" и создать наш собственный SCNNode, который будет отображать ограничивающую рамку приблизительного размера ARReferenceObject
который хранится локально до его обнаружения.
Обратите внимание, что вам нужно найти "wireframe_shader" из примера кода Apple, чтобы boundingBox отображался прозрачным:
import Foundation
import ARKit
import SceneKit
class BlackMirrorzBoundingBox: SCNNode {
//-----------------------
// MARK: - Initialization
//-----------------------
/// Creates A WireFrame Bounding Box From The Data Retrieved From The ARReferenceObject
///
/// - Parameters:
/// - points: [float3]
/// - scale: CGFloat
/// - color: UIColor
init(points: [float3], scale: CGFloat, color: UIColor = .cyan) {
super.init()
var localMin = float3(Float.greatestFiniteMagnitude)
var localMax = float3(-Float.greatestFiniteMagnitude)
for point in points {
localMin = min(localMin, point)
localMax = max(localMax, point)
}
self.simdPosition += (localMax + localMin) / 2
let extent = localMax - localMin
let wireFrame = SCNNode()
let box = SCNBox(width: CGFloat(extent.x), height: CGFloat(extent.y), length: CGFloat(extent.z), chamferRadius: 0)
box.firstMaterial?.diffuse.contents = color
box.firstMaterial?.isDoubleSided = true
wireFrame.geometry = box
setupShaderOnGeometry(box)
self.addChildNode(wireFrame)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) Has Not Been Implemented") }
//----------------
// MARK: - Shaders
//----------------
/// Sets A Shader To Render The Cube As A Wireframe
///
/// - Parameter geometry: SCNBox
func setupShaderOnGeometry(_ geometry: SCNBox) {
guard let path = Bundle.main.path(forResource: "wireframe_shader", ofType: "metal", inDirectory: "art.scnassets"),
let shader = try? String(contentsOfFile: path, encoding: .utf8) else {
return
}
geometry.firstMaterial?.shaderModifiers = [.surface: shader]
}
}
Чтобы отобразить ограничивающий прямоугольник, вы должны сделать что-то вроде следующего, отметив, что в моем примере у меня есть следующие переменные:
@IBOutlet var augmentedRealityView: ARSCNView!
let configuration = ARWorldTrackingConfiguration()
let augmentedRealitySession = ARSession()
Чтобы отобразить boundingBox до обнаружения самого объекта, вы должны вызвать func
loadBoundigBox
в viewDidLoad
например:
/// Creates A Bounding Box From The Data Available From The ARObject In The Local Bundle
func loadBoundingBox(){
//1. Run Our Session
augmentedRealityView.session = augmentedRealitySession
augmentedRealityView.delegate = self
//2. Load A Single ARReferenceObject From The Main Bundle
if let objectURL = Bundle.main.url(forResource: "fox", withExtension: ".arobject"){
do{
var referenceObjects = [ARReferenceObject]()
let object = try ARReferenceObject(archiveURL: objectURL)
//3. Log it's Properties
print("""
Object Center = \(object.center)
Object Extent = \(object.extent)
Object Scale = \(object.scale)
""")
//4. Get It's Scale
let scale = CGFloat(object.scale.x)
//5. Create A Bounding Box
let boundingBoxNode = BlackMirrorzBoundingBox(points: object.rawFeaturePoints.points, scale: scale)
//6. Add It To The ARSCNView
self.augmentedRealityView.scene.rootNode.addChildNode(boundingBoxNode)
//7. Position It 0.5m Away From The Camera
boundingBoxNode.position = SCNVector3(0, -0.5, -0.5)
//8. Add It To The Configuration
referenceObjects.append(object)
configuration.detectionObjects = Set(referenceObjects)
}catch{
print(error)
}
}
//9. Run The Session
augmentedRealitySession.run(configuration, options: [.resetTracking, .removeExistingAnchors])
augmentedRealityView.automaticallyUpdatesLighting = true
}
В приведенном выше примере просто создает boundingBox из необнаруженных ARReferenceObject
и помещает его на 0,5 м вниз и на 0,5 м от Camera
который дает что-то вроде этого:
Вам, конечно, нужно сначала обработать положение boundBox, а также мотыгу, чтобы обработать удаление индикатора индикатора boundingBox.
Метод ниже просто показывает boundBox, когда фактический объект обнаружен, например:
//--------------------------
// MARK: - ARSCNViewDelegate
//--------------------------
extension ViewController: ARSCNViewDelegate{
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check We Have A Valid ARObject Anchor
guard let objectAnchor = anchor as? ARObjectAnchor else { return }
//2. Create A Bounding Box Around Our Object
let scale = CGFloat(objectAnchor.referenceObject.scale.x)
let boundingBoxNode = BlackMirrorzBoundingBox(points: objectAnchor.referenceObject.rawFeaturePoints.points, scale: scale)
node.addChildNode(boundingBoxNode)
}
}
Что дает что-то вроде этого:
Что касается таймера обнаружения, в Apple Sample Code есть пример, который показывает, сколько времени требуется для обнаружения модели.
В его самой грубой форме (не считая миллисекунд) вы можете сделать что-то вроде этого:
Сначала создайте А Timer
и var
хранить время обнаружения, например:
var detectionTimer = Timer()
var detectionTime: Int = 0
Затем, когда вы запускаете свой ARSessionConfiguration
инициализировать таймер, например:
/// Starts The Detection Timer
func startDetectionTimer(){
detectionTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(logDetectionTime), userInfo: nil, repeats: true)
}
/// Increments The Total Detection Time Before The ARReference Object Is Detected
@objc func logDetectionTime(){
detectionTime += 1
}
Тогда когда ARReferenceObject
было обнаружено недействительным таймер и записать время, например:
//--------------------------
// MARK: - ARSCNViewDelegate
//--------------------------
extension ViewController: ARSCNViewDelegate{
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check We Have A Valid ARObject Anchor
guard let _ = anchor as? ARObjectAnchor else { return }
//2. Stop The Timer
detectionTimer.invalidate()
//3. Log The Detection Time
print("Total Detection Time = \(detectionTime) Seconds")
//4. Reset The Detection Time
detectionTime = 0
}
}
Этого должно быть более чем достаточно, чтобы начать...
И обратите внимание, что этот пример не предоставляет boundingBox при сканировании объекта (посмотрите на пример кода Apple), он предоставляет его на основе существующего объекта ARReferenceObject, который подразумевается в вашем вопросе (при условии, что я правильно его интерпретировал).