Post on 26-May-2020
Carlos Tirado García
César Domínguez Pérez y Jonathan Heras Vicente
Máster universitario en Tecnologías Informáticas
2015-2016
Título
Director/es
Facultad
Titulación
Departamento
TRABAJO FIN DE ESTUDIOS
Curso Académico
Gestión móvil de regiones de interés en imágenesdigitales
Autor/es
© El autor© Universidad de La Rioja, Servicio de Publicaciones,
publicaciones.unirioja.esE-mail: publicaciones@unirioja.es
Gestión móvil de regiones de interés en imágenes digitales, trabajo fin de estudiosde Carlos Tirado García, dirigido por César Domínguez Pérez y Jonathan Heras Vicente
(publicado por la Universidad de La Rioja), se difunde bajo una LicenciaCreative Commons Reconocimiento-NoComercial-SinObraDerivada 3.0 Unported.
Permisos que vayan más allá de lo cubierto por esta licencia pueden solicitarse a lostitulares del copyright.
Trabajo de Fin de Máster
Gestión móvil de regiones de interés en imágenes digitales
Autor:
Carlos Tirado García
Tutor/es: César Domínguez Pérez, Jónathan Heras Vicente
Fdo.:
MÁSTER:
Máster en Tecnologías Informáticas (853M)
Escuela de Máster y Doctorado
AÑOACADÉMICO:2015/2016
2
RESUMEN
OpenCV es una librería muy potente para el procesamiento de imágenes
digitales disponible en diversas plataformas, incluyendo Android e iOS. A partir
de una imagen, OpenCV proporciona una gran variedad de algoritmos para
detectar las regiones de interés (por ejemplo, las caras de una foto, las sinapsis
o dendritas de una neurona, o células tumorales dentro de un conjunto de
células). Sin embargo, hay ocasiones en las que las regiones proporcionadas
por OpenCV no son correctas y necesitan ser ajustadas. Desafortunadamente,
OpenCV no proporciona dicha funcionalidad. En este proyecto se aborda dicho
problema en el contexto de aplicaciones móviles. En concreto, el objetivo es
desarrollar una plataforma para aplicaciones móviles que permita gestionar
regiones de interés detectadas mediante OpenCV.
ABSTRACT
OpenCV is a powerful library for digital image processing available in several
platforms, including Android and iOS. From an image, OpenCV offers a variety
of algorithms for detecting the regions of interest (the faces in a photo, the
synapses or the dendrites of a neuron, or tumor cells). However, there are times
where in the regions provided by OpenCV are not correct and need to be
adjusted. Unfortunately, OpenCV does not provide this feature. In this project,
we address this issue in the context of the mobile apps. Specifically, the
objective is develop a platform for mobile apps that allows us to manage the
regions of interest detected with OpenCV.
3
Índice 1. Introducción .................................................................................................... 4
2. Plan de trabajo ............................................................................................... 6
3. Desarrollo del trabajo ..................................................................................... 8
3.1. Parte I: Gestión de regiones de interés en imágenes .............................. 9
3.1.1. Análisis ............................................................................................ 10
3.1.2. Diseño ............................................................................................. 14
3.1.3. Implementación ............................................................................... 16
3.1.4. Ejemplo de uso................................................................................ 18
3.2. Parte II: Integración con OpenCV .......................................................... 21
3.2.1. Análisis ............................................................................................ 21
3.2.2. Diseño ............................................................................................. 22
3.2.3. Implementación en iOS ................................................................... 24
3.2.4. Implementación en Android ............................................................. 25
3.2.5. Ejemplo de uso en React Native ..................................................... 28
3.3. Parte III: Caso de uso ............................................................................ 29
3.3.1. Análisis ............................................................................................ 30
3.3.2. Diseño ............................................................................................. 33
3.3.3. Implementación ............................................................................... 36
4. Conclusiones ................................................................................................ 45
5. Glosario ........................................................................................................ 46
6. Referencias bibliográficas ............................................................................ 47
4
1. Introducción
Se puede definir una región de interés (Region Of Interest, ROI) como un
subconjunto seleccionado de muestras dentro de un conjunto de datos
identificados para un propósito en particular. El concepto de ROI es
comúnmente usado en muchas áreas y podemos tener regiones de interés en
conjuntos de datos de distintas dimensiones.
En nuestro caso, vamos a tratar con conjuntos de datos de dos dimensiones,
las imágenes, una región de interés podría ser las caras de una foto, las
sinapsis o dendritas de una neurona, o células tumorales. En este caso
podemos tener regiones con distintas formas geométricas (rectangulares,
ovaladas, puntos…). En la siguiente imagen, por ejemplo, se puede ver un
ejemplo utilizado en medicina.
OpenCV [1] es una librería muy potente para el procesamiento de imágenes
digitales disponible en diversas plataformas, incluyendo Android e IOS.
A partir de una imagen, OpenCV proporciona una gran variedad de algoritmos
para detectar las regiones de interés, sin embargo, hay ocasiones en las que
las regiones proporcionadas por OpenCV no son correctas y necesitan ser
ajustadas. Desafortunadamente, OpenCV no proporciona dicha funcionalidad.
Si tomamos como ejemplo la detección de caras en una imagen, se puede
observar la necesidad de esta gestión de regiones. OpenCV nos devuelve las
caras detectadas en una imagen, pero puede que sea necesario añadir nuevas
caras (en caso de que algunas caras no se hayan detectado), eliminar (si la
detección indica regiones como caras pero no lo son) y/o modificarlas (por
5
ejemplo, se ha detectado una cara parcialmente o queremos etiquetar a una
persona con su nombre).
Existen alternativas en aplicaciones de escritorio, como por ejemplo ImageJ [2],
que nos permiten utilizar la salida de OpenCV y gestionar dichas regiones, pero
no existe una versión de esta aplicación para móvil. En este proyecto, se
aborda dicho problema en el contexto de aplicaciones móviles.
6
2. Plan de trabajo
El principal objetivo es desarrollar una plataforma para aplicaciones móviles
que permita gestionar regiones de interés detectadas mediante OpenCV. Esta
plataforma debe servir para dispositivos de distintos sistemas operativos,
incluyendo al menos, la compatibilidad con iOS y Android.
El problema surge como una necesidad del Departamento de Matemáticas y
Computación de la Universidad de La Rioja, inmerso en varios proyectos de
reconocimiento de imágenes, para los cuales tiene una solución para gestionar
las regiones de interés en aplicaciones de escritorio, careciendo por el
contrario, de la alternativa para dispositivos móviles.
Durante el periodo de prácticas (para la Fundación de la Universidad de La
Rioja) se analizaron las distintas alternativas para el caso que estábamos
tratando en el proyecto, realizando diversas pruebas sobre las distintas partes
de la aplicación.
Notas de la reunión inicial
Para este proyecto, se divide el trabajo en dos fases:
Desarrollo de una PoC (Proof of Concept): Primero, desarrollaremos una
aplicación sencilla en la que a partir de una imagen, mediante OpenCV,
detectemos las caras, de manera que, esto origine, una lista de cuadrados que
después gestionaremos a través del dispositivo móvil. Donde gestionar
significa, poder añadir, eliminar y modificar (posición o tamaño) de las regiones.
7
Desarrollo de la plataforma: Una vez realizada la prueba de concepto
descrita en el punto anterior, y tras haber comprobado que es viable la
realización de la aplicación, se generalizará la misma, para el uso de más tipos
de regiones de interés y algoritmos. Desarrollándose finalmente una aplicación
más completa como ejemplo de uso.
La duración total del proyecto se planifica en 300 horas, las cuales han sido
divididas proporcionalmente entre las distintas semanas durante las cuales se
realizará el desarrollo del mismo.
Para la gestión de las tareas se utilizará la herramienta Trello [3] a modo de
panel Kanban [4] con el fin de tenerlas organizadas, y poder ver visualmente y
de manera rápida la carga de trabajo existente en cada momento. Siendo cada
tarea clasificada según su ámbito o importancia.
8
3. Desarrollo del trabajo
Durante el periodo de prácticas, se realizaron una serie de pruebas de las
distintas partes que en principio iban a componer la aplicación, con el fin de
seleccionar las tecnologías a usar, y definir la integración entre las mismas.
Tomando como base esas pruebas, en un primer momento se realizó una
prueba concepto con el fin de confirmar la viabilidad de las tecnologías elegidas
inicialmente.
A partir del trabajo realizado, fue definida la siguiente arquitectura para la
plataforma a desarrollar:
Tal y como podemos ver en la imagen, la interfaz móvil será desarrollada
mediante la librería React Native [5], esta parte de la aplicación será la
encargada de seleccionar la imagen con la que se va a trabajar.
Una vez hecho esto, la imagen pasará a OpenCV, dónde será analizada,
utilizando diversos algoritmos, dando como resultado las regiones de interés.
Por último, estas regiones serán enviadas de nuevo a la interfaz móvil donde
podrán ser gestionadas.
Queda de esta manera el desarrollo del trabajo dividido en tres partes:
9
- Gestión de regiones de interés en imágenes
- Integración con OpenCV
- Caso de uso. Aplicación de ejemplo.
A continuación pasaremos a detallar cada una de ellas, justificando el uso de la
tecnología y la estructura elegida para su desarrollo en cada caso.
3.1. Parte I: Gestión de regiones de interés en imágenes
Siendo conscientes de que se trataba de una de las partes más importantes del
proyecto, fueron analizadas las posibles alternativas existentes para su
desarrollo, analizando también, las librerías o frameworks que ya existían en el
mercado, con el fin de determinar si cumplían con la función demandada.
Una de ellas, fue la plataforma ImageJ, que si bien en su roadmap existe una
versión para dispositivos móviles, actualmente no se encuentra publicada para
su uso. Además de esto, que esté desarrollada en Java hubiese hecho
complicado su utilización en dispositivos con sistema operativo iOS.
Finalmente, al no encontrar una librería que cumpliese con nuestras
necesidades, fueron evaluadas diferentes alternativas para realizar un
desarrollo a medida, en base a los siguientes requerimientos:
● La aplicación debía funcionar tanto en Android como en iOS.
● Debía poderse integrar con OpenCV ya que iba a ser el encargado del
procesado de las imágenes.
Se tuvieron en cuenta varias alternativas para este cometido, siendo las más
destacadas:
- Aplicaciones híbridas (Ionic [6] y PhoneGap [7]): El desarrollo se hace
normalmente como en los entornos web (HTML/JS/CSS) y las
aplicaciones son desplegadas en un contenedor nativo que brinda
acceso a las capacidades del dispositivo.
- Aplicaciones generadas (Titanium [8] y React Native): En este tipo de
aplicaciones se escribe una base común utilizando el lenguaje del
framework (JavaScript en los dos casos referenciados), pero a la hora
10
de generar la aplicación se hace con las herramientas nativas,
generando código nativo.
- Aplicaciones nativas: Consiste en desarrollar aplicaciones
independientes para cada plataforma utilizando el lenguaje nativo del
dispositivo.
Con el fin de reutilizar la mayor cantidad de código posible se descartó en un
primer momento el uso de los frameworks nativos. Una vez descartada esta
alternativa, y teniendo que elegir entre utilizar un framework híbrido y otro que
compila a nativo nos decantamos por la segunda opción, siendo factores
decisivos, el rendimiento y la facilidad para integrar módulos nativos (por
ejemplo, la integración con OpenCV).
Aunque Titanium se trataba de una plataforma con la que ya teníamos
experiencia en el desarrollo, finalmente fue React Native la elegida,
principalmente, por los siguientes motivos:
- No es un framework, es una librería de interfaces de usuario y nuestro
objetivo principal es manejar la interfaz de usuario (UI).
- Tiene su base centrada en componentes y en cambios de estado lo que
facilita nuestro trabajo.
- El soporte de la comunidad y desarrollo de módulos en este momento es
mayor para esta plataforma, y la tendencia es ascendente.
Aunque actualmente, el soporte oficial de Facebook (desarrollador de React
Native) es para Android e iOS, existe un port para Windows 10 desarrollado por
Microsoft, por lo que presumiblemente, sería fácilmente adaptable a otras
plataformas.
3.1.1. Análisis
En esta parte del proyecto, trataremos de manera general la gestión de las
regiones dentro de una imagen, sin particularizar en OpenCV.
Las tipos de regiones que van a ser soportados por la aplicación son los
siguientes: Rectángulo, Círculo, Línea y Punto.
11
RECTÁNGULO CÍRCULO LÍNEA PUNTO
Para cada uno de estos tipos de regiones, deberemos implementar los
siguientes casos de uso (a realizar sobre una imagen)
- Añadir una nueva región
- Eliminar una región
- Modificar el tamaño de una región (no aplicable para el Punto).
- Cambiar la posición de la región
Con el fin de comprender mejor cómo deben funcionar cada uno de los casos
de uso, se han creado prototipos de todos ellos.
Añadir una nueva región
A través de una acción, en principio un botón, podremos añadir una nueva
región del tipo configurado en la aplicación en ese momento. Esta se colocará
por defecto en el centro de la imagen.
12
Eliminar una región
Al hacer long press (presión prolongada), sobre cualquiera de las regiones
presentes en la imagen, aparecerá un diálogo de borrado, pidiéndonos
confirmación. En caso de elegir eliminar la región sobre la que estamos
realizando la acción, esta dejará de mostrarse.
Modificar el tamaño de una región
Al hacer double tap (pulsar dos veces) sobre una región, ésta pasará a estar
en modo resize. Mientras permanezca en este modo no se podrá cambiar la
posición. Arrastrando de cualquiera de los laterales modificaremos el tamaño
del elemento.
Si se arrastra desde cualquiera de las esquinas se redimensionarán los dos
laterales adyacentes de manera proporcional. Volviendo a hacer double tap
volveremos al estado anterior a iniciar la modificación del tamaño de la región,
finalizando, por tanto, la edición.
13
Cambiar la posición de la región
Dada una imagen, en la que se muestran las distintas regiones existentes
sobre la misma, cualquiera de las regiones puede ser arrastrada con el dedo
para cambiar su posición.
14
Para comprender mejor los estados por los que puede llegar a pasar una
región determinada dentro de una imagen, añadimos el siguiente diagrama de
estados:
3.1.2. Diseño
Se va a implementar un módulo, que nos permitirá gestionar los distintos tipos
de regiones que podremos añadir sobre una imagen. En la plataforma utilizada,
recordemos que estamos utilizando React Native, gestionaremos todo
mediante componentes por lo que tendremos que definir los necesarios para
que las regiones puedan ser mostradas.
La estructura de componentes definida es la siguiente:
15
Contaremos con un componente principal Region que será dónde
implementemos la mayor parte de la lógica, y que poseerá las siguientes
propiedades y métodos:
En lugar de utilizar herencia o mixins, utilizaremos la composición, basándonos
en el principio de CompositionOverInheritance [9], que hará que se favorezca la
reutilización de los componentes.
Tendremos los componentes RectRegion, CircleRegion, PointRegion y
LineRegion que utilizarán el componente Region con los parámetros
adecuados. Para ello, será necesario que le pasemos los estilos que definirán
las distintas formas.
Por último crearemos el componente ImageWithRegions que será el que
nos aporte los distintos métodos para la gestión de regiones sobre una imagen.
Este será el que se expondrá para poder utilizarse.
En el constructor junto con las propiedades propias de la imagen, le pasaremos
también las regiones iniciales en el caso de que quisiéramos que la imagen
poseyese alguna región ya al inicializarla. Además del constructor, poseerá los
siguientes métodos:
16
3.1.3. Implementación
Para la implementación de las regiones vamos a utilizar el componente View
de React Native, es decir, éstas serán tratadas como vistas. El componente
View es el componente más fundamental que nos aporta React para construir
interfaces de usuario, siendo un contenedor que soporta flexbox, estilos, control
de gestos y controles de accesibilidad y que está diseñado para poder contener
0 o varias de vistas de cualquier tipo. Los elementos equivalentes en las
distintas plataformas serían UIView (iOS), android.view (android) y <div>
(HTML). Utilizar este componente nos aporta mucha flexibilidad y toda la
potencia de las vistas. Además, esto nos va a permitir compartir el mismo
código tanto para Android como para iOS.
Para la gestión de gestos y movimientos utilizaremos el componente
PanResponder [10], el cual nos aporta una gran flexibilidad, debido a que
permite controlar los movimientos a muy bajo nivel.
Por ejemplo, para detectar, si se ha hecho un double tap tendremos el
siguiente código:
17
A continuación se muestra otro ejemplo, al finalizar un movimiento, en el cual
comprobamos si se ha tratado de un long press (pulsación prolongada) o no. Y
realizamos la acción adecuada en cada caso:
En el siguiente fragmento de código observamos cómo está implementada,
parcialmente, una región circular CircleRegion. Para cada una de las formas
tendremos que pasarle el estilo necesario para representarla.
18
Será publicado como módulo NPM (librería compatible con gestor de paquetes
de Node.js), con el objetivo de poder utilizarlo fácilmente en nuestro proyecto.
Para ello seguiremos las instrucciones para crear un paquete de NPM que
podemos encontrar en: https://docs.npmjs.com/misc/developers
3.1.4. Ejemplo de uso
En el siguiente código podemos observar cómo utilizamos una imagen a través
de url, en la que mostramos dos regiones, un círculo y un cuadrado. El círculo
es pasado como parámetro en el momento de inicialización de la imagen,
mientras que el cuadrado lo pasamos cuando se pulsa el botón Añadir. En
React para añadir nuevos elementos lo hacemos mediante un cambio de
estado, ya que volverá a ejecutar el método render y mostrando después las
dos regiones.
21
3.2. Parte II: Integración con OpenCV
3.2.1. Análisis
OpenCV es una librería de visión por computador que proporciona una API de
C/C++ compuesta por diferentes módulos, que contienen una gran variedad de
funciones, desde funciones de bajo nivel para realizar conversiones de color en
un imagen a herramientas de alto nivel de machine learning.
Posee interfaces en C++, C, Java y Python y se encuentra disponible para
múltiples plataformas, destacando las versiones para GNU/Linux, Mac OS X y
Windows, y versiones para dispositivos móviles en Android e iOS.
En el caso de iOS, está directamente escrita en C++, por lo que podemos
utilizarla, sin mucho esfuerzo adicional, directamente en los ficheros Objective-
C++ de nuestra aplicación.
En Android existen dos alternativas. En primer lugar, utilizar OpenCV4Android
[11], un SDK que nos permitirá escribir nuestros algoritmos en Java. La otra
opción, que a su vez es la alternativa recomendada por OpenCV para
desarrolladores profesionales, es utilizar código nativo C++ a través de la NDK.
En nuestro caso lo que haremos será escribir los algoritmos directamente en
C++ para que éstos puedan ser utilizados en las aplicaciones desarrolladas
para cada plataforma (iOS y Android). En iOS a través de Objective-C++ y en
Android mediante cabeceras JNI a través del NDK.
22
Para poder utilizar posteriormente estos algoritmos desde nuestra aplicación
utilizaremos el concepto de módulos nativos de React Native e
implementaremos un nuevo módulo con el mismo API en ambas plataformas,
de manera que sea transparente su utilización. En cada una de las secciones
específicas iremos entrando en sus detalles de implementación.
En el alcance de este proyecto únicamente vamos a incluir el caso el que todos
las regiones de una imagen pertenezcan al mismo tipo, es decir, por ejemplo,
no mezclaremos regiones de tipo círculo con regiones de tipo cuadrado, pero
realmente no sería complicado extenderlo para contemplar estos casos.
3.2.2. Diseño
El tratamiento de la imagen se hará directamente en C++ y los resultados
obtenidos serán utilizados en las distintas plataformas.
Crearemos un fichero cabecera OpenCVAlgorithm.h para el cual deberemos
implementar el fichero OpenCVAlgorithm.cpp. Definiremos un método
sobrecargado con los tipos de cada una de las 4 regiones que la aplicación va
a soportar (Rectángulos, Círculos, Líneas y Puntos). Deberemos implementar
el del tipo adecuado en cada uno de los casos.
23
Los tipos de OpenCV que utilizaremos para representar cada unos de los
casos son:
- Rectángulos: vector<Rect>
- Círculos: vector<Vec3f>
- Líneas: vector<Vec4i>
- Puntos: vector<Point>
Por lo que únicamente tendremos un único método, sobrecargado para todos
los tipos que soportamos, en nuestra clase:
La implementación del fichero de cabecera quedaría, por tanto, de la siguiente
manera:
Desde las distintas plataformas haremos uso de esta clase y para ello, nos
serviremos de una combinación del Factory Method y el patrón Strategy [9].
24
De esta manera, el módulo instanciará una factoría a partir del tipo, que
devolverá el Adapter adecuado. Cada uno de ellos hará la conversión al tipo
oportuno, en el algoritmo. Como se puede observar en la figura todos
devuelven una lista de diccionarios (que implementaremos de distinta forma
según la plataforma).
En ambos casos el módulo OpenCVROI contará con la misma interfaz, que
consistirá en un único método que aceptará como argumentos el nombre de la
imagen a tratar, el tipo de región de interés, y además un tercer argumento
como callback, que devolverá como primer argumento un error en caso de que
lo hubiese y como segundo argumento una lista de diccionarios con los valores
de las regiones encontradas.
A continuación vamos a ver los detalles de implementación de cada una de las
plataformas.
3.2.3. Implementación en iOS
En este caso la librería de OpenCV, será añadida mediante CocoaPods [12],
que se trata de una de las formas más comunes de gestionar librerías en iOS.
En el Anexo I se muestra una explicación, de los pasos para realizar esta
operación.
Un módulo nativo de React Native en iOS se trata únicamente de una clase
Objective-C que implementa el protocolo RCTBridgeModule.
Además de esto, nuestra clase deberá incluir el macro
RCT_EXPORT_MODULE(). React únicamente expondrá los métodos que sean
marcados mediante el macro RCT_EXPORT_METHOD().
En nuestra clase tomaremos como argumento la imagen que será pasada al
algoritmo. Instanciamos la factoría a partir del tipo (de regiones) que entra
como argumento y una vez aplicado el método para detectar las regiones
devolveremos el resultado en un callback.
25
La implementación de la clase OpenCVRectAlgorithmAdapter, que utiliza
como argumento para pasar al algoritmo un vector<Rect>, y que devuelve
como resultado un NSMutableArray de NSMutableDictionary de tipo rect
quedaría de esta forma:
3.2.4. Implementación en Android
Podremos utilizar OpenCV en Android, de dos maneras diferentes, siendo la
recomendada por OpenCV, OpenCV Manager [13]. Ésta nos permitirá una
instalación de OpenCV compartida por todas las aplicaciones instaladas en el
dispositivo, lo cual hace que disminuya el tamaño de las aplicaciones, pero a
26
su vez, empeora el nivel de experiencia de usuario, al obligar al usuario final a
tener que instalar una aplicación de un tercero.
En nuestro caso, hemos elegido la otra alternativa, incluyendo OpenCV como
librería en nuestro proyecto Java y añadiéndola al build mediante Gradle [14].
En el Anexo I se explica todos los pasos realizados para conseguirlo.
En Java un módulo nativo de React Native es una clase que extiende de
ReactContextBaseJavaModule e implementa la función requerida. En este
caso, aunque la interfaz será similar a la de Objective-C, devolveremos en el
callback un WritableArray de WritableMaps. El código implementado
sería el siguiente:
Como ejemplo, la implementación de OpenCVCircleAlgorithmAdapter
que utiliza Mat y que sería como sigue, debemos notar que en este caso
estamos haciendo uso de funciones de la clase JNI.
27
Siendo la implementación de esta clase JNI como se muestra a continuación:
Y su correspondiente implementación en C++:
28
Como último paso deberemos registrar el módulo. Para ello crearemos la clase
OpenCVROIPackage y añadiremos el módulo OpenCVROIModule desde el
método createNativeModules.
Una vez realizado el paso anterior, añadiremos ese paquete en el método
getPackages() de la clase MainActivity de nuestra aplicación Android.
3.2.5. Ejemplo de uso en React Native
El siguiente ejemplo consistirá en aplicar sobre una imágen (en este caso de
unas latas), el algoritmo HoughCircles [15], éste nos devolverá las posiciones
de los elementos, mostrándolos a continuación por consola.
En el siguiente fragmento de código, podemos ver la implementación del
algoritmo en C++:
29
Siendo su uso en React Native, como sigue:
Este ejemplo, devolverá como resultado, las regiones de interés de la imagen
utilizada, que se visualizarán por consola, como se muestra a continuación:
30
3.3. Parte III: Caso de uso
Tomando como base los módulos desarrollados en las anteriores secciones
crearemos una aplicación. Ésta consistirá, en utilizar un algoritmo de
reconocimiento facial para detectar las caras presentes en una fotografía, y
posteriormente gestionarlas.
El objetivo es desarrollar una aplicación, que pueda tomarse como base para
crear otras similares, de tal manera que cambiando únicamente el algoritmo,
sea reutilizable para otro cometido totalmente distinto.
Al final del capítulo, veremos un ejemplo de lo que comentamos, cambiando el
algoritmo por otro que detecte las forma circulares existentes en una imagen.
3.3.1. Análisis
La aplicación desarrollada debe permitirnos, o bien tomar una foto o
seleccionar una, de entre las existentes en la galería del dispositivo, para
posteriormente, detectar las caras que hay en ella y poder gestionarlas. La
funcionalidad incluída sobre las imágenes, sería la siguiente:
- Añadir una nueva cara.
- Eliminar una cara.
- Mover de posición la región de la cara.
- Cambiar el tamaño de la región de la cara.
La aplicación guardará los resultados para poder recuperarlos más adelante,
con el fin de reeditarlos.
Al entrar en la aplicación visualizaremos un listado de todas las imágenes que
han sido tratadas con anterioridad, pudiendo también ser eliminadas de la
misma.
31
Para comprender mejor el comportamiento de la aplicación hemos realizado
prototipos, con el fin de clarificar algunos aspectos de lo explicado con
anterioridad acerca de la funcionalidad de la aplicación:
Listar imágenes
Al entrar a la aplicación aparece un listado con todas
las imágenes ya analizadas, junto a su nombre, fecha
de última modificación y un thumbnail.
En caso de que aún no se haya cargado ninguna, se
mostrará el listado vacío.
En la esquina superior derecha, pulsando sobre el
símbolo ‘+’, podremos añadir una nueva imagen (ver
Subir imagen).
Pulsando sobre cualquiera de los elementos del
listado, podremos acceder a la imágen
correspondiente (ver Detalle de la imagen).
Subir imagen
Pulsando sobre el símbolo ‘+’ en la esquina superior
derecha, podremos seleccionar una imagen de la
librería del teléfono o tomar una foto nueva. También
existirá la opción de cancelar la acción. Tras
seleccionar una imagen y después de analizar las
caras existentes en la misma, accederemos a la
pantalla Detalle de imagen.
32
Detalle de la imagen
Se mostrará la imagen seleccionada con las
regiones almacenadas. Las acciones a realizar son
las ya indicadas:
- Añadir una nueva cara.
- Eliminar una cara.
- Mover de posición la región
- Cambiar el tamaño de la región
Desde la parte superior podremos añadir una nueva
región que se colocará por defecto en el centro de
la imagen.
También existirá la posibilidad de mover cada una
de las regiones para cambiar su posición.
Al hacer presión prolongada sobre alguna de ellas
nos aparecerá un diálogo para poder eliminarla.
Y por último, si hacemos doble tap sobre una
región aparecerán controles de transformación para
modificar el tamaño de la región.
Eliminación de imagen
Desde el listado inicial, el que aparece al abrir la
aplicación, si hacemos swipe sobre alguna de las
celdas de este listado, nos ofrece la opción de
eliminar la imagen. Si pulsamos sobre la acción y
confirmamos que deseamos eliminarla, ésta
desaparecerá de la aplicación.
33
El esquema de navegación del conjunto de los casos de uso tratados, sería el
siguiente:
3.3.2. Diseño
Componentes
Cómo la tecnología seleccionada para el desarrollo de la aplicación en este
punto, va a ser React Native, el diseño va a estar orientado al modelo de
componentes que se utiliza. Haciéndonos eco del principio de Separation of
concerns, cada una de nuestras pantallas la descompondremos en diferentes
componentes, si tomáramos como ejemplo, el prototipo del listado de imágenes
obtendríamos la siguiente descomposición:
Podemos observar una visión global, de los componentes principales usados
en la aplicación, en el siguiente gráfico:
35
Arquitectura y comunicación
Aunque la tendencia es utilizar Flux como arquitectura en las aplicaciones
basadas en React Native, en este caso no ha sido utilizado, con el fin de evitar,
la “premature fluxing”1.
Dado el nivel de complejidad de la aplicación, y que toda nuestra lógica es de
nivel jerárquico, pasando las propiedades de padres a hijos, no se ha visto
necesario utilizar Flux en este momento.
No obstante, sí que vamos a separar el módulo encargado de gestionar los
datos, para ello crearemos el módulo DataStore que contendrá los métodos
necesarios para listar, añadir y modificar los datos contenidos en la aplicación.
En el siguiente diagrama de secuencia podemos observar la comunicación
existente entre los distintos componentes, al cargarse una nueva imagen:
Modelo de base de datos
Inicialmente, los datos que vamos a necesitar almacenar en la base de datos,
son los de las imágenes tratadas y las regiones detectadas, siendo el esquema
de base de datos utilizado, el siguiente:
1 https://twitter.com/floydophone/status/649786438330945536
36
Como se observa en la figura superior crearemos dos tablas, Image y ROI.
Ambas tendrán como clave primaria el campo id, en el que almacenaremos un
uuid generado desde la aplicación. La relación entre ellas es de 1 a 0 o varios,
pudiendo tener una imagen varias regiones de interés o ninguna. Utilizamos
una única tabla (ROI) para almacenar la información de todos los tipos de
región soportados, por lo que en el campo type informaremos del tipo concreto.
3.3.3. Implementación
En esta parte de la memoria, trataremos sobre algunos detalles de
implementación específicos del problema tratado, sobre los que nos ha
parecido interesante profundizar, además de esto, hablaremos sobre, la
integración entre los diferentes módulos.
Selección de imágenes
Para el selector de imágenes hemos utilizado el módulo react-native-image-
picker [16], el cual nos va a permitir, utilizar casi el mismo código para la
selección de imágenes en iOS y Android consiguiendo, de esta manera, un
efecto similar. Aunque este componente, posee la restricción de permitir
seleccionar únicamente una imagen, esto es suficiente en nuestro caso.
37
iOS Android
La única diferencia existente en la implementación, entre las dos plataformas
(iOS y Android), es la información que almacenamos como la ruta de nuestra
imagen. En el caso de iOS la ruta devuelta como uri (la cual es una ruta
absoluta) puede no persistir2 (por reinstalaciones o actualizaciones de la
aplicación), por lo que sólo almacenamos la parte relativa, es decir, la sección
de la ruta, tras el directorio de documentos de la aplicación, almacenando para
Android la ruta absoluta de la imagen.
Por ejemplo, de la siguiente ruta devuelta para una imagen en iOS sólo
almacenaremos la parte en negrita.
/Users/carlos/Library/Developer/CoreSimulator/Devices/EC86CB40-C31E-
4B8C-BC56-6E3DF7578735/data/Containers/Data/Application/885737E8-EDD9-
4813-BBC6-8A34542B8679/Documents/images/BC0E8875-B374-43FE-9EE5-04FBAB4B6F88.jpg
A la hora de recuperar la imagen, tendremos que construir la ruta donde se
encuentra almacenada dinámicamente, concatenando la ruta de la aplicación
con la que teníamos almacenada. Para realizar este cometido utilizaremos el
módulo react-native-fs [17] que nos va a permitir recuperar el
DocumentsDirectory de nuestra aplicación. En el caso de Android simplemente
utilizamos la ruta almacenada.
Base de datos
Como ya hemos mencionado con anterioridad en el apartado de Diseño, la
información de las imágenes y las regiones detectadas van a ser almacenadas.
2 http://stackoverflow.com/questions/5607655/
38
Para este cometido, hemos recurrido al módulo de React Native existente para
la utilización de la base de datos Realm [18].
Realm no es un ORM (Object Relational Mapping), sino que se trata, de una
base de datos completa para desarrolladores de aplicaciones móviles y que
usa objetos nativos de JavaScript, que son dinámicamente mapeados en una
base de datos (no es de tipo clave-valor). Es totalmente cross-plattform, su API
nos permite escribir una única vez el código en JavaScript, para luego utilizarlo
en Android e iOS. Además de esto, también el propio fichero Realm se
caracteriza por ser cross-plattform.
Los objetos de Realm siempre se encuentran actualizados con los últimos
datos, lo cual nos facilita el flujo de datos unidireccional que vamos a seguir.
En la imagen de la izquierda, podemos ver un ejemplo de definición del
esquema de una de tablas, mientras que en el fragmento de código de la
derecha, nos encontramos con un ejemplo de una escritura en base de datos
del objeto que utiliza el esquema arriba descrito.
Además de lo explicado con anterioridad, Realm también nos ofrece un
componente ListView (con la misma API que el de React) pero que como
DataSource acepta un conjunto de objetos de Realm lo cual hace que se
simplifique mucho el uso.
39
Código específico para las plataformas
React Native nos ofrece varias alternativas para implementar código diferente
en función de la plataforma.
React Native detecta cuando un fichero tiene .ios o .android como
extensión y selecciona el fichero adecuado para cada plataforma, al ser
requerido desde otro componente. Por ejemplo, en la carga inicial lo hemos
utilizado de esta manera:
En contrapunto con el método anterior, podremos utilizar el módulo Platform
para saber que plataforma estamos usando.
En el ejemplo que se muestra a continuación, vemos que el componente
cargado para la cabecera es distinto dependiendo del sistema operativo sobre
el que esté corriendo la aplicación:
En este otro ejemplo, vemos cómo variará el tamaño, en función de la
plataforma:
40
Integración de los módulos y aplicación final
Vamos a analizar la integración entre los módulos y las partes que hemos ido
implementado o configurando para construir la aplicación completa.
Por ejemplo, en la configuración hemos indicado que las regiones a tratar serán
de tipo rect.
Además de esto, a continuación mostramos la implementación del algoritmo de
detección de caras, implementado en OpenCVAlgorithm.cpp
En este fragmento de código podemos observar cómo pasamos a OpenCV la
ruta y los resultados obtenidos tras analizar la imágen, para finalmente
almacenar esta información en la base de datos.
41
En este otro fragmento de código, podemos ver cómo una vez recuperados
desde base de datos las imágenes analizadas y sus regiones, se muestran en
la vista de detalle, para lo cual utilizaremos el componente
ImageWithRegions.
42
A continuación se muestra el aspecto de las aplicaciones desarrolladas para
las diferentes plataformas:
44
Detección de círculos
En el caso de querer desarrollar una aplicación similar, queda patente, la
flexibilidad de la plataforma, ya que únicamente, cambiando el algoritmo y el
tipo de la región de interés conseguimos una aplicación que nos va a permitir
detectar círculos y también gestionarlos. Para ello, utilizaremos el algoritmo
HoughCircles que ya ha sido nombrado en ejemplos anteriores.
Será necesario indicar el tipo de región a detectar, que en este caso se va a
tratar de circle, en la configuración de la aplicación:
Como podemos observar en la imagen inferior, tras realizar estos dos simples
cambios, ahora nuestra aplicación detectará los círculos existentes en una
imagen.
45
4. Conclusiones
El objetivo principal del proyecto ha sido cumplido, consiguiendo una
plataforma que nos permite detectar y gestionar regiones de interés, en
imágenes, mediante dispositivos móviles.
Además de esto, la aplicación de reconocimiento facial desarrollada, nos va a
servir como base para futuras aplicaciones, pudiendo mediante la realización
de modificaciones sencillas, reutilizar el código de la misma.
Consideramos que, la elección de React Native ha sido acertada. Aunque al
ser una tecnología reciente ha requerido un esfuerzo extra de investigación y
formación, finalmente, la facilidad para integrar los distintos módulos y la
limpieza del código desarrollado ha compensado este sobreesfuerzo. En
cuanto a la parte gráfica, hemos conseguido crear una buena experiencia de
usuario.
Una posible mejora podría ser modificar el modo en el que se pueden crear
nuevas aplicaciones. Por ejemplo, desarrollando un generador, que nos
permita crear la estructura sin tener que copiar la anterior, de manera que sea
más configurable.
Otro punto a desarrollar en el futuro podría ser que los datos de las imágenes,
que son almacenados, en vez de serlo en el dispositivo móvil, lo sean en un
servidor o en un servicio, ya sea para poder ser accesible para la edición,
también desde otras plataformas, como para facilitar la colaboración entre
varios usuarios.
46
5. Glosario
API: Cualquier interfaz bien definida que especifique los servicios que un
componente, modulo o aplicacion ofrece a otras piezas de software.
Componente: En el contexto de React, es la unidad base en la que
descomponen las aplicaciones.
Framework: Conjunto estandarizado de conceptos, prácticas y criterios para
enfocar un tipo de problemática particular que sirve como referencia, para
enfrentar y resolver nuevos problemas de índole similar.
JNI (Java Native Interface): Framework de programación que permite que un
programa escrito en Java ejecutado en la máquina virtual java (JVM) pueda
interactuar con programas escritos en otros lenguajes como C, C++ y
ensamblador.
Librería: Conjunto de implementaciones funcionales que ofrece una interfaz
bien definida para la funcionalidad que se invoca.
NDK: El Android NDK (Native Development Kit) permite a los desarrolladores
reutilizar código escrito en C/C++ introduciéndolo en las aplicaciones a través
de JNI (Java Native Interface).
PoC (Proof of Concept): Una prueba de concepto es una implementación, a
menudo resumida o incompleta, de un método o de una idea, realizada con el
propósito de verificar que el concepto o teoría en cuestión es susceptible de ser
explotada de una manera útil.
ROI (Region Of Interest): Subconjunto seleccionado de muestras dentro de un
conjunto de datos identificados para un propósito en particular.
SDK: Software Development Kit. Conjunto de herramientas de desarrollo
software que permite al programador crear aplicaciones para un sistema
concreto (paquetes de software, framework, etc.).
47
6. Referencias bibliográficas
[1] Sitio web de OpenCV http://opencv.org/
[2] Sitio web de ImageJ https://imagej.nih.gov/ij/
[3] Sitio web de Trello https://trello.com/
[4] Henrik Kniberg, Mattias Skarin.Kanban and Scrum - Making the Most of
Both (InfoQ, 2009)
[5] Sitio web de React Native https://facebook.github.io/react-native/
[6] Sitio web de Ionic http://ionicframework.com/
[7] Sitio web de PhoneGap http://phonegap.com/
[8] Sitio web de Appcelerator Titanium http://www.appcelerator.com/mobile-
app-development-products/
[9] Erich Gamma, Richard Helm, Ralph Johnson, and John M.Vlissides. Design
Patterns, Elements of Reusable Object-Oriented Software (Addison-Wesley,
1995)
[10] Documentación online del componente PanResponder
https://facebook.github.io/react-native/docs/panresponder.html
[11] Documentación de OpenCV para Android
http://opencv.org/platforms/android.html
[12] Sitio web de CocoaPods https://cocoapods.org/
[13] Documentación de Android OpenCVManager
http://docs.opencv.org/2.4/platforms/android/service/doc/index.html
[14] Sitio web de Gradle http://gradle.org/
[15] Documentación de OpenCV sobre el algoritmo HoughCircles
http://docs.opencv.org/2.4/modules/imgproc/doc/feature_detection.html?highlig
ht=houghcircles
48
[16] Página GitHub del módulo react-native-image-picker
https://github.com/marcshilling/react-native-image-picker
[17] Página GitHub del módulo react-native-fs
https://github.com/johanneslumpe/react-native-fs
[18] Sitio web de Realm https://realm.io/
Otras referencias consultadas
Bonnie Eisenman. Learning React Native (O’Reilly Media, Inc., 2015)
Ethan Holmes, Tom Bray. Getting Started with React Native (Packt Publishing
Ltd., 2015)
Joseph Howse. Android Application Programming with OpenCV (Packt
Publishing Ltd., 2013)
Kirill Kornyakov, Alexander Shishkov. Instant OpenCV for iOS (Packt Publishing
Ltd., 2013)
Akshat Paul, Abhishek Nalwaya. React Native for iOS Development (Apress,
2016)
Martin Fowler. Patterns of Enterprise Application Architecture (Addison-Wesley,
2002)
Anexo I: Instalación de OpenCV
Instalación en Xcode
En nuestro caso, hemos utilizado el gestor de dependencias CocoaPods para
instalar la librería OpenCV en el proyecto de XCode.
Para ello, lo primero que tendremos que hacer es instalar la librería en nuestro
sistema operativo, si es que no se encuentra ya instalada. Básicamente
consiste en instalar una gema Ruby. Tenemos las instrucciones completas en
su página web https://guides.cocoapods.org/using/getting-started.html.
$ sudo gem install cocoapods
Una vez instalado el paquete, tendremos que crear un fichero Podfile dentro de
nuestro proyecto, en el que definiremos las dependencias, en este caso de
OpenCV.
Con esto en la ruta de nuestro proyecto, ejecutamos el siguiente comando para
instalar las dependencias.
$ pod install
Tras la realización de estos pasos, ya tendremos instalada la librería. Como
vamos a utilizar objetos en Objective-C++ para utilizar la funcionalidad de
OpenCV, tendremos que cambiar la configuración de compilación de nuestro
proyecto.
1
Instalación en Android Studio
En el caso de Android descargaremos la librería de la página web de OpenCV
http://opencv.org/downloads.html
Una vez descargada integraremos OpenCV en la herramienta de build que
estamos utilizando, Gradle.
Para ello seguiremos los siguientes pasos:
1. Crearemos la carpeta libraries bajo nuestro directorio principal.
2. Iremos a la localización donde hemos descargado el SDK, donde
tendremos "\OpenCV-android-sdk\sdk" y aquí encontraremos una
carpeta llamada java, la renombramos a opencv.
3. Ahora copiaremos todo el directorio opencv del SDK en la carpeta de
librerías que acabamos de crear.
4. A continuación crearemos un fichero build.gradle dentro del directorio
opencv con el siguiente contenido (adaptado a nuestras necesidades)
5. Editaremos el fichero settings.gradle del directorio principal y
añadiremos la siguiente línea: include ':libraries:opencv'
6. Sincronizaremos el proyecto con Gradle.
2
7. Haremos click con el botón derecho sobre nuestro proyecto y
seleccionaremos Open Module Settings y luego en Choose Modules del
listado de la parte izquierda. Seleccionaremos el módulo de nuestra
aplicación y la pestaña Dependencies, y haremos click en botón + para
añadir un nueva dependencia.
8. Elegiremos Module dependency, que abrirá un diálogo con la lista de
módulos y seleccionamos “:libraries:opencv”.
9. Crearemos la carpeta jniLibs en /app/src/main/ location y copiaremos
todas las carpetas con los ficheros *.so (armeabi, armeabi-v7a, mips,
x86) de OpenCV SDK en esta carpeta.
10. Pulsaremos OK y con esto ya tedremos OpenCV listo para usar en
nuestro proyecto Android.
Anexo II: Referencias a principios y patrones
En este apartado se hace una breve definición de los patrones y principios
software mencionados en el proyecto, con el fin de facilitar su lectura y
comprensión.
Principios y patrones
Adapter (Adaptador)
Patrón que se utiliza para transformar una interfaz en otra, de tal modo que una
clase que no pudiera utilizar la primera, haga uso de ella a través de la
segunda.
Composition over inheritance (Composición sobre herencia)
Principio que dice que las clases deberían conseguir el comportamiento
polimórfico y la reutilización de código a través de composición (conteniendo
instancia de otras clases que implementen la funcionalidad deseada) en vez de
heredar de una clase base o padre.
Facade (Fachada)
Patron en el que un objeto proporciona una interfaz simplificada para un cuerpo
mas grande de codigo, como por ejemplo una libreria.
Factory method
Consiste en utilizar una clase constructora (al estilo del Abstract Factory)
abstracta con unos cuantos métodos definidos y otro(s) abstracto(s): el
dedicado a la construcción de objetos de un subtipo de un tipo determinado.
Separation of Concerns (Separacion de intereses)
Principio de diseno para separar una aplicacion en distintas secciones, cada
una de ellas enfocada en un interes distinto. Un interes es un conjunto de
informacion que afecta al codigo del programa y que puede ser general o
especifico.
1
Strategy pattern (Patrón estrategia)
Patrón de comportamiento que determina cómo se debe realizar el intercambio
de mensajes entre diferentes objetos para resolver una tarea. El patrón
estrategia permite mantener un conjunto de algoritmos de entre los cuales el
objeto cliente puede elegir aquel que le conviene e intercambiarlo
dinámicamente según sus necesidades.