在SCNNode / ARKit Swift中获取图像大小

时间:2018-08-13 13:35:13

标签: swift uiimage arkit scnnode

我正在尝试扫描参考图像,然后在打印的参考图像上方显示图像本身。 “虚拟”图像尺寸应与打印尺寸相同。

我的想法:获取打印的参考图像的大小,然后将SCNNode中的图像缩放至该大小(或将SCNNode缩放至此大小?)

但是:1->如何获取打印图像的大小,2->用于缩放SCNNode,我也需要此节点的大小。如何获得?

import UIKit
import SceneKit
import ARKit
import AVKit
import AVFoundation

class ViewController: UIViewController, ARSCNViewDelegate {

@IBOutlet var sceneView: ARSCNView!
private var planeNode: SCNNode?
private var imageNode: SCNNode?
private var animationInfo: AnimationInfo?
private var currentMediaName: String?
private var scrollView: UIScrollView!

override func viewDidLoad() {
    super.viewDidLoad()

    let scene = SCNScene()
    sceneView.scene = scene
    sceneView.delegate = self

}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Load reference images to look for from "AR Resources" folder
    guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else {
        fatalError("Missing expected asset catalog resources.")
    }

    // Create a session configuration
    let configuration = ARWorldTrackingConfiguration()

    // Add previously loaded images to ARScene configuration as detectionImages
    configuration.detectionImages = referenceImages

    // Run the view's session
    sceneView.session.run(configuration)

    let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(rec:)))
    //Add recognizer to sceneview
    sceneView.addGestureRecognizer(tap)
}

//Method called when tap
@objc func handleTap(rec: UITapGestureRecognizer){
        let location: CGPoint = rec.location(in: sceneView)
        let hits = self.sceneView.hitTest(location, options: nil)
        if !hits.isEmpty{
            let tappedNode = hits.first?.node
            if tappedNode != nil && tappedNode?.name != nil{

                let stringArr = tappedNode?.name?.components(separatedBy: "-")
                let name = stringArr! [0]
                let size = stringArr! [1].components(separatedBy: ",")
                let width = Float(size [0])
                let height = Float(size [1])
                loadReferenceImage(tappedNode: tappedNode!, name: (name),  width: width!, height: height!)
            }
        }
}

private func playVideo() {
    guard let path = Bundle.main.path(forResource: "video", ofType:"m4v") else {
        debugPrint("video.m4v not found")
        return
    }
    let player = AVPlayer(url: URL(fileURLWithPath: path))
    let playerController = AVPlayerViewController()
    playerController.player = player
    present(playerController, animated: true) {
        player.play()
    }
}





