I've implemented solution similar to Apple's example of capturing depth data with iPhone lidar camera. Main code snippets are as follow:
- Setting depth formats
let device = AVCaptureDevice.default(.builtInLiDARDepthCamera, for: .video, position: .back)!
let vidFormatsWithDepth = device.formats.filter { format in
format.formatDescription.dimensions.width == 1920 &&
format.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange &&
!format.isVideoBinned &&
!format.supportedDepthDataFormats.isEmpty &&
format.supportedDepthDataFormats.contains { $0.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_DepthFloat16 }
}
if let format = vidFormatsWithDepth.first {
let depthFormats = format.supportedDepthDataFormats.filter { $0.formatDescription.mediaSubType.rawValue == kCVPixelFormatType_DepthFloat16 }
try! device.lockForConfiguration()
device.activeFormat = format
device.activeDepthDataFormat = depthFormats.last
device.unlockForConfiguration()
}
- Photo output
func setUpPhotoOutput() {
photoOutput = AVCapturePhotoOutput()
photoOutput.maxPhotoQualityPrioritization = .quality
self.captureSession.addOutput(photoOutput)
photoOutput.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliverySupported
}
- Capturing photo
var format: [String: Any] = [:]
if photoOutput.availablePhotoPixelFormatTypes.contains(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
format[kCVPixelBufferPixelFormatTypeKey as String] = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
}
let settings = AVCapturePhotoSettings(format: format)
settings.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliveryEnabled
settings.isDepthDataFiltered = photoOutput.isDepthDataDeliveryEnabled
settings.embedsDepthDataInPhoto = photoOutput.isDepthDataDeliveryEnabled
photoOutput.capturePhoto(with: settings , delegate: self)
- Processing captured photo data
func createPhotoFile(
photo: AVCapturePhoto
) {
let customizer = PhotoDataCustomizer()
var mainImageData = photo.fileDataRepresentation(with: customizer)!
// note mainImageData should have embeded depth data, but...
let imageSource = CGImageSourceCreateWithData(mainImageData as CFData, nil)!
let depthDataDict = CGImageSourceCopyAuxiliaryDataInfoAtIndex(
imageSource,
0,
kCGImageAuxiliaryDataTypeDepth
)
let disparityDataDict = CGImageSourceCopyAuxiliaryDataInfoAtIndex(
imageSource,
0,
kCGImageAuxiliaryDataTypeDisparity
)
print("depthDataDict", depthDataDict ?? "nil")
print("disparityDataDict", disparityDataDict ?? "nil")
// ... both depthDataDict and disparityDataDict come out as nil
}
class PhotoDataCustomizer: NSObject, AVCapturePhotoFileDataRepresentationCustomizer {
func replacementDepthData(for photo: AVCapturePhoto) -> AVDepthData? {
let depthData = photo.depthData?.converting(toDepthDataType: kCVPixelFormatType_DepthFloat16)
return depthData
}
}
AVCapturePhoto's photo.depthData is present (not nil) and I would expect that it is embedded if settings.embedsDepthDataInPhoto = true, but both variants of depth data (kCGImageAuxiliaryDataTypeDepth, kCGImageAuxiliaryDataTypeDisparity) come out nil from CGImageSource.
How to properly read the depth data from photo file ... or properly write the depth data in the first place?
I was able to save depth data by adding it manually bypassing
var mainImageData = photo.fileDataRepresentation(with: customizer)!with the following: