Gestión móvil de regiones de interés en imágenes digitales · realizará el desarrollo del...

57
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ágenes digitales Autor/es

Transcript of Gestión móvil de regiones de interés en imágenes digitales · realizará el desarrollo del...

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: [email protected]

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

1

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.

19

20

El resultado es el siguiente:

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:

34

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:

43

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.

3

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.