Повышение точности распознавания текста при использовании iOS Vision Framework для сканирования документа

Я пытаюсь создать сканер документов, способный считывать текст с любого документа / карты. Однако иногда возникают проблемы с правильным распознаванием текста на кредитной карте. Точность неплохая, но, безусловно, есть возможности для улучшения. Я использовал фреймворк VisionTextRecognition и использовал все стандартные настройки, которые подходят для настройки распознавания текста.

Это то, что мне пришлось настроить для запроса распознавания текста

textRecognitionRequest = VNRecognizeTextRequest(completionHandler: { (request, error) in
            if let results = request.results, !results.isEmpty {
                if let requestResults = request.results as? [VNRecognizedTextObservation] {
                    var foundText = ""
                    for observation in recognizedText {
                        guard let candidate = observation.topCandidates(1).first else { continue }
                     foundText.append(candidate.string + "\n")
                    }
                }
            }
        }) 
        textRecognitionRequest.recognitionLevel = .accurate
        textRecognitionRequest.usesLanguageCorrection = true

Есть ли у кого-нибудь предложения по улучшению идентификации программным способом путем предварительной или последующей обработки сканирования в какой-то момент?

0 ответов

Вы ничего не можете сделать для повышения точности, кроме добавления некоторых предустановленных значений, которые нужно специально искать, что не имеет смысла с числами CC, поэтому я даже не буду показывать этот код. Вам нужно будет положиться на Apple, чтобы улучшить свою модель распознавания текста по мере того, как iOS повторяет ее, чтобы она действительно улучшилась.

А пока я предлагаю вам сделать две вещи:

  1. Проведите проверку номеров своих кредитных карт, которые, по вашему мнению, вы получаете. Например, Visa начинается с 4, MasterCard начинается с 5, Discover с 6, Amex с 3 и т. Д. Они имеют определенную длину и так далее. Смотрите здесь: https://www.freeformatter.com/credit-card-number-generator-validator.html

  2. Продолжайте повторять видео с камеры снова и снова, пока не получите подтверждающее число. Я не уверен, что вы сейчас просто фотографируете карту и обрабатываете это изображение (что, похоже, делаете вы), но вы должны обрабатывать много изображений в секунду, пока не получите действительный CC. Скорее всего, именно так Apple делает это при добавлении карты через Apple Pay на ваш телефон или при внесении чеков в цифровом виде с помощью банковских приложений (поиск действительных номеров маршрутизации и счетов).

Вот пример того, что я имею в виду...

Я написал этот код, который может выбирать и проверять номера ISBN (в основном 10- и 13-значные номера, которые каталогизируют книги, у которых есть контрольная цифра для проверки) в любом заданном тексте, и буду продолжать поиск, пока не найдет все номера, а затем подтвердит. Он работает очень хорошо и очень быстро. Посмотрите этот код Swift 5.3:

