Как стать автором
Обновить

Сканирование документов и распознавание текстов с помощью VisionKit и Vision Framework на iOS

Время на прочтение4 мин
Количество просмотров5K
Сегодня отсканируем документ и выведем на экран распознанный текст из этого документа. Для этого не нужно устанавливать дополнительные библиотеки: пригодятся VisionKit для сканирования и Vision для распознавания текстов.



Для начала убедитесь что у вас стоит Xcode 11 и iOS 13, потом создайте новый проект с поддержкой Storyboard.

Сканирование совершим с помощью видеокамеры. Значит нам нужно добавить NSCameraUsageDescription в Info.plist, без этого приложения будет аварийно завершаться.



Сканирование


Для сканирования документов мы используем VisionKit Framework. Чтобы открыть экран для сканирования, надо создать новый образец из VNDocumentCameraViewController и вывести:

let scanner = VNDocumentCameraViewController()
scanner.delegate = self
present(scanner, animated: true)

Добавьте VNDocumentCameraViewControllerDelegate во ViewController:

class ViewController: UIViewController, VNDocumentCameraViewControllerDelegate {
...

После нажатия «Отмены» или ошибки нужно закрыть открытый экран:

func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
  controller.dismiss(animated: true)
}

func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
  controller.dismiss(animated: true)
}

После сканирования и нажатия на «Сохранить» сработает следующее:

func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
  for i in 0 ..< scan.pageCount {
    let img = scan.imageOfPage(at: i)
    // recognizeText(inImage: img)
  }
  controller.dismiss(animated: true)
}

Каждую страницу можно обработать по отдельности.

Распознавание текстов


Со сканированием разобрались, теперь извлечем текст.

Чтобы все прошло плавно, распознавание проведем в фоновом режиме. Для этого нужно создать DispatchQueue:

lazy var workQueue = {
  return DispatchQueue(label: "workQueue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
}()

Для распознавания нам нужен VNImageRequestHandler с картинкой и VNRecognizeTextRequest с опциями recognitionLevel, customWords, recognitionLanguages, а также обработчик завершения, который даст результат в текстовом виде. По завершении собираем лучшие варианты текстов и выводим на экран:

lazy var textRecognitionRequest: VNRecognizeTextRequest = {
    let req = VNRecognizeTextRequest { (request, error) in
        guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
        
        var resultText = ""
        for observation in observations {
            guard let topCandidate = observation.topCandidates(1).first else { return }
            resultText += topCandidate.string
            resultText += "\n"
        }
        
        DispatchQueue.main.async {
            self.txt.text = resultText
        }
    }
    return req
}()

VNImageRequestHandler:

func recognizeText(inImage: UIImage) {
    guard let cgImage = inImage.cgImage else { return }
    
    workQueue.async {
        let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
        do {
            try requestHandler.perform([self.textRecognitionRequest])
        } catch {
            print(error)
        }
    }
}

Последняя версия ViewController


import UIKit
import Vision
import VisionKit

class ViewController: UIViewController, VNDocumentCameraViewControllerDelegate {
    @IBOutlet weak var txt: UITextView!
    
    lazy var workQueue = {
        return DispatchQueue(label: "workQueue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
    }()
    
    lazy var textRecognitionRequest: VNRecognizeTextRequest = {
        let req = VNRecognizeTextRequest { (request, error) in
            guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
            
            var resultText = ""
            for observation in observations {
                guard let topCandidate = observation.topCandidates(1).first else { return }
                resultText += topCandidate.string
                resultText += "\n"
            }
            
            DispatchQueue.main.async {
                self.txt.text = self.txt.text + "\n" + resultText
            }
        }
        return req
    }()

    @IBAction func startScan(_ sender: Any) {
        txt.text = ""
        
        let scanner = VNDocumentCameraViewController()
        scanner.delegate = self
        present(scanner, animated: true)
    }
    
    func recognizeText(inImage: UIImage) {
        guard let cgImage = inImage.cgImage else { return }
        
        workQueue.async {
            let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
            do {
                try requestHandler.perform([self.textRecognitionRequest])
            } catch {
                print(error)
            }
        }
    }
    
    // MARK: - Document Camera VC Delegate
    
    func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
        for i in 0 ..< scan.pageCount {
            let img = scan.imageOfPage(at: i)
            recognizeText(inImage: img)
        }
        
        controller.dismiss(animated: true)
    }
    
    func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
        controller.dismiss(animated: true)
    }
    
    func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
        print(error)
        controller.dismiss(animated: true)
    }
    
}




Что дальше?


Документация:
developer.apple.com/documentation/vision
developer.apple.com/documentation/visionkit

WWDC видео о Speech framework:
developer.apple.com/videos/all-videos/?q=Vision

GitHub проект:
github.com/usenbekov/vision-demo
Теги:
Хабы:
+4
Комментарии0

Публикации

Изменить настройки темы

Истории

Работа

iOS разработчик
23 вакансии
Swift разработчик
32 вакансии

Ближайшие события