coco
coco

Reputation: 3136

Microblink: successfully reading an image

I'm asking the Microblink card reader to look at a photo of a card, rather than using the camera. I've tried both a portrait, and landscape version of a Mastercard. This is how I'm declaring my main variables:

let blinkCardRecognizer = MBCBlinkCardRecognizer()
var recognizerList = [MBCRecognizer]()

lazy var recognizerCollection: MBCRecognizerCollection = {
    blinkCardRecognizer.extractCvv = false
    blinkCardRecognizer.extractIban = false
    blinkCardRecognizer.extractExpiryDate = false

    recognizerList.append(blinkCardRecognizer)
    return MBCRecognizerCollection(recognizers: recognizerList)
}()
lazy var recognizerRunner: MBCRecognizerRunner = {
    MBCRecognizerRunner(recognizerCollection: recognizerCollection)
}()

I've declared these two delegates:

MBCBlinkCardOverlayViewControllerDelegate, MBCScanningRecognizerRunnerDelegate

After I get my UIImage, I call this function:

func prepareToReadImage(_ theImage: UIImage?) {
    recognizerRunner.scanningRecognizerRunnerDelegate = self
    
    var image: MBCImage? = nil
    if let anImage = theImage {
        image = MBCImage(uiImage: anImage)
    }
    image?.cameraFrame = true
    image?.orientation = MBCProcessingOrientation.down
    let _serialQueue = DispatchQueue(label: "com.microblink.DirectAPI-sample-swift")
    _serialQueue.async(execute: {() -> Void in
        self.recognizerRunner.processImage(image!)
    })
}

Here is my delegate callback. Every time, I am getting a status of .empty:

func recognizerRunner(_ recognizerRunner: MBCRecognizerRunner, didFinishScanningWith state: MBCRecognizerResultState) {
    if state == .valid { // values: .empty, .uncertain, .valid, .stageValid
        let result = blinkCardRecognizer.result
        
        DispatchQueue.main.async(execute: {() -> Void in
            print (result.owner)
            print (result.cardNumber)
            print (result.cardNumberPrefix)
        })
    }
}

Any reason why I'm not getting a .valid response? Also, does this look like the correct way to be pulling off the card details, once (if and when) I do? Thanks!

Upvotes: 2

Views: 138

Answers (2)

coco
coco

Reputation: 3136

Removing the following line allowed me to get a .valid response:

image?.cameraFrame = true

Upvotes: 1

mipar
mipar

Reputation: 196

Is this issue connected with the issue here?

If so, I can also suggest the following: You can add a func which calls the image picker (I am using the devices camera as the source type):

private func openImagePicker() {
        let imagePicker = UIImagePickerController()
        imagePicker.sourceType = .camera
        imagePicker.cameraDevice = .rear

        addOverlayLabel(toImagePicker: imagePicker)
        
        // Displays a control that allows the user to choose only photos
        imagePicker.mediaTypes = [kUTTypeImage as String]
        
        // Hides the controls for moving & scaling pictures, or for trimming movies.
        imagePicker.allowsEditing = false
        
        // Shows default camera control overlay over camera preview.
        imagePicker.showsCameraControls = true
        
        // set delegate
        imagePicker.delegate = self
        present(imagePicker, animated: true) {() -> Void in }
        imagePickerController = imagePicker
    }

In the func recognizerRunner, you can place the func to be called when the state is not valid, like this:

extension ViewController: MBCScanningRecognizerRunnerDelegate {

func recognizerRunner(_ recognizerRunner: MBCRecognizerRunner, didFinishScanningWith state: MBCRecognizerResultState) {
    DispatchQueue.main.async(execute: {() -> Void in
        if state != .valid {
            self.openImagePicker()
            return
        }
        
        print(state.rawValue)
        let result = self.blinkCardRecognizer.result

        print("Owner: \(result.owner)")
        print("Card number: \(result.cardNumber)")
        print("Card number prefix: \(result.cardNumberPrefix)")
        
    })
  }
}

This will give you a chance to re-take the image again if the state is not valid.

Also to ask, where are you placing the prepareToReadImage?

You need to place it in the func imagePickerController, like this:

extension ViewController: UIImagePickerControllerDelegate {

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String
    
    if CFStringCompare(mediaType as CFString?, kUTTypeImage, CFStringCompareFlags(rawValue: 9)) == .compareEqualTo {
        let originalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
        prepareToReadImage(originalImage)
    }
    
    picker.dismiss(animated: true, completion: nil)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    serialQueue.async {
        self.recognizerRunner?.resetState()
    }

    dismiss(animated: true, completion: nil)

  }
}

Lastly, make sure you're placing the settings of the recognizer before starting the scanning process.

I've added the func setupRecognizerRunner, where I'm defining everything you did in your block of code:

    private func setupRecognizerRunner() {

    blinkCardRecognizer = MBCBlinkCardRecognizer()
    var recognizers = [MBCRecognizer]()
    
    blinkCardRecognizer.extractCvv = false
    blinkCardRecognizer.extractIban = false
    blinkCardRecognizer.extractExpiryDate = false
    
    recognizers.append(blinkCardRecognizer)
    let recognizerCollection = MBCRecognizerCollection(recognizers: recognizers)
    recognizerRunner = MBCRecognizerRunner(recognizerCollection: recognizerCollection)
    
    recognizerRunner?.scanningRecognizerRunnerDelegate = self
}

And I've placed it in the viewDidLoad(), after the license key method has been loaded.

Upvotes: 3

Related Questions