import UIKit
import Vision
import Photos
import AVFoundation

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
    
    var recognizedText = ""
    var finalText = ""
    var image: UIImage?
    var processing = false
    
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var setLabel: UILabel!
    @IBOutlet weak var numberLabel: UILabel!
    
    lazy var textDetectionRequest: VNRecognizeTextRequest = {
        let request = VNRecognizeTextRequest(completionHandler: self.handleDetectedText)
        request.recognitionLevel = .accurate
        request.usesLanguageCorrection = false
        return request
    }()
    
    private let videoOutput = AVCaptureVideoDataOutput()
    private let captureSession = AVCaptureSession()
    private lazy var previewLayer: AVCaptureVideoPreviewLayer = {
        let preview = AVCaptureVideoPreviewLayer(session: self.captureSession)
        preview.videoGravity = .resizeAspect
        return preview
    }()

    // MARK: AV
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.addCameraInput()
        self.addVideoOutput()
    }
    
    private func addCameraInput() {
        let device = AVCaptureDevice.default(for: .video)!
        let cameraInput = try! AVCaptureDeviceInput(device: device)
        self.captureSession.addInput(cameraInput)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.previewLayer.frame = self.view.bounds
    }
    
    private func addVideoOutput() {
        self.videoOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_32BGRA)] as [String : Any]
        self.videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "my.image.handling.queue"))
        self.captureSession.addOutput(self.videoOutput)
    }
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)
    {
        if !processing
        {
            guard let frame = CMSampleBufferGetImageBuffer(sampleBuffer) else {
                debugPrint("unable to get image from sample buffer")
                return
            }
            print("did receive image frame")
            // process image here
        
            self.processing = true
            
            let ciimage : CIImage = CIImage(cvPixelBuffer: frame)
            let theimage : UIImage = self.convert(cmage: ciimage)
            
            self.image = theimage
            processImage()
        }
    }

    // Convert CIImage to CGImage
    func convert(cmage:CIImage) -> UIImage
    {
         let context:CIContext = CIContext.init(options: nil)
         let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
         let image:UIImage = UIImage.init(cgImage: cgImage)
         return image
    }
    
    // AV
    
    func processImage()
    {
        DispatchQueue.main.async {
            self.nameLabel.text = ""
            self.setLabel.text = ""
            self.numberLabel.text = ""
        }
        
        guard let image = image, let cgImage = image.cgImage else { return }
        
        let requests = [textDetectionRequest]
        let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, orientation: .right, options: [:])
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                try imageRequestHandler.perform(requests)
            } catch let error {
                print("Error: \(error)")
            }
        }
    }
    
    fileprivate func handleDetectedText(request: VNRequest?, error: Error?)
    {
        self.finalText = ""
        
        if let error = error {
            print(error.localizedDescription)
            self.processing = false
            return
        }
        guard let results = request?.results, results.count > 0 else {
            print("No text was found.")
            self.processing = false
            return
        }

        if let requestResults = request?.results as? [VNRecognizedTextObservation] {
            self.recognizedText = ""
            for observation in requestResults {
                guard let candidiate = observation.topCandidates(1).first else { return }
                self.recognizedText += candidiate.string
                self.recognizedText += " "
            }
            
            var replaced = self.recognizedText.replacingOccurrences(of: "-", with: "")
            replaced = String(replaced.filter { !"\n\t\r".contains($0) })
            let replacedArr = replaced.components(separatedBy: " ")
            
            for here in replacedArr
            {
                let final = here.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

                if (final.count == 10 || final.count == 13) && final.containsISBNnums && Validate.isbn(final) // validate barcode
                {
                    self.finalText += final
                    print(final)
                    self.captureSession.stopRunning()
                    DispatchQueue.main.async {
                        self.previewLayer.removeFromSuperlayer()
                    }
                    break
                }
            }

            DispatchQueue.main.async {
                self.numberLabel.text = self.finalText
            }
        }
        
        self.processing = false
    }
    
    // MARK: Buttons

    // This is a live camera view that will start a capture session
    @IBAction func takePhoto(_ sender: Any) {
        self.view.layer.addSublayer(self.previewLayer)
        self.captureSession.startRunning()
    }
    
    @IBAction func choosePhoto(_ sender: Any) {
        presentPhotoPicker(type: .photoLibrary)
    }
    
    fileprivate func presentPhotoPicker(type: UIImagePickerController.SourceType) {
        let controller = UIImagePickerController()
        controller.sourceType = type
        controller.delegate = self
        present(controller, animated: true, completion: nil)
    }
}

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true, completion: nil)
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        dismiss(animated: true, completion: nil)
        image = info[.originalImage] as? UIImage
        processImage()
    }
}

extension String {
    var containsISBNnums: Bool {
        guard self.count > 0 else { return false }
        let nums: Set<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "X"]
        return Set(self).isSubset(of: nums)
    }
}
Другие вопросы по тегам