Сегодня мы будем сканировать документ и выводить на экран распознанный текст в этом документе. Для этого нам не нужно устанавливать дополнительные библиотеки. Мы используем VisionKit Framework для сканирования документа и Vision Framework для распознавания текстов.
Для начало убедитесь что у вас стоит Xcode 11 и iOS 13, потом создайте новый проект с поддержкой Storyboard.
Сканирование совершим с помощью видеокамеры. Значит нам нужно добавить NSCameraUsageDescription в Info.plist, без этого приложения будет аварийно завершаться.
Для сканирования документов мы используем VisionKit Framework. Чтобы открыть экран для сканирования нам надо создать новый образец из VNDocumentCameraViewController и показать это:
Добавьте VNDocumentCameraViewControllerDelegate в ViewController:
Когда нажимаем отмену или получаем ошибку нужно закрыть открытый экран:
Когда отсканировали и нажали сохранить сработает следующий метод:
Каждую страницу можно обработать по отдельности.
Со сканированием разобрались, теперь давайте извлечем текст из отсканированных рисунков.
Чтобы все пошло плавно, распознавание будем делать в заднем плане. Для этого нужно создать DispatchQueue:
Для распознавания нам нужен VNImageRequestHandler с картинкой и VNRecognizeTextRequest с опциями как recognitionLevel, customWords, recognitionLanguages, а также обработчик завершения, который даст результат в текстовом виде. При завершении, собираем самые лучшие варианты текстов и показываем на экране:
VNImageRequestHandler:
Документацию можно найти здесь:
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
Для начало убедитесь что у вас стоит 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