Подтвердить что ты не робот

Преобразование видения VNTextОбразование в строку

Я просматриваю Apple документацию по API Vision, и я вижу пару классов, которые относятся к обнаружению текста в UIImages:

1) class VNDetectTextRectanglesRequest

2) class VNTextObservation

Похоже, что они могут обнаруживать символы, но я не вижу возможности ничего делать с персонажами. Как только вы обнаружите символы, как бы вы могли превратить их во что-то, что может быть интерпретировано NSLinguisticTagger?

Вот сообщение, которое представляет собой краткий обзор Vision.

Спасибо за чтение.

4b9b3361

Ответ 1

Apple наконец-то обновила Vision, чтобы сделать OCR. Откройте игровую площадку и поместите пару тестовых изображений в папку "Ресурсы". В моем случае я назвал их "demoDocument.jpg" и "demoLicensePlate.jpg".

Новый класс называется VNRecognizeTextRequest. Сбросьте это на игровой площадке и поверните его:

import Vision

enum DemoImage: String {
    case document = "demoDocument"
    case licensePlate = "demoLicensePlate"
}

class OCRReader {
    func performOCR(on url: URL?, recognitionLevel: VNRequestTextRecognitionLevel)  {
        guard let url = url else { return }
        let requestHandler = VNImageRequestHandler(url: url, options: [:])

        let request = VNRecognizeTextRequest  { (request, error) in
            if let error = error {
                print(error)
                return
            }

            guard let observations = request.results as? [VNRecognizedTextObservation] else { return }

            for currentObservation in observations {
                let topCandidate = currentObservation.topCandidates(1)
                if let recognizedText = topCandidate.first {
                    print(recognizedText.string)
                }
            }
        }
        request.recognitionLevel = recognitionLevel

        try? requestHandler.perform([request])
    }
}

func url(for image: DemoImage) -> URL? {
    return Bundle.main.url(forResource: image.rawValue, withExtension: "jpg")
}

let ocrReader = OCRReader()
ocrReader.performOCR(on: url(for: .document), recognitionLevel: .fast)

Там всестороннее обсуждение этого от WWDC19

Ответ 2

SwiftOCR

Я просто получил SwiftOCR для работы с небольшими наборами текста.

https://github.com/garnele007/SwiftOCR

использует

https://github.com/Swift-AI/Swift-AI

который использует модель NeuralNet-MNIST для распознавания текста.

TODO: VNTextObservation > SwiftOCR

Будет опубликован пример с помощью VNTextObservation, когда я подключу его к другому.

OpenCV + Tesseract OCR

Я попытался использовать OpenCV + Tesseract, но получил ошибки компиляции, а затем нашел SwiftOCR.

СМОТРИТЕ ТАКЖЕ: Google Vision iOS

Примечание. Распознавание текста Google Vision - Android sdk имеет обнаружение текста, но также имеет iOS cocoapod. Поэтому следите за ним, так как в конечном итоге следует добавить распознавание текста в iOS.

https://developers.google.com/vision/text-overview

//Исправление: просто попробовал, но только Android-версия sdk поддерживает обнаружение текста.

https://developers.google.com/vision/text-overview

Если вы подписаны на выпуски: https://libraries.io/cocoapods/GoogleMobileVision

Нажмите ПОДПИСКА НА РЕЛИЗЫ вы можете видеть, когда TextDetection добавляется в iOS-часть Cocoapod

Ответ 3

Добавление моего собственного прогресса в этом, если у кого есть лучшее решение:

Я успешно нарисовал окно области и символьные поля на экране. Визуальный API Apple фактически очень эффективен. Вы должны преобразовать каждый кадр вашего видео в изображение и передать его распознавателю. Это гораздо более точная, чем прямое подавление пиксельного буфера с камеры.

 if #available(iOS 11.0, *) {
            guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {return}

            var requestOptions:[VNImageOption : Any] = [:]

            if let camData = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, nil) {
                requestOptions = [.cameraIntrinsics:camData]
            }

            let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer,
                                                            orientation: 6,
                                                            options: requestOptions)

            let request = VNDetectTextRectanglesRequest(completionHandler: { (request, _) in
                guard let observations = request.results else {print("no result"); return}
                let result = observations.map({$0 as? VNTextObservation})
                DispatchQueue.main.async {
                    self.previewLayer.sublayers?.removeSubrange(1...)
                    for region in result {
                        guard let rg = region else {continue}
                        self.drawRegionBox(box: rg)
                        if let boxes = region?.characterBoxes {
                            for characterBox in boxes {
                                self.drawTextBox(box: characterBox)
                            }
                        }
                    }
                }
            })
            request.reportCharacterBoxes = true
            try? imageRequestHandler.perform([request])
        }
    }