func loadReferenceImage(tappedNode: SCNNode, name: String, width: Float, height: Float){
    print("TAP")
    print(name)

    let currentNode = tappedNode.parent

    if let image = UIImage(named: "col" + name){

        let childNodes = currentNode?.childNodes
        for node in (childNodes)!{
            node.removeFromParentNode()
        }

        let newImage = UIImage(named: "col" + name)
        let newnode = SCNNode(geometry: SCNPlane(width: CGFloat(width), height: CGFloat(height)))
        newnode.geometry?.firstMaterial?.diffuse.contents = newImage
        newnode.scale = SCNVector3(x: 10, y: 10, z: 10)
        currentNode?.removeAnimation(forKey: "spin_around")



        let rotation = SCNVector3((currentNode?.eulerAngles.x)!-0.95,(currentNode?.eulerAngles.y)!,(currentNode?.eulerAngles.z)!)
        currentNode?.eulerAngles = rotation



        //SIZE??????

        let nodex = currentNode?.scale.x
        let nodey = currentNode?.scale.y
        let nodez = currentNode?.scale.z

        let factorx = width / nodex!
        let factory = height / nodey!

        currentNode?.addChildNode(newnode)
    }



}

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let imageAnchor = anchor as? ARImageAnchor else {
        return
    }


    // 1. Load scene.
    let planeScene = SCNScene(named: "art.scnassets/plane.scn")!
    let planeNode = planeScene.rootNode.childNode(withName: "planeRootNode", recursively: true)!

    // 2. Calculate size based on planeNode's bounding box.
    let (min, max) = planeNode.boundingBox
    let size = SCNVector3Make(max.x - min.x, max.y - min.y, max.z - min.z)

    // 3. Calculate the ratio of difference between real image and object size.
    // Ignore Y axis because it will be pointed out of the image.
    let widthRatio = Float(imageAnchor.referenceImage.physicalSize.width)/1.2
    let heightRatio = Float(imageAnchor.referenceImage.physicalSize.height)/1.2


    let width = imageAnchor.referenceImage.physicalSize.width
    let height = imageAnchor.referenceImage.physicalSize.height

    let prefix = "-"

    let imageSize = width.description + "," + height.description


    let targetName = imageAnchor.referenceImage.name! + prefix + imageSize


    // Pick smallest value to be sure that object fits into the image.
    let finalRatio = [widthRatio, heightRatio].min()!

    // 4. Set transform from imageAnchor data.
    planeNode.transform = SCNMatrix4(imageAnchor.transform)

    // 5. Animate appearance by scaling model from 0 to previously calculated value.
    let appearanceAction = SCNAction.scale(to: CGFloat(finalRatio), duration: 0.4)
    //test
    appearanceAction.timingMode = .easeOut

    // Set initial scale to 0.
    planeNode.scale = SCNVector3Make(0 , 0, 0)

    //rotate y
    let spin = CABasicAnimation(keyPath: "rotation")
    spin.fromValue = NSValue(scnVector4: SCNVector4(x: 0, y: 1, z: 0, w: 0))
    spin.toValue = NSValue(scnVector4: SCNVector4(x: 0, y: 1, z: 0, w: Float(CGFloat(2 * Double.pi))))
    spin.duration = 4
    spin.repeatCount = .infinity
    planeNode.addAnimation(spin, forKey: "spin_around")

    // Add to root node.
    sceneView.scene.rootNode.addChildNode(planeNode)
    // Run the appearance animation.
    planeNode.runAction(appearanceAction)
    planeNode.name = targetName

    let nodes = planeNode.childNodes
    for node in nodes{
        node.name = targetName
    }

    self.planeNode = planeNode
    self.imageNode = node

}

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor, updateAtTime time: TimeInterval) {
    guard let imageNode = imageNode, let planeNode = planeNode else {
        return
    }

    // 1. Unwrap animationInfo. Calculate animationInfo if it is nil.
    guard let animationInfo = animationInfo else {
        refreshAnimationVariables(startTime: time,
                                  initialPosition: planeNode.simdWorldPosition,
                                  finalPosition: imageNode.simdWorldPosition,
                                  initialOrientation: planeNode.simdWorldOrientation,
                                  finalOrientation: imageNode.simdWorldOrientation)
        return
    }

    // 2. Calculate new animationInfo if image position or orientation changed.
    if !simd_equal(animationInfo.finalModelPosition, imageNode.simdWorldPosition) || animationInfo.finalModelOrientation != imageNode.simdWorldOrientation {

        refreshAnimationVariables(startTime: time,
                                  initialPosition: planeNode.simdWorldPosition,
                                  finalPosition: imageNode.simdWorldPosition,
                                  initialOrientation: planeNode.simdWorldOrientation,
                                  finalOrientation: imageNode.simdWorldOrientation)
    }

    // 3. Calculate interpolation based on passedTime/totalTime ratio.
    let passedTime = time - animationInfo.startTime
    var t = min(Float(passedTime/animationInfo.duration), 1)
    // Applying curve function to time parameter to achieve "ease out" timing
    t = sin(t * .pi * 0.5)

    // 4. Calculate and set new model position and orientation.
    let f3t = simd_make_float3(t, t, t)
    planeNode.simdWorldPosition = simd_mix(animationInfo.initialModelPosition, animationInfo.finalModelPosition, f3t)
    planeNode.simdWorldOrientation = simd_slerp(animationInfo.initialModelOrientation, animationInfo.finalModelOrientation, t)
    //planeNode.simdWorldOrientation = imageNode.simdWorldOrientation

    guard let currentImageAnchor = anchor as? ARImageAnchor else { return }
}



func refreshAnimationVariables(startTime: TimeInterval, initialPosition: float3, finalPosition: float3, initialOrientation: simd_quatf, finalOrientation: simd_quatf) {
    let distance = simd_distance(initialPosition, finalPosition)
    // Average speed of movement is 0.15 m/s.
    let speed = Float(0.15)
    // Total time is calculated as distance/speed. Min time is set to 0.1s and max is set to 2s.
    let animationDuration = Double(min(max(0.1, distance/speed), 2))
    // Store animation information for later usage.
    animationInfo = AnimationInfo(startTime: startTime,
                                  duration: animationDuration,
                                  initialModelPosition: initialPosition,
                                  finalModelPosition: finalPosition,
                                  initialModelOrientation: initialOrientation,
                                  finalModelOrientation: finalOrientation)
}

}

1 个答案:

答案 0 :(得分:1)

为此,我认为首先需要通过

获取Pixels的{​​{1}}中的大小
  

将大小值乘以scale属性中的值以获得   图片的像素尺寸。

这样的例子就像这样:

UIImage

guard let image = UIImage(named: "launchScreen") else { return } let pixelWidth = image.size.width * image.scale let pixelHeight = image.size.height * image.scale print(pixelWidth, pixelHeight) 中制作时,我的图片大小为3072 x 4099,当我在控制台中记录结果时,尺寸也相同。

现在,这里最棘手的部分是将像素计算为可以在Adobe Illustrator中使用的大小,记住不同的设备具有不同的PPI(ARKit)密度。

在我的示例中,我将使用Pixels Per Inch的PPI(即401)。

iPhone7Plus

现在我们有了以米为单位的图像大小,创建一个具有该大小的 //1. Get The PPI Of The iPhone7Plus let iphone7PlusPixelsPerInch: CGFloat = 401 //2. To Get The Image Size In Inches We Need To Divide By The PPI let inchWidth = pixelWidth/iphone7PlusPixelsPerInch let inchHeight = pixelHeight/iphone7PlusPixelsPerInch //3. Calculate The Size In Metres (There Are 2.54 Cm's In An Inch) let widthInMetres = (inchWidth * 2.54) / 100 let heightInMeters = (inchHeight * 2.54) / 100 很简单,例如:

SCNNode

希望有帮助...

P.S:这可能有助于您获取屏幕的PPI(marchv/UIScreenExtension