Jake Alsemgeest
Jake Alsemgeest

Reputation: 702

How can you scan a UIImage for a QR code using Swift

I'm trying to read a QR code from a static image using Swift.

I can easily read it using a video source although it seems to be very different for images and I can't find too many resources online for this.

Any help appreciated, thanks.

Upvotes: 3

Views: 3481

Answers (2)

Inder Kumar Rathore
Inder Kumar Rathore

Reputation: 39978

func readQRCode(from image: UIImage) -> Result<String, QRReadingError> {
    guard let ciImage = CIImage(image: image) else {
        print("Couldn't create CIImage")
        return .failure(.generic)
    }

    guard let detector = CIDetector(
        ofType: CIDetectorTypeQRCode,
        context: nil,
        options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]
    ) else {
        print("Detector not intialized")
        return .failure(.generic)
    }
    let features = detector.features(in: ciImage)
    let qrCodeFeatures = features.compactMap { $0 as? CIQRCodeFeature }
    guard let qrCode = qrCodeFeatures.first?.messageString else {
        print("No QR code found in the image")
        return .failure(.qrCodeNotFoundInImage)
    }
    return .success(qrCode)
}

enum QRReadingError: Error {
    case generic
    case qrCodeNotFoundInImage
}

Upvotes: 3

Edison
Edison

Reputation: 11987

You can make a great QRCode scanner using ZXingObjC. It's a barcode image processing library designed to be used on both iOS devices and in Mac applications. It scans from live video or from images in your photo library and supports all the major QRCode formats.

This is only to get you started in the right direction. You'll need more methods to set up the camera etc. ZXingObjC includes sample projects and there are camera set up solutions all over SO so it's pretty straight forward.

You'll need to install ZXingObjC pods pod 'ZXingObjC' as well as create a bridging-header.h file of course to be able to use the ZXingObjC library.

ViewController.swift

import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

@IBOutlet weak var labelOutput: UILabel!
@IBOutlet weak var QRImage: UIImageView!

var imagePicker = UIImagePickerController()

// imagePicker delegate is itself (UIImagePickerController)
override func viewDidLoad() {
    super.viewDidLoad()
    imagePicker.delegate = self
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

@IBAction func scanQRCode(sender: AnyObject) {
    imagePicker.sourceType = .PhotoLibrary
    imagePicker.allowsEditing = false
    presentViewController(imagePicker, animated: true, completion: nil)
}

// set up the picker
// initialize luminance source, scanning algorithm, decoding of bitmap, reader helpers, decoder
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

    let placeHolderImage:UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage
    QRImage.contentMode = .ScaleAspectFit
    QRImage.image = placeHolderImage
    dismissViewControllerAnimated(true, completion: nil)

    let luminanceSource: ZXLuminanceSource = ZXCGImageLuminanceSource(CGImage: placeHolderImage.CGImage)
    let binarizer = ZXHybridBinarizer(source: luminanceSource)
    let bitmap = ZXBinaryBitmap(binarizer: binarizer)
    let hints: ZXDecodeHints = ZXDecodeHints.hints() as! ZXDecodeHints
    let QRReader = ZXMultiFormatReader()

    // throw/do/catch and all that jazz
    do {
        let result = try QRReader.decode(bitmap, hints: hints)
        labelOutput.text = result.text
    } catch let err as NSError {
        print(err)
    }
}

// Conform to ZXCaptureDelegate
func captureResult(capture: ZXCapture!, result: ZXResult!) {
    // do some stuff
    return
 }
}

One note: As of this post there is a known initializer error in the library's ZXParsedResult.m file. After installing the library the location of the file in Xcode is: Project -> Pods -> ZXingObjC -> All -> ZXParsedResult.m

On line 29 Change the Objective-C code

+ (id)parsedResultWithType:(ZXParsedResultType)type {
return [[self alloc] initWithType:type];
}

to

+ (id)parsedResultWithType:(ZXParsedResultType)type {
return [(ZXParsedResult *)[self alloc] initWithType:type];
}

Upvotes: 1

Related Questions