onmyway133 / blog

🍁 What you don't know is what you haven't learned
https://onmyway133.com/
MIT License
669 stars 33 forks source link

How to detect Barcode and QR code #973

Open onmyway133 opened 3 weeks ago

onmyway133 commented 3 weeks ago

Before iOS 11, we used to use CIDetector and CIDetectorTypeQRCode to detect QR code

CIDetector

An image processor that identifies notable features, such as faces and barcodes, in a still image or video.

CIDetectorTypeQRCode

A detector that searches for Quick Response codes (a type of 2D barcode) in a still image or video, returning CIQRCodeFeature objects that provide information about detected barcodes.

func readQrOnly(image: CGImage) async -> String? {
    let ciImage = CIImage(cgImage: image)

    let detector = CIDetector(
        ofType: CIDetectorTypeQRCode,
        context: nil,
        options: [
            CIDetectorAccuracy: CIDetectorAccuracyHigh
        ]
    )

    guard
        let detector = detector
    else { return nil }

    let features = detector
        .features(in: ciImage)
        .compactMap { $0 as? CIQRCodeFeature }

    guard
        !features.isEmpty
    else { return nil }

    return features
        .compactMap(\.messageString)
        .joined(separator: " ")
}

In macOS 10.13, iOS 11, and tvOS 11 or later, the Vision framework replaces these classes for identifying and analyzing image features.

Using VNRequest, and particular VNDetectBarcodesRequest, we can detect barcodes in an image. It supports a bunch of code types, in form of VNBarcodeSymbology

func read(image: CGImage) async -> VNBarcodeObservation? {
    await withCheckedContinuation { con in
        do {
            let request = VNDetectBarcodesRequest(completionHandler: { request, error in
                if let request = request as? VNDetectBarcodesRequest,
                   let result = request.results?.first {
                    con.resume(returning: result)
                } else {
                    con.resume(returning: VNBarcodeObservation?.none)
                }
            })
            request.symbologies = try request.supportedSymbologies()
            request.revision = VNDetectBarcodesRequestRevision1

            let handler = VNImageRequestHandler(cgImage: image)
            try handler.perform([request])
        } catch {
            con.resume(returning: VNBarcodeObservation?.none)
        }
    }
}

Note we have to use VNDetectBarcodesRequestRevision1 for now since the default VNDetectBarcodesRequestRevision4 does not seem to work.

With VNBarcodeObservation, we can read payloadStringValue and symbology to get content and which code type it has detected