Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Cannot extract imagePair from generated Spatial Photos

Hi I am trying to implement something simple as people can share their Spatial Photos with others (just like this post). I encountered the same issue with him, but his answer doesn't help me out here.

Briefly speaking, I am using CGImgaeSoruce to extract paired leftImage and rightImage from one fetched spatial photo

let photos = PHAsset.fetchAssets(with: .image, options: nil)
// enumerating photos ....
if asset.mediaSubtypes.contains(PHAssetMediaSubtype.spatialMedia) {
        spatialAsset = asset
}
// other code show below

I can fetch left and right images from native Spatial Photo (taken by Apple Vision Pro or iPhone 15+), but it didn't work on generated spatial photo (2D -> 3D feat in Photos).

// imageCount is 1 when it comes to generated spatial photo
let imageCount = CGImageSourceGetCount(source)

I searched over the net and someone says the generated version is having a depth image instead of left/right pair. But still I cannot extract any depth image from imageSource.

The full code below, the imagePair extraction will stop at "no groups found":

func extractPairedImage(phAsset: PHAsset, completion: @escaping (StereoImagePair?) -> Void) {
        let options = PHImageRequestOptions()
        options.isNetworkAccessAllowed = true
        options.deliveryMode = .highQualityFormat
        options.resizeMode = .none
        options.version = .original
        return PHImageManager.default().requestImageDataAndOrientation(for: phAsset, options: options) {
            imageData, _, _, _ in
            guard let imageData,
                let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil)
            else {
                completion(nil)
                return
            }
            let stereoImagePair = stereoImagePair(from: imageSource)
                completion(stereoImagePair)             
            }
        }
}

 func stereoImagePair(from source: CGImageSource) -> StereoImagePair? {
            guard let properties = CGImageSourceCopyProperties(source, nil) as? [CFString: Any] else {
                return nil
            }
            let imageCount = CGImageSourceGetCount(source)
            print(String(format: "%d images found", imageCount))

            guard let groups = properties[kCGImagePropertyGroups] as? [[CFString: Any]] else {
                /// function returns here
                print("no groups found")
                return nil
            }
            guard
                let stereoGroup = groups.first(where: {
                    let groupType = $0[kCGImagePropertyGroupType] as! CFString
                    return groupType == kCGImagePropertyGroupTypeStereoPair
                })
            else {
                return nil
            }

            guard let leftIndex = stereoGroup[kCGImagePropertyGroupImageIndexLeft] as? Int,
                let rightIndex = stereoGroup[kCGImagePropertyGroupImageIndexRight] as? Int,
                let leftImage = CGImageSourceCreateImageAtIndex(source, leftIndex, nil),
                let rightImage = CGImageSourceCreateImageAtIndex(source, rightIndex, nil),
                let leftProperties = CGImageSourceCopyPropertiesAtIndex(source, leftIndex, nil),
                let rightProperties = CGImageSourceCopyPropertiesAtIndex(source, rightIndex, nil)
            else {
                return nil
            }
            return (leftImage, rightImage, self.identifier)
}

Any suggestion? Thanks visionOS 2.4

Hi. Thanks for asking. I tried to reproduce your results using a Spatial Photo I created here but I was unsuccessful. Can you provide a copy of the image you are using for testing?

Accepted Answer

I figure that myself. It turns that the I would request .current version of PHAsset in PHImageRequestOptions. I use .original before, but I guess the converted spatial photo should be using .current because the original photo is a .jpeg, which cannot be spatial photo wrapper format (heic did).

I guess when implementing the API, the logic here is, if the original photo is heic format, 3D-conversion updates imgDataSource by appending more image to the original source. But when it comes to jpeg, it stores the new 3D photo as .current only.

Hello! You're absolutely right. When you use the Create Spatial feature in the visionOS 2 Photos app to convert a 2D photo to a spatial photo, the created spatial photo is saved as an edit to the original photo, so that you can always undo the edit and revert the photo to the 2D original in your Photos library if you want. As a result, you need to retrieve the current version of the asset, not the original, to retrieve the HEIC image for the created spatial photo.

Cannot extract imagePair from generated Spatial Photos
 
 
Q