Теперь я пытаюсь пересоздать текст. Apple не предоставляет встроенную OCR-модель. И я хочу использовать CoreML для этого, поэтому я пытаюсь преобразовать обучаемую модель данных Tesseract в CoreML.

Здесь вы можете найти модели Tesseract: https://github.com/tesseract-ocr/tessdata, и я думаю, что следующим шагом будет написать конвертер coremltools, который поддерживает эти типы ввода и выводит файл .coreML.

Или вы можете напрямую связать себя с TesseractiOS и попытаться передать его полям вашего региона и символьным полям, которые вы получаете из API Vision.

Ответ 4

Вот как это сделать...

    //
//  ViewController.swift
//


import UIKit
import Vision
import CoreML

class ViewController: UIViewController {

    //HOLDS OUR INPUT
    var  inputImage:CIImage?

    //RESULT FROM OVERALL RECOGNITION
    var  recognizedWords:[String] = [String]()

    //RESULT FROM RECOGNITION
    var recognizedRegion:String = String()


    //OCR-REQUEST
    lazy var ocrRequest: VNCoreMLRequest = {
        do {
            //THIS MODEL IS TRAINED BY ME FOR FONT "Inconsolata" (Numbers 0...9 and UpperCase Characters A..Z)
            let model = try VNCoreMLModel(for:OCR().model)
            return VNCoreMLRequest(model: model, completionHandler: self.handleClassification)
        } catch {
            fatalError("cannot load model")
        }
    }()

    //OCR-HANDLER
    func handleClassification(request: VNRequest, error: Error?)
    {
        guard let observations = request.results as? [VNClassificationObservation]
            else {fatalError("unexpected result") }
        guard let best = observations.first
            else { fatalError("cant get best result")}

        self.recognizedRegion = self.recognizedRegion.appending(best.identifier)
    }

    //TEXT-DETECTION-REQUEST
    lazy var textDetectionRequest: VNDetectTextRectanglesRequest = {
        return VNDetectTextRectanglesRequest(completionHandler: self.handleDetection)
    }()

    //TEXT-DETECTION-HANDLER
    func handleDetection(request:VNRequest, error: Error?)
    {
        guard let observations = request.results as? [VNTextObservation]
            else {fatalError("unexpected result") }

       // EMPTY THE RESULTS
        self.recognizedWords = [String]()

        //NEEDED BECAUSE OF DIFFERENT SCALES
        let  transform = CGAffineTransform.identity.scaledBy(x: (self.inputImage?.extent.size.width)!, y:  (self.inputImage?.extent.size.height)!)

        //A REGION IS LIKE A "WORD"
        for region:VNTextObservation in observations
        {
            guard let boxesIn = region.characterBoxes else {
                continue
            }

            //EMPTY THE RESULT FOR REGION
            self.recognizedRegion = ""

            //A "BOX" IS THE POSITION IN THE ORIGINAL IMAGE (SCALED FROM 0... 1.0)
            for box in boxesIn
            {
                //SCALE THE BOUNDING BOX TO PIXELS
                let realBoundingBox = box.boundingBox.applying(transform)

                //TO BE SURE
                guard (inputImage?.extent.contains(realBoundingBox))!
                    else { print("invalid detected rectangle"); return}

                //SCALE THE POINTS TO PIXELS
                let topleft = box.topLeft.applying(transform)
                let topright = box.topRight.applying(transform)
                let bottomleft = box.bottomLeft.applying(transform)
                let bottomright = box.bottomRight.applying(transform)

                //LET CROP AND RECTIFY
                let charImage = inputImage?
                    .cropped(to: realBoundingBox)
                    .applyingFilter("CIPerspectiveCorrection", parameters: [
                        "inputTopLeft" : CIVector(cgPoint: topleft),
                        "inputTopRight" : CIVector(cgPoint: topright),
                        "inputBottomLeft" : CIVector(cgPoint: bottomleft),
                        "inputBottomRight" : CIVector(cgPoint: bottomright)
                        ])

                //PREPARE THE HANDLER
                let handler = VNImageRequestHandler(ciImage: charImage!, options: [:])

                //SOME OPTIONS (TO PLAY WITH..)
                self.ocrRequest.imageCropAndScaleOption = VNImageCropAndScaleOption.scaleFill

                //FEED THE CHAR-IMAGE TO OUR OCR-REQUEST - NO NEED TO SCALE IT - VISION WILL DO IT FOR US !!
                do {
                    try handler.perform([self.ocrRequest])
                }  catch { print("Error")}

            }

            //APPEND RECOGNIZED CHARS FOR THAT REGION
            self.recognizedWords.append(recognizedRegion)
        }

        //THATS WHAT WE WANT - PRINT WORDS TO CONSOLE
        DispatchQueue.main.async {
            self.PrintWords(words: self.recognizedWords)
        }
    }

