Показать ограничивающий прямоугольник при обнаружении объекта с помощью 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 до обнаружения самого объекта, вы должны вызвать funcloadBoundigBox в 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, который подразумевается в вашем вопросе (при условии, что я правильно его интерпретировал).

Другие вопросы по тегам