Can't set VNGenerateImageFeaturePrintRequestRevision consistently, breaking computeDistance between two images

My app uses the Vision framework to find images that are visually similar. Before WWDC23, this code worked predictably to request image observations and compare images:

private func observe(cgImage: CGImage?) -> VNFeaturePrintObservation? {
        var returnVal: VNFeaturePrintObservation? = nil
        guard let cgImage = cgImage else {return returnVal}
        let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
        let request = VNGenerateImageFeaturePrintRequest()
        request.usesCPUOnly = true
        do {
            try imageRequestHandler.perform([request])
            returnVal = request.results?.first as? VNFeaturePrintObservation
        } catch {
            
        }
        return returnVal
    }


        func similarity(to compareAsset: Asset) -> Float {
        var dist = Float.infinity
        if let obs = self.observation, let compareObs = compareAsset.observation {
            try? obs.computeDistance(&dist, to: compareObs)
        }
        return dist
    }

In the new frameworks, there is a new VNGenerateImageFeaturePrintRequestRevision value, and observations made with different request revisions can't be compared. If you try, you get an error:

Error Domain=com.apple.vis Code=12 "The revision of the observations do not match" UserInfo={NSLocalizedDescription=The revision of the observations do not match}.

The docs state that by explicitly setting the new VNGenerateImageFeaturePrintRequestRevision property on my requests, I can force the request to use a particular version. But I've updated the above code to do this, but explicitly setting the revision of my request doesn't work, and my app still gets tons of errors about mismatched request revisions. Here's the updated code:

 private func observe(cgImage: CGImage?) -> VNFeaturePrintObservation? {
        var returnVal: VNFeaturePrintObservation? = nil
        guard let cgImage = cgImage else {return returnVal}
        let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
        let request = VNGenerateImageFeaturePrintRequest()
        
        if #available(iOS 17.0, *) {
            request.revision = VNGenerateImageFeaturePrintRequestRevision2
        } else {
            request.revision = VNGenerateImageFeaturePrintRequestRevision1
        }
        request.usesCPUOnly = true
        do {
            try imageRequestHandler.perform([request])
            returnVal = request.results?.first as? VNFeaturePrintObservation
        } catch {
            print("\(type(of: self)) :: \(#function) :: error in observation request \(error)")
        }
        return returnVal
    }

    func similarity(to compareAsset: Asset) -> Float {
        var dist = Float.infinity
        if let obs = self.observation, let compareObs = compareAsset.observation {
            do {
                try obs.computeDistance(&dist, to: compareObs)
            } catch {
                print("\(type(of: self)) :: \(#function) :: error in simvalue \(error)")
                if (error as NSError).code == 12 {
                    let revision = obs.requestRevision
                    let compareRevision = compareObs.requestRevision
                    print("\(type(of: self)) :: \(#function) :: simValue req mismatch \(revision) \(compareRevision)")
                }
            }
            
        }
        return dist
    }

This breaks my app, and I can't figure out how to reliably force requests to the revision number I need. What am I doing wrong here? Will this behavior sort itself out as the SDK evolves?

Thanks y'all

It's unclear from your code if the Asset you are comparing was produced from previous run / older version of your app. If so, then you are likely attempting to compute the distance of prints from two different revisions. (All VNObservations have a requestRevision property which indicates the revision of the request that produced it).

To resolve your issue, you will likely need to do one (or more) of the following:

  • Explicitly set the request revision to VNGenerateImageFeaturePrintRequestRevision1, even for newer builds (assuming that the Asset has an observation that was produced by an older build).
  • If an Asset observation was produced by an older revision, then the same revision must be used to perform the request on other Assets being compared. You could change your observe() function to also take the desired request revision.
  • All previously-produced Assset observations must be re-processed with the newer revision if working with the newer featureprint going forward is desired.

Wow, thanks for the quick reply. I should have mentioned that I thought of the data being from an old run too. To make sure it wasn't, I've repeatedly nuked all the data (even in iCloud) and started from a fresh install on my phone running iOS 17. The issue persists, and it happens whether I set the revision to 1 or 2.

Can't set VNGenerateImageFeaturePrintRequestRevision consistently, breaking computeDistance between two images
 
 
Q