使用vision pro实现图片识别,真的很简单。本文阅读约3分钟,读完你就能复刻一个在visionpro上图片识别的app。
创建项目
首先创建VP项目
在项目中选择Window,空间渲染选择realityKit,沉浸空间选择混合。Mixed支持窗口和全景融合。Include Tests可选可不选。
设置Image Tracker
这里随便找一张图片即可,比如我们使用下面的图片
在Xcode中,选择Assets。默认是没有图片集合的,因此我们点击Assets窗口下面的+,在选中+/AR and Textures/Texture Set。
添加图片到1x中。
Image Set方便我们直接方便通过代码在程序包体中加载图片。IOS中的图片加载细节参考:https://juejin.cn/post/6844903978262773774
添加模型
模型可以直接从Apple的测试模型网站中获得。https://developer.apple.com/jp/augmented-reality/quick-look/
这个网站中提供了多个USDZ的模型下载地址。
这里我们下载小飞机模型文件。下载完成后,将下载好的usdz文件直接拖拽到项目中即可。
编写代码
import SwiftUI
@main
struct VisionOSImageTrackingSampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImageTrackingView()
}
}
}
代码中ContentView是我们等会要声明的面板类的描述,而ImageTrackingView是在空间中的自由画面的类描述。下面对ContentView的内容进行实现。创建一个新的脚本ContentView.swift在脚本中添加如下脚本。
import SwiftUI
import RealityKit
import RealityKitContent
struct ContentView: View {
@State private var showImmersiveSpace = false
@State private var immersiveSpaceIsShown = false
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
VStack {
Model3D(named: "toy_biplane_idle")
.padding(.bottom, 50)
Text("Image Tracking Sample")
Toggle("Image Tracking Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
}
.padding()
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
switch await openImmersiveSpace(id: "ImmersiveSpace") {
case .opened:
immersiveSpaceIsShown = true
case .error, .userCancelled:
fallthrough
@unknown default:
immersiveSpaceIsShown = false
showImmersiveSpace = false
}
} else if immersiveSpaceIsShown {
await dismissImmersiveSpace()
immersiveSpaceIsShown = false
}
}
}
}
}
#Preview(windowStyle: .automatic) {
ContentView()
}
创建一个ImageTrackingView.swift脚本,复制下面代码进去。
import ARKit
import SwiftUI
import RealityKit
struct ImageTrackingView: View {
@State private var imageTrackingProvider:ImageTrackingProvider?
@State private var entityMap: [UUID: Entity] = [:]
@State private var contentEntity: Entity?
private let session = ARKitSession()
var body: some View {
RealityView { content in
contentEntity = try! await Entity(named: "toy_biplane_idle")
content.add(contentEntity!)
}.task {
await loadImage()
await runSession()
await processImageTrackingUpdates()
}
}
func loadImage() async {
let uiImage = UIImage(named: "oneplanet_applevisionpro_marker")
let cgImage = uiImage?.cgImage
let referenceImage = ReferenceImage(cgimage: cgImage!, physicalSize: CGSize(width: 1920, height: 1005))
imageTrackingProvider = ImageTrackingProvider(
referenceImages: [referenceImage]
)
}
func runSession() async {
do {
if ImageTrackingProvider.isSupported {
try await session.run([imageTrackingProvider!])
print("image tracking initializing in progress.")
} else {
print("image tracking is not supported.")
}
} catch {
print("Error during initialization of image tracking. [\(type(of: self))] [\(#function)] \(error)")
}
}
func processImageTrackingUpdates() async {
for await update in imageTrackingProvider!.anchorUpdates {
updateImage(update.anchor)
}
}
private func updateImage(_ anchor: ImageAnchor) {
if entityMap[anchor.id] == nil {
entityMap[anchor.id] = contentEntity
}
if anchor.isTracked {
contentEntity!.isEnabled = true
let transform = Transform(matrix: anchor.originFromAnchorTransform)
entityMap[anchor.id]?.transform.translation = transform.translation
entityMap[anchor.id]?.transform.rotation = transform.rotation
} else {
contentEntity!.isEnabled = false
}
}
}
#Preview {
ImageTrackingView()
}
脚本的内容很容易理解。首先来看ContentView中,VStack描述界面的布局。在最上面绘制了一个三维模型。中间声明了一个文本内容,下面为一个选择按钮。
VStack {
Model3D(named: "toy_biplane_idle")
.padding(.bottom, 50)
Text("Image Tracking Sample")
Toggle("Image Tracking Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
}
ImageTrackingView的内容也很好理解。
首先加载Texture Set中的图片
let uiImage = UIImage(named: "oneplanet_applevisionpro_marker")
然后作为ImageTrackingProvider的图片源传入。
imageTrackingProvider = ImageTrackingProvider(
referenceImages: [referenceImage]
)
最后将图片获取到的空间位姿传给模型,实现模型和图片的匹配。
let transform = Transform(matrix: anchor.originFromAnchorTransform)
entityMap[anchor.id]?.transform.translation = transform.translation
entityMap[anchor.id]?.transform.rotation = transform.rotation
声明
首先声明:参考文章
其次声明:首先vp的模拟器目前是不支持手势、平面、图片等对象的追踪,因此希望开发这两个效果的朋友,需要使用真机进行验证。