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

Сегодня отсканируем документ и выведем на экран распознанный текст из этого документа. Для этого не нужно устанавливать дополнительные библиотеки: пригодятся 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) } }

Что дальше

0
2 комментария
Елена Черкасова

Как вариант бюджетный - фотографировать текст сразу в гугл перерводчике, За качество не отвечаю) А если уж хорошо надо, то лучше человека специально обученного нанять за денежку.

Ответить
Развернуть ветку
Ivan Vavilov

Пока самое главное ограничение — нет поддержки русского языка.

Ответить
Развернуть ветку
-1 комментариев
Раскрывать всегда