ios - googlenetplaces - machine learning kit apple
Conversión de Vision VNTextObservation en una cadena (8)
Estoy revisando la
documentación de la API Vision
de Apple y veo un par de clases relacionadas con la detección de texto en
UIImages
:
1)
class VNDetectTextRectanglesRequest
Parece que pueden detectar personajes, pero no veo un medio para hacer nada con los personajes.
Una vez que hayas detectado personajes, ¿cómo harías para convertirlos en algo que
NSLinguisticTagger
pueda interpretar?
Aquí hay una publicación que es una breve descripción de
Vision
.
Gracias por leer.
Agregando mi propio progreso en esto, si alguien tiene una mejor solución:
Dibujé con éxito el cuadro de región y los cuadros de caracteres en la pantalla. La visión API de Apple es realmente muy eficiente. Tienes que transformar cada fotograma de tu video en una imagen y alimentarlo al reconocedor. Es mucho más preciso que alimentar directamente el búfer de píxeles desde la cámara.
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])
}
}
Ahora estoy tratando de reconciliar el texto. Apple no proporciona ningún modelo OCR integrado. Y quiero usar CoreML para hacer eso, así que estoy tratando de convertir un modelo de datos capacitados de Tesseract a CoreML.
Puede encontrar modelos de Tesseract aquí: https://github.com/tesseract-ocr/tessdata y creo que el siguiente paso es escribir un convertidor de coremltools que admita ese tipo de entrada y salida de un archivo .coreML.
O bien, puede vincular directamente a TesseractiOS e intentar alimentarlo con sus cuadros de región y cuadros de caracteres que obtiene de la API de Vision.
Apple finalmente actualizó Vision para hacer OCR. Abra un patio de juegos y descargue un par de imágenes de prueba en la carpeta Recursos. En mi caso, los llamé "demoDocument.jpg" y "demoLicensePlate.jpg".
La nueva clase se llama
VNRecognizeTextRequest
.
Vuelca esto en un patio de juegos y dale un giro:
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)
Hay una discusión en profundidad de esto de WWDC19
Asi es como se hace ...
//
// 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.
}
}
¡Encontrarás que el proyecto completo here incluido es el modelo entrenado!
Estoy usando el motor Tesseract OCR de Google para convertir las imágenes en cadenas reales. Tendrás que agregarlo a tu proyecto Xcode usando cocoapods. Aunque Tesseract realizará OCR incluso si simplemente le alimenta la imagen que contiene textos, la forma de hacerlo funcionar mejor / más rápido es usar los rectángulos de texto detectados para alimentar piezas de la imagen que realmente contienen texto, que es donde se encuentra el Marco de visión de Apple Viene muy bien. Aquí hay un enlace al motor: Tesseract OCR Y aquí hay un enlace a la etapa actual de mi proyecto que ya tiene implementada la detección de texto + OCR: Out Loud - Camera to Speech Espero que estos puedan ser de alguna utilidad. ¡Buena suerte!
Firebase ML Kit lo hace para iOS (y Android) con su API de visión en el dispositivo y supera a Tesseract y SwiftOCR.
Gracias a un usuario de GitHub, puede probar un ejemplo: 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);
}
}
La cuestión es que el resultado es una matriz de cuadros delimitadores para cada carácter detectado. Por lo que deduje de la sesión de Vision, creo que se supone que debes usar CoreML para detectar los caracteres reales.
Charla recomendada de WWDC 2017: Marco de visión: Construir en Core ML (tampoco he terminado de verlo), eche un vistazo a 25:50 para ver un ejemplo similar llamado MNISTVision
Aquí hay otra aplicación ingeniosa que demuestra el uso de Keras (Tensorflow) para la capacitación de un modelo MNIST para el reconocimiento de escritura a mano usando CoreML : Github
Para aquellos que todavía buscan una solución, escribí una library rápida para hacer esto. Utiliza tanto la API de Vision como Tesseract y puede usarse para lograr la tarea que la pregunta describe con un solo método:
func sliceaAndOCR(image: UIImage, charWhitelist: String, charBlackList: String = "", completion: @escaping ((_: String, _: UIImage) -> Void))
Este método buscará texto en su imagen, devolverá la cadena encontrada y una porción de la imagen original que muestra dónde se encontró el texto
SwiftOCR
Acabo de hacer que SwiftOCR trabaje con pequeños conjuntos de texto.
https://github.com/garnele007/SwiftOCR
usos
https://github.com/Swift-AI/Swift-AI
que usa el modelo NeuralNet-MNIST para el reconocimiento de texto.
TODO: VNTextObservation> SwiftOCR
Publicaré un ejemplo de esto usando VNTextObservation una vez que tenga uno conectado al otro.
OpenCV + Tesseract OCR
Intenté usar OpenCV + Tesseract pero obtuve errores de compilación y luego encontré SwiftOCR.
VEA TAMBIÉN: Google Vision iOS
Nota Reconocimiento de texto de Google Vision: el SDK de Android tiene detección de texto pero también tiene cocoapod iOS. Esté atento, ya que eventualmente debería agregar reconocimiento de texto al iOS.
https://developers.google.com/vision/text-overview
// Corrección: lo probé pero solo la versión de Android del SDK admite la detección de texto.
https://developers.google.com/vision/text-overview
Si se suscribe a lanzamientos: https://libraries.io/cocoapods/GoogleMobileVision
Haga clic en SUSCRIBIRSE A LOS LANZAMIENTOS que puede ver cuando TextDetection se agrega a la parte de iOS de Cocoapod