    func PrintWords(words:[String])
    {
        // VOILA'
        print(recognizedWords)

    }

    func doOCR(ciImage:CIImage)
    {
        //PREPARE THE HANDLER
        let handler = VNImageRequestHandler(ciImage: ciImage, options:[:])

        //WE NEED A BOX FOR EACH DETECTED CHARACTER
        self.textDetectionRequest.reportCharacterBoxes = true
        self.textDetectionRequest.preferBackgroundProcessing = false

        //FEED IT TO THE QUEUE FOR TEXT-DETECTION
        DispatchQueue.global(qos: .userInteractive).async {
            do {
                try  handler.perform([self.textDetectionRequest])
            } catch {
                print ("Error")
            }
        }

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        //LETS LOAD AN IMAGE FROM RESOURCE
        let loadedImage:UIImage = UIImage(named: "Sample1.png")! //TRY Sample2, Sample3 too

        //WE NEED A CIIMAGE - NOT NEEDED TO SCALE
        inputImage = CIImage(image:loadedImage)!

        //LET DO IT
        self.doOCR(ciImage: inputImage!)


    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Вы найдете полный проект здесь включен обучаемая модель!

Ответ 5

Благодаря пользователю GitHub вы можете протестировать пример: https://gist.github.com/Koze/e59fa3098388265e578dee6b3ce89dd8

- (void)detectWithImageURL:(NSURL *)URL
{
    VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithURL:URL options:@{}];
    VNDetectTextRectanglesRequest *request = [[VNDetectTextRectanglesRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@", error);
        }
        else {
            for (VNTextObservation *textObservation in request.results) {
//                NSLog(@"%@", textObservation);
//                NSLog(@"%@", textObservation.characterBoxes);
                NSLog(@"%@", NSStringFromCGRect(textObservation.boundingBox));
                for (VNRectangleObservation *rectangleObservation in textObservation.characterBoxes) {
                    NSLog(@" |-%@", NSStringFromCGRect(rectangleObservation.boundingBox));
                }
            }
        }
    }];
    request.reportCharacterBoxes = YES;
    NSError *error;
    [handler performRequests:@[request] error:&error];
    if (error) {
        NSLog(@"%@", error);
    }
}

Дело в том, что результатом является массив ограничивающих блоков для каждого обнаруженного символа. Из того, что я собрал из сеанса Vision, я думаю, вы должны использовать CoreML для определения фактических символов.

Рекомендуемый разговор WWDC 2017: Концепция Vision: основываясь на Core ML (еще не закончил смотреть), посмотрите 25: 50 для аналогичного примера, называемого MNISTVision

Вот еще одно отличное приложение, демонстрирующее использование Keras (Tensorflow) для обучения модели MNIST для распознавания рукописного ввода с использованием CoreML: Github

Ответ 6

Firebase ML Kit делает это для iOS (и Android) с их API-интерфейсом Vision и превосходит Tesseract и SwiftOCR.

Ответ 7

Я использую механизм Google Tesseract OCR для преобразования изображений в реальные строки. Вы должны добавить его в свой проект Xcode, используя cocoapods. Хотя Tesseract будет выполнять OCR, даже если вы просто загружаете изображение, содержащее тексты, способ сделать его лучше/быстрее - использовать обнаруженные текстовые прямоугольники для подачи фрагментов изображения, которые фактически содержат текст, в котором Apple Vision Framework пригодится. Здесь ссылка на движок: Tesseract OCR И здесь уже добавлена ​​ссылка на текущий этап моего проекта, в котором есть функция обнаружения текста + OCR: Out Loud - Камера для речи Надеюсь, это может пригодиться. Удачи!

Ответ 8

Для тех, кто все еще ищет решение, я написал небольшую библиотеку, чтобы сделать это. Он использует как Vision API, так и Tesseract и может быть использован для решения задачи, описанной в вопросе, одним единственным методом:

func sliceaAndOCR(image: UIImage, charWhitelist: String, charBlackList: String = "", completion: @escaping ((_: String, _: UIImage) -> Void))

Этот метод будет искать текст в вашем изображении, возвращать найденную строку и фрагмент исходного изображения, показывающий, где был найден текст