You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AR Kit 2.0跟踪图片批量下载转换优化方案咨询

如何优化AR应用中大量ARReferenceImage的下载与加载流程?

我近期开发了一款支持自定义图片与文字创建AR对象的AR应用,目前应用会生成QR码并将其图片存储至服务器。我希望实现将这些图片取回并存储到AR Resource Group文件夹的功能。

当前方案是下载全部图片,转换为ARReferenceImage类型后,基于该图片列表运行AR Session。但随着用户与AR创作内容增多,下载耗时大幅增加,用户体验越来越差。

以下是我用于取回并转换所有图片类型的代码:

func getAllQRCodes(completion: @escaping CompletionHandler) {
    var downloaded = 0
    let reference = Database.database().reference().child("QR")
    reference.observe(DataEventType.value, with: { (snapshot) in
        let postDict = snapshot.value as? [String : String] ?? [:]
        DispatchQueue.global(qos: .background).async {
            for (key, value) in postDict {
                print("downloaded: \(downloaded) / total: \(postDict.count)")
                let percentage = (Double(downloaded)/Double(postDict.count)) * 100
                DispatchQueue.main.async {
                    self.qrProgressCircle.startProgress(to: CGFloat(percentage), duration: 1)
                }
                if self.downloadURLs.contains(value) == false {
                    self.downloadURLs.insert(value)
                    let qrImage = UIImage(url: URL(string: value))
                    let qrCiImage = CIImage(image: qrImage!)
                    let qrCGImage = self.convertCIImageToCGImage(inputImage: qrCiImage!)
                    let qrARImage = ARReferenceImage(qrCGImage!, orientation: CGImagePropertyOrientation.up, physicalWidth: 0.2)
                    qrARImage.name = key
                    self.ARTrackingImages.insert(qrARImage)
                    downloaded += 1
                }
            }
            completion(true)
        }
    })
}

我的问题是:是否存在更优实现方案?比如在后台线程执行操作并持续重启sceneView session?这是否会导致糟糕的用户体验?


更优实现方案推荐

针对你当前的问题,我有几个具体的优化方向,既能减少下载耗时,又能提升用户体验:

1. 改用增量下载,避免全量同步

当前全量下载所有QR图片的方式,随着内容增多必然越来越慢。你可以:

  • 在服务器端给每个QR记录添加最后更新时间戳或唯一标识(比如版本号)
  • 本地保存上次同步的时间戳/版本号,每次只从服务器拉取新增或更新的QR图片
  • 用数据库的queryOrdered(byChild:)和queryStarting(atValue:)方法过滤出需要下载的内容,而不是获取整个QR节点的全部数据

示例思路代码:

func getNewQRCodes(lastSyncTimestamp: TimeInterval, completion: @escaping CompletionHandler) {
    let reference = Database.database().reference().child("QR")
    // 假设服务器每个QR节点有"updatedAt"字段记录更新时间
    reference.queryOrdered(byChild: "updatedAt").queryStarting(atValue: lastSyncTimestamp)
        .observeSingleEvent(of: .value) { snapshot in
            // 只处理新增/更新的内容
            let newPostDict = snapshot.value as? [String: [String: Any]] ?? [:]
            // 后续下载转换逻辑...
            // 更新本地lastSyncTimestamp为当前时间
        }
}

2. 并行异步下载,提升下载效率

你当前的代码是串行循环下载图片,效率极低。可以用OperationQueue或URLSession的并行任务来同时下载多个图片:

  • 创建一个后台OperationQueue,设置合理的最大并发数(比如5-10,避免服务器压力过大)
  • 每个图片下载作为一个独立的Operation,完成后转换为ARReferenceImage
  • 注意对downloadURLs、ARTrackingImages这些共享集合的线程安全访问(比如用串行队列同步读写)

示例代码片段:

let downloadQueue = OperationQueue()
downloadQueue.maxConcurrentOperationCount = 5
downloadQueue.qualityOfService = .background

for (key, value) in newPostDict {
    guard !self.downloadURLs.contains(value) else { continue }
    let operation = BlockOperation { [weak self] in
        guard let self = self, let url = URL(string: value) else { return }
        do {
            let data = try Data(contentsOf: url)
            guard let qrImage = UIImage(data: data), let ciImage = CIImage(image: qrImage) else { return }
            let cgImage = self.convertCIImageToCGImage(inputImage: ciImage)
            guard let arImage = ARReferenceImage(cgImage!, orientation: .up, physicalWidth: 0.2) else { return }
            arImage.name = key
            
            // 线程安全更新共享集合
            DispatchQueue.main.sync {
                self.downloadURLs.insert(value)
                self.ARTrackingImages.insert(arImage)
                // 更新进度
                let downloadedCount = self.ARTrackingImages.count
                let percentage = Double(downloadedCount)/Double(newPostDict.count) * 100
                self.qrProgressCircle.startProgress(to: CGFloat(percentage), duration: 0.5)
            }
        } catch {
            print("下载失败: \(error)")
        }
    }
    downloadQueue.addOperation(operation)
}

3. 动态更新AR Session,无需重启

完全不需要重启SceneView的Session!ARKit支持在Session运行时动态添加参考图片,用户可以在下载过程中就开始使用已下载好的QR码进行AR追踪,不用等全部下载完成。

使用session.add(_:)方法添加单个或多个ARReferenceImage:

// 当一个ARReferenceImage转换完成后,直接添加到当前Session
func addARImageToSession(_ arImage: ARReferenceImage) {
    guard let configuration = self.sceneView.session.configuration as? ARImageTrackingConfiguration else { return }
    // 如果是首次添加,初始化trackingImages数组
    if configuration.trackingImages == nil {
        configuration.trackingImages = Set<ARReferenceImage>()
    }
    configuration.trackingImages?.insert(arImage)
    // 重新运行配置(不会中断当前追踪,只是更新可识别的图片)
    self.sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}

注意:这里的run方法使用.resetTracking选项只会重置图片追踪状态,不会重启整个相机会话,用户几乎感知不到中断。

4. 本地缓存已转换的ARReferenceImage

把已经转换好的ARReferenceImage序列化存储到本地(比如Documents目录),下次启动App时直接加载缓存,不用重新下载和转换:

  • 利用ARReferenceImage的rawData属性获取序列化数据
  • 将数据保存到本地文件,文件名用QR的key作为标识
  • 启动App时先加载所有本地缓存的ARReferenceImage,再进行增量同步

关于“后台线程操作+持续重启Session”的问题

频繁重启SceneView的Session会导致非常糟糕的用户体验:

  • 每次重启都会中断相机预览,出现短暂的黑屏或卡顿
  • AR追踪会被重置,之前识别的AR对象会消失,需要用户重新扫描
  • 频繁的状态切换会让用户感到困惑,降低App的流畅感

而上面提到的动态添加参考图片的方式,完全避免了重启Session的必要,是更优的选择。


内容的提问来源于stack exchange,提问作者Antonio Chan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 06:28:20