web-dev-qa-db-fra.com

Conversion d'une vision VNTextObservation en chaîne

Je regarde dans la documentation de Vision API de Apple et je vois quelques classes relatives à la détection de texte dans UIImages:

1) class VNDetectTextRectanglesRequest

2) class VNTextObservation

On dirait qu'ils peuvent détecter les caractères, mais je ne vois pas le moyen de faire quoi que ce soit avec les caractères. Une fois que vous avez détecté des caractères, comment voulez-vous les transformer en quelque chose qui puisse être interprété par NSLinguisticTagger ?

Voici un article qui est un bref aperçu de Vision .

Merci pour la lecture.

52
Adrian

Apple a finalement mis à jour Vision pour faire de la ROC. Ouvrez une cour de récréation et déposez quelques images de test dans le dossier Ressources. Dans mon cas, je les ai appelés "demoDocument.jpg" et "demoLicensePlate.jpg".

La nouvelle classe s'appelle VNRecognizeTextRequest. Dump this dans un terrain de jeu et donnez-lui un tourbillon:

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)

Il y a un discussion en profondeur de cela depuis WWDC19

2
Adrian

SwiftOCR

Je viens juste de faire travailler SwiftOCR avec de petits ensembles de texte.

https://github.com/garnele007/SwiftOCR

les usages

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

qui utilise le modèle NeuralNet-MNIST pour la reconnaissance de texte.

TODO: VNTextObservation> SwiftOCR

J'en posterai un exemple en utilisant VNTextObservation une fois que je l'aurai connecté à l'autre.

OpenCV + Tesseract OCR

J'ai essayé d'utiliser OpenCV + Tesseract mais des erreurs de compilation ont ensuite été trouvées par SwiftOCR.

VOIR AUSSI: Google Vision iOS

Remarque Reconnaissance de texte Google Vision - Android sdk détecte le texte, mais possède également un cocoapod iOS. Surveillez-le afin de pouvoir éventuellement ajouter une reconnaissance de texte à iOS.

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

// Correction: je viens de l'essayer, mais seulement Android du sdk prend en charge la détection de texte.

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

Si vous vous abonnez aux versions: https://libraries.io/cocoapods/GoogleMobileVision

Cliquez sur SUBSCRIBE TO RELEASES pour voir quand TextDetection est ajouté à la partie iOS du Cocoapod.

12
brian.clear

Ajout de mes propres progrès à ce sujet, si quelqu'un a une meilleure solution:

J'ai réussi à dessiner la zone et les zones de caractères à l'écran. L’API de vision de Apple est en fait très performant. Vous devez transformer chaque image de votre vidéo en image et l’envoyer au dispositif de reconnaissance. C’est beaucoup plus précis que d’alimenter directement le tampon de pixels de la vidéo. caméra.

 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])
        }
    }

Maintenant, j'essaie de reconstituer le texte. Apple ne fournit aucun modèle d'OCR intégré. Et je veux utiliser CoreML pour le faire. C'est pourquoi j'essaie de convertir un modèle de données formé par Tesseract en CoreML.

Vous pouvez trouver les modèles Tesseract ici: https://github.com/tesseract-ocr/tessdata et je pense que l'étape suivante consiste à écrire un convertisseur coremltools prenant en charge ce type d'entrée et de sortie .coreML fichier.

Vous pouvez également vous connecter directement à TesseractiOS et essayer de l’alimenter avec les zones de votre région et celles que vous recevez de l’API Vision.

10
Dimillian

Voici comment le faire ...

    //
//  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'S 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'S DO IT
        self.doOCR(ciImage: inputImage!)


    }

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

Vous trouverez le projet complet ici inclus est le modèle formé!

9
DrNeurosurg

Grâce à un utilisateur de GitHub, vous pouvez tester un exemple: 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);
    }
}

Le résultat est que le résultat est un tableau de boîtes englobantes pour chaque caractère détecté. D'après ce que j'ai compris de la session de Vision, je pense que vous êtes censé utiliser CoreML pour détecter les caractères réels.

Conférence WWDC 2017 recommandée: Vision Framework: Construire à partir du noyau ML (n’a pas fini de le regarder non plus), jetez un oeil à 25:50 pour un exemple similaire appelé MNISTVision

Voici une autre application astucieuse démontrant l'utilisation de Keras (Tensorflow) pour l'apprentissage d'un modèle MNIST pour la reconnaissance de l'écriture manuscrite à l'aide de CoreML : Github =

3
nathan

Firebase ML Kit le fait pour iOS (et Android) avec leur API Vision sur l'appareil et surpasse Tesseract et SwiftOCR.

2
Foti Dim

J'utilise le moteur OCR Tesseract de Google pour convertir les images en chaînes réelles. Vous devrez l'ajouter à votre projet Xcode à l'aide de cocoapodes. Bien que Tesseract exécute l’OCR même si vous alimentez simplement l’image contenant du texte, la meilleure façon de la rendre plus performante/plus rapide consiste à utiliser les rectangles de texte détectés pour alimenter des éléments de l’image contenant réellement du texte. est très pratique. Voici un lien vers le moteur: Tesseract OCR Et voici un lien vers l'étape en cours de mon projet dans lequel la détection de texte + OCR est déjà implémentée: Out Loud - Camera to Speech Hope ceux-ci peuvent être utiles. Bonne chance!

1
Andre Guerra

Pour ceux qui recherchent encore une solution, j’ai écrit un rapide bibliothèque pour le faire. Il utilise à la fois l'API Vision et Tesseract et peut être utilisé pour réaliser la tâche décrite dans la question avec une seule méthode:

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

Cette méthode cherchera du texte dans votre image, retournera la chaîne trouvée et une tranche de l'image originale montrant où le texte a été trouvé

1
Roberto Ferraz