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
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.
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
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.
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.
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é!
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 =
Firebase ML Kit le fait pour iOS (et Android) avec leur API Vision sur l'appareil et surpasse Tesseract et SwiftOCR.
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!
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é