Saltar al contenido →

Detectar texto en una imagen con Vision

Publicado originalmente en Apple Coding

El uso de técnicas de Machine Learning ha crecido de manera exponencial de un tiempo a esta parte, pasando de ser algo reservado a unos pocos, a estar al alcance de la mayoría gracias a librerías como TensorFlow o Keras y frameworks como CoreML de Apple y MLKit de Google.

Una de las disciplinas con mayor número de crecimiento es el Deep Learning, más concretamente, el reconocmiento y/o clasificación de imágenes.

En esta guía veremos como Apple nos brinda su ayuda en este menester mediante el framework Vision.

¿Qué es Vision?

Vision, a parte de ser el androide supérheroe sintético que forma parte de Los Vengadores, es el nombre que recibe uno de los frameworks desarrollados por Apple para que los programadores podamos aplicar Machine Learning a nuestras apps.

Construido sobre CoreML y Core Graphics, Vision nos da las herramientas necesarias para trabajar con imagenes y…

  • Encontrar caras y sus características
  • Rectángulos
  • Seguimiento de objetos (Tracking)
  • Detección de Códigos de Barras (En diferentes formatos)
  • Situar la línea del horizonte
  • Alineación de imágenes
  • Análisis de imágenes con Machine Learning
  • Encontrar texto

En nuestro caso vamos a dedicarnos a encontrar texto dentro de las imágenes y mediante CoreML averiguar que texto es.

BiciMAD. Caso Práctico

Vamos a desarrollar una app que nos permita leer los números de identificación de las bicicletas del servicio BiciMAD, de la ciudad de Madrid.

En este caso la app tendrá que hacer lo siguiente:

  1. Cargar una imagen
  2. Buscar texto
  3. Extraer las imágenes correspodientes a cada uno de los cuatro números
  4. Pasar esas imágenes al modelo de CoreML
  5. Presentarle al usuario al número que ve en la bici

Pues manos a la obra

Necesitamos imágenes.

Para poder probar la app vamos a necesitar imágenes de los códigos de las bicis, así que lo primero es importarlas al simulador de iOS.

En este enlace encontraréis un pequeño conjunto de imágenes con los que podréis hacer las pruebas.

Una vez que tengais descargadas las imágenes en vuestro ordenador, arrancad el simulador y abrir la app Photos. Para importar las imágenes basta con arrastrar los archivos de las fotos hasta el simulador. ¡Ya está!

Vale, ya tengo las imágenes. ¿Ahora qué?

Ahora abrir Xcode y creamos un proyecto de tipo Single View. Para seleccionar las imágenes que acabamos de importar en el simulador nos valdremos de UIImagePickerController.

A nuestro view controller le añadimos un UIButton que nos servirá para presentar el UIImagePickerController.

En cuanto selecciones una imagen el delegado del picker controller podremos cogerla desde la función imagePickerController(_:didFinishPickingMediaWithInfo:) Esta función recibe como uno de sus parámetros un diccionarion en el que se encuentran valores como la ruta al archivo de la imagen o la imagen en si misma.

Lo primero que hacemos es que de verdad tenemos una imagen y que esta la podemos convertir a una CIImage o una CGImage (no amigos, a CoreML no le gustan las UIImage) (1)

Con la imagen ya covertida lanzamos una solicitud de análisis sobre la image (2) y le decimos a Vision que lo que esperamos encontrar es texto (3)

Supongo que te preguntarás que qué es ese self.requestText que se le pasa a la función perform. Pues eso es la petición de búsqueda de texto que le pasamos a Vision

La clase VNDetectTextRectanglesRequest recibe como parámetro un closure de tipo VNRequestCompletionHandler, que se traduce en (request: VNRequest, error: Error?) -> Void

El hecho de que en lugar de incluir el código como un trailing closure se debe a que la funcionalidad es un tanto extensa, por lo que por claridad del código lo ponemos en un función con cumple con la firma de VNRequestCompletionHandler

Fijaos en la propiedad reportCharacterBoxes, se usa para indicarle a Vision que queremos, o no, que nos informe además de cada uno de los caracteres que encuentre en el texto. Como el modelo espera como entrada un sólo dígito le asignamos un valor de true

¿Eso es todo? ¡Qué fácil!

No amigos, esto no ha hecho más que empezar, lo único que hemos hecho es llamar la atención de Vision sobre la imagen.

Tenemos que decirle a Vision que queremos hacer cuando encuentre texto, si lo encuetra, en la imagen, y para eso tenemos que programarlo dentro de la función handleTextDetection

Lo primero que tenemos que hacer es comprobar que tenemos todo los necesario para poder convertir esa foto en números

Cuando sepamos que tenemos resultados válidos, y que esos resultados tienen el formato esperado (4 caracteres) nos preparamos para procesar cada uno de esos caracteres

La clase Visionary es un wrapper que nos facilita el uso del modelo de CoreML. Ya estamos listos para empezar a procesar cada uno de los caracteres encontrados, así que vamos a ver que tenemos que hacer.

Lo primero es traducir las coordenas de las observación a coordenadas sobre la imagen. La app que acompaña al artículo incluye una función showMarker que muestra sobre la imagen los rectángulos tanto para la palabra completa (en color rojo) como para cada uno de los caracteres (en color azul)

Con las coordenadas para el caracter, cortamos la porción de imagen en la que se encuentra y aplicamos una serie de filtros sobre la imagen

La función cropped es la encargada de recortar, el filtro CIColorInvert realiza una inversión de color, CIPerspectiveCorrection corrige la perspectiva de la imagen para ponerla de frente y por último CIColorControls pone la imagen en blanco y negro.

Con todo esto ya tenemos una imagen tal y como la espera el modelo.

Ya tengo las imágenes listas ¿Hemos terminado?

Nos queda muy poco, ahora que tenemos las 4 imagenes, una para caracter, se las pasamos a la función prediction de la clase Visionary. Desde ahora esa clase va a ser la encargada de tratar con el modelo y clasificar las imágenes

visionary.prediction(images: boxes)

Para cada una de las imágenes tenemos que hacer una solicitud de procesado de imagen (sí, otra vez) pero en este caso lo que queremos que ejecute es un VNCoreMLRequest, es decir, una llamada directa a nuestro modelo para que nos devuelva una predicción o una clasificación, dependiendo del tipo de modelo. Dicho lisa y llanamente, los que nos va a decir VNCoreMLRequest es si la imagen es un 3, un 5 o un 8.

¡Pero dónde está el resultado!

Paciencia joven Padawan, que ya hemos terminado, si te fijas en el código anterior, verás que VNCoreMLRequest también tiene un closure como parámetro que se invoca cuando el modelo devuelve su resultado

El parámetro request contiene una array de observaciones, almacenadas en la propiedad results, que son las probabilidades para cada uno de las clases (números) del modelo

Cuando selecciono el primer valor de results estoy cogiendo el número con más probabilidades de coincidir con la imagen según lo ha interpretado el modelo.

Una reflexión final

Hay que terner clara una cosa, Vision aplicado a la detección de texto se limita a eso, a detectar texto, no esperéis que por arte de magía una variable de tipo String tenga el contenido del texto. Para ello es necesario un modelo de Machine Learning.

Pero que esto no nos haga pensar que Vision no es tan bueno como parece, todo lo contrario, que en nuestro bolsillo podamos llevar tal capacidad de análisis de imágenes de tipo tan variado es todo un lujo.

En este repositorio de GitHub está el proyecto Xcode junto las imágenes para pruebas

Publicado en CoreML iOS Machine Learning Swift

Los comentarios están cerrados.