网络知识 娱乐 iPad Swift Playgrounds中实现AR图像检测跟踪

iPad Swift Playgrounds中实现AR图像检测跟踪

c92c3178bab34c348c3fffbac1678114.gif

在Xcode中我们可以通过在Assets下创建”AR Resource Group"来添加需要检测与跟踪的图片(ARReferenceImage), 通过对每张图片的属性设定(如Name, Size, 和Units)以此来达到更好的显示效果。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP54mm54mbNjY2,size_20,color_FFFFFF,t_70,g_se,x_16watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP54mm54mbNjY2,size_20,color_FFFFFF,t_70,g_se,x_16 

但截至目前2022年3月10日, Swift Playgrounds Version 4.0.2还没有功能菜单创建AR Resource Group,也没有对图片属性添加的功能窗口。所以为了解决此问题,我们必须通过手动添加参考图像库,参考图片,以及参考图片属性来实现相同的功能,以下为实现过程。

1. 添加照片到资源文件

点击左侧“添加”- 选择“照片”,注意这里一定要添加格式为PNG或JPEG的图片,手机或ipad拍摄的照片格式为HEIF, 可以通过截屏来获取PNG格式。添加后,图片会在“资源”下显示,长按图片可以“重命名”为英文或拼音。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP54mm54mbNjY2,size_20,color_FFFFFF,t_70,g_se,x_16

2. 添加3D模型到“资源”文件

点击左侧“添加”- 选择“插入自”,选择你想要展示的格式为usdz的3D模型。添加完成后,模型文件会在左侧“资源”文件下显示。这里可以更改图片名称与相对应模型名称一致。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP54mm54mbNjY2,size_20,color_FFFFFF,t_70,g_se,x_16

 3. 创建参考图片库,生成参考图片,设置参考图片属性,添加参考图片到库,

        3.1 我们需要手动创建参考图片库.

var trackedImageLibs = Set()

        3.2 将图片转变为UIImage, 然后转变为CGImage,最后转变为ARReferenceImage, 并设置属性:朝向,实际宽度.

let teapotUIImage = UIImage(named: "teapot")
let carUIImage = UIImage(named: "toy_car")
let drummerUIImage = UIImage(named: "toy_drummer")

let teapotRefImg = ARReferenceImage(teapotUIImage!.cgImage!, orientation: .up, physicalWidth: 0.075)
let carRefImg = ARReferenceImage(carUIImage!.cgImage!, orientation: .up, physicalWidth: 0.075)
let drummerRefImg = ARReferenceImage(drummerUIImage!.cgImage!, orientation: .up, physicalWidth: 0.075)

        3.3 给每一个ARReferenceImage的name属性一个值,这个值类型为Optional String. 注意这里的name和资源库里图片的名字是两个东西,如果你在Xcode中添加了参考图片,这里的name就是参考图片属性窗口里的name。如果你手动创建参考图片,这里的name默认值为nil。

teapotRefImg.name = "teapot"
carRefImg.name = "toy_car"
drummerRefImg.name = "toy_drummer"

        3.4 将所有参考图片添加进参考图片库

trackedImageLibs.insert(teapotRefImg)
trackedImageLibs.insert(carRefImg)
trackedImageLibs.insert(drummerRefImg)

4. 将我们创建的参考图片库分配给ARImageTrackingConfiguration里的trackingImages(跟踪图片库),因为ARKit只会把trackingImages库里的图片拿去与用户环境中的图片做检测和跟踪。

config.trackingImages = trackedImageLibs

5. 当ARKit检测到相关图片并生成ARImageAnchor时,可以利用ARImageAnchor的referenceImage属性里的name属性,用来加载与之对应的模型。

        5.1 如果检测到特征点相匹配的图片,生成ARImageAnchor

guard let imageAnchor = anchors[0] as? ARImageAnchor else {return}

        5.2 将ARImageAnchor里的参考图片里的名称分配给一个新的类型为String的常量

let referImageName: String = imageAnchor.referenceImage.name ?? "example"

        5.3 根据获得的参考图片名称,加载与之对应的模型

ModelEntity.loadModelAsync(named: referImageName)

优点补充:在swift playgrounds上编写AR应用可以及时预览所呈现效果,避免了在Xcode中需要连接真机安装部署才能测试,其次还避免了免费用户在Xcode每周开发安装App数量限制的问题(未来swift playgrounds会不会有限制,不知道,希望永远不会)

缺点补充:在Xcode中,上传的参考图像会被检测是否具有高对比,多特征等因素,从而指导开发者筛选调整出更容易检测和跟踪的参考图像,优化用户体验。而在swift playgrounds中,开发者目前只能通过自身经验去挑选一些对比度高,特征信息丰富的图片。

以下为完整代码:

import SwiftUI
import ARKit
import RealityKit
import Combine

struct ContentView: View {
    var body: some View {
        ZStack{
            ARViewContainer()
        }
    }
}

struct ARViewContainer: UIViewRepresentable {
    func makeUIView(context: Context) -> ARView {
        let arView = ARView(frame: .zero)
        let config = ARImageTrackingConfiguration()
        
        var trackedImageLibs = Set()
        
        let teapotUIImage = UIImage(named: "teapot")
        let carUIImage = UIImage(named: "toy_car")
        let drummerUIImage = UIImage(named: "toy_drummer")
        
        let teapotRefImg = ARReferenceImage(teapotUIImage!.cgImage!, orientation: .up, physicalWidth: 0.075)
        let carRefImg = ARReferenceImage(carUIImage!.cgImage!, orientation: .up, physicalWidth: 0.075)
        let drummerRefImg = ARReferenceImage(drummerUIImage!.cgImage!, orientation: .up, physicalWidth: 0.075)
        
        teapotRefImg.name = "teapot"
        carRefImg.name = "toy_car"
        drummerRefImg.name = "toy_drummer"
        
        trackedImageLibs.insert(teapotRefImg)
        trackedImageLibs.insert(carRefImg)
        trackedImageLibs.insert(drummerRefImg)
        
        config.trackingImages = trackedImageLibs
        config.maximumNumberOfTrackedImages = 4
        
        arView.session.run(config, options: [])
        arView.session.delegate = arView
        return arView
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
    }
}

extension ARView: ARSessionDelegate {
    public func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        guard let imageAnchor = anchors[0] as? ARImageAnchor else {return}
        
        let imageAnchorEntity = AnchorEntity(anchor: imageAnchor)
        let referImageName = imageAnchor.referenceImage.name ?? "example"
        
        var cancellable: AnyCancellable? = nil 
        cancellable = ModelEntity.loadModelAsync(named: referImageName).sink(receiveCompletion: { status in
            print("Completion: (status)")
            cancellable?.cancel()
        }, receiveValue: { entity in
            imageAnchorEntity.addChild(entity)
        })
        self.scene.addAnchor(imageAnchorEntity)
    }
}