如何从AVCapturePhoto对象将AVDepthData写入照片文件并正确读取?
解决iPhone LiDAR深度数据写入与读取问题
一、正确写入嵌入深度数据的关键步骤
1. 确保捕获会话启用深度数据交付
在配置AVCaptureSession时,必须开启深度数据交付,并设置合适的深度格式:
let session = AVCaptureSession() session.beginConfiguration() // 选择支持深度的后置相机(LiDAR/TrueDepth) guard let cameraDevice = AVCaptureDevice.default(.builtInTrueDepthCamera, for: .photo, position: .back) else { fatalError("设备不支持深度相机") } guard let input = try? AVCaptureDeviceInput(device: cameraDevice) else { return } if session.canAddInput(input) { session.addInput(input) } // 配置照片输出并启用深度数据 let photoOutput = AVCapturePhotoOutput() if session.canAddOutput(photoOutput) { session.addOutput(photoOutput) } if photoOutput.isDepthDataDeliverySupported { photoOutput.isDepthDataDeliveryEnabled = true // 优先选择Float16格式的深度数据(兼容性更优) let preferredDepthFormat = photoOutput.availableDepthDataFormats.first(where: { $0.formatDescription.mediaSubType == .depthDataTypeFloat16 }) ?? photoOutput.availableDepthDataFormats.first! photoOutput.setPreferredDepthDataFormat(preferredDepthFormat) } session.commitConfiguration()
2. 配置照片捕获设置
捕获照片时,必须开启embedsDepthDataInPhoto:
let captureSettings = AVCapturePhotoSettings() captureSettings.embedsDepthDataInPhoto = true // 若需要视差数据,确保设备支持并开启对应配置 captureSettings.isDisparityDeliveryEnabled = photoOutput.isDisparityDeliverySupported photoOutput.capturePhoto(with: captureSettings, delegate: self)
3. 正确保存照片数据
禁止二次编码:必须直接使用AVCapturePhoto的fileDataRepresentation()获取包含深度数据的原始文件数据,再写入磁盘。不要通过UIImage转码保存(会丢失深度信息):
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { guard error == nil, let imageData = photo.fileDataRepresentation() else { print(error?.localizedDescription ?? "无法获取照片数据") return } let saveURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("depth_photo.heic") do { try imageData.write(to: saveURL) print("带深度的照片已保存至: \(saveURL)") } catch { print("保存失败: \(error.localizedDescription)") } }
推荐使用HEIC格式保存,它对深度数据的支持更完善;JPEG也支持,但部分场景下可能丢失辅助数据。
二、正确读取嵌入的深度/视差数据
使用CGImageSource读取时,需指定对应的辅助数据类型:
func extractDepthAndDisparity(from fileURL: URL) { guard let imageSource = CGImageSourceCreateWithURL(fileURL as CFURL, nil) else { print("无法创建CGImageSource") return } // 读取深度数据(kCGImageAuxiliaryDataTypeDepth) let depthOptions = [kCGImageSourceAuxiliaryDataType: kCGImageAuxiliaryDataTypeDepth] as CFDictionary if let depthAuxInfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(imageSource, 0, depthOptions) as? [String: Any] { if let depthData = depthAuxInfo[kCGImageAuxiliaryDataInfoData] as? Data, let depthCGImage = CGImageCreateWithJPEGDataProvider( CGDataProvider(data: depthData as CFData), nil, true, .intentDefault ) { // 成功获取深度图像,可转换为UIImage使用 let depthImage = UIImage(cgImage: depthCGImage) print("深度数据读取成功") } } // 读取视差数据(kCGImageAuxiliaryDataTypeDisparity) let disparityOptions = [kCGImageSourceAuxiliaryDataType: kCGImageAuxiliaryDataTypeDisparity] as CFDictionary if let disparityAuxInfo = CGImageSourceCopyAuxiliaryDataInfoAtIndex(imageSource, 0, disparityOptions) as? [String: Any] { if let disparityData = disparityAuxInfo[kCGImageAuxiliaryDataInfoData] as? Data, let disparityCGImage = CGImageCreateWithJPEGDataProvider( CGDataProvider(data: disparityData as CFData), nil, true, .intentDefault ) { let disparityImage = UIImage(cgImage: disparityCGImage) print("视差数据读取成功") } } }
三、常见排查要点
- 设备兼容性:仅配备TrueDepth相机(iPhone X及以后)或LiDAR扫描仪(iPhone 12 Pro及以后)的机型支持深度数据嵌入,确保测试设备符合要求。
- 避免二次编码:任何对
AVCapturePhoto的CGImage进行重新编码的操作(比如用UIImageJPEGRepresentation)都会丢失深度数据,必须直接保存fileDataRepresentation()的原始数据。 - 检查文件索引:如果是Live Photo等包含多帧的文件,需遍历
CGImageSource的所有索引,而不只是索引0。 - 验证深度数据存在:在保存前可通过
photo.depthData != nil确认捕获到了深度数据,排除捕获阶段的问题。
内容的提问来源于stack exchange,提问作者Matej Ukmar
相关产品推荐
相关产品推荐

