lunes, 6 de mayo de 2013

Separación por color II

El siguiente paso en el desarrollo de nuestra aplicación ha sido el estudio y la transformación de las componentes RGB del color obtenido de la cámara a otros espacios de color (HSV, XYZ, YCrCb, Luv, YUV, etc.) para ver si en estos las variaciones de alguna de las coordenadas era determinista para poder diferenciar entre los dos tipos de color en los que se ha enfocado nuestro estudio: oro y cobre.

Los resultados obtenidos eran mejores en unos espacios de color que en otros, pero ninguno ha sido concluyente. A continuación se pasa a explicar brevemente las características principales de los espacios de color más relevantes dentro de todos los utilizados:

HSV


Un color representado en este espacio queda determinado por tres componentes: 
  • H: Hue (Matiz): Representa cada color mediante un ángulo.
  • S: Saturation (Saturación): Representa distancia al eje de brillo negro-blanco.
  • V: Value (Valor): Representa la altura en el eje blanco-negro.
El problema de este espacio con respecto a nuestra aplicación es que los colores oro y cobre están bastante juntos en la circunferencia, aún así podrían ser mucho mas estables y diferenciables que los valores de las componentes RGB. Después de tomar valores de varias monedas, observamos que tampoco cumplía los objetivos.

CIE XYZ


La matriz de transformación del espacio de color RGB a XYZ depende del punto blanco considerado. En nuestro caso se ha tomado como referencia el punto blanco D65, por lo que el respectivo cambio se realiza mediante la siguiente operación:


Este espacio de color se ha utilizado como transformación intermedia del espacio RGB a otros creados por  la Comision Internacional de la Iluminación (CIE), como el que se explica a continuación.

CIE Luv


Las tres componentes que se representan en este espacio de color son las siguientes:
  • L*: Luminosidad del color. Valores altos indican una mayor cercanía al blanco y valores en torno a 0 se corresponden con el color negro.
  • u*: Uniformidad del color.
  • v*: Brillo: Representa la altura en el eje blanco-negro.
Al contrario que los espacios anteriormente descritos, la transformación en este caso no es lineal. Las ecuaciones que definen dicha transformación son las siguientes:


\begin{align}
L^* &= \begin{cases}
  \left(\frac{29}{3}\right)^3 Y / Y_n,&   Y / Y_n \le \left(\frac{6}{29}\right)^3 \\
  116 \left( Y / Y_n \right)^{1/3} - 16,&  Y / Y_n  >   \left(\frac{6}{29}\right)^3      
\end{cases}\\
u^* &= 13 L^*\cdot (u^\prime - u_n^\prime) \\
v^* &= 13 L^*\cdot (v^\prime - v_n^\prime)
\end{align}

donde un y vn se corresponden con las coordenadas del llamado "Punto blanco" y Yn es su luminancia. En este caso se ha tomado como punto blanco: un = 0.2009vn = 0.4610. Además:


La peculiaridad de este espacio de color (y lo que le hace interesante para nuestra aplicación) es que provoca una percepción del color más lineal, es decir, se busca que un cambio de la misma cantidad en un valor de color provoque un cambio de la misma importancia visual.

En este caso, se utilizó la componente u* para intentar diferenciar los colores sin obtener tampoco unos resultados adecuados ya que, aunque obteniendo unos mejores resultados, la separación entre ellos no era lo suficientemente grande como para establecer un umbral determinado.



viernes, 3 de mayo de 2013

Separación por color I

Como se comentó en entradas anteriores, las relaciones de radios entre monedas del mismo color son únicas  y, teóricamente, deterministas. Pero antes de poder desarrollar los algoritmos necesarios para hacer este tipo de diferenciación, es necesario separar dentro de una imagen las monedas de color oro y las de color cobre.

En primer lugar es necesario obtener los colores del centro de las monedas, para obtener unos mejores resultados (colores mas fiables), se ha hecho la media de un cuadrado de 16 píxeles de área. Aunque puede parecer casi trivial en realidad no lo es tanto, el procedimiento a seguir es:
  • Crear un cuadrado con centro el de la elipse; en Opencv los cuadrados se definen a partir del vértice superior izquierdo, por lo que será necesario comprobar que al situar su centro este no se encuentre fuera de la esquina superior izquierda de la pantalla: posiciones inferiores a (1, 1), ya que se obtendría un error de NullPointerException.
  • Lo mismo ocurre al definir la medida de los lados, pero en este caso hay que tener en cuenta el número de filas y de columnas que forman la imagen para que la zona a la que se va a acceder quede totalmente incluida en el área de la pantalla.
  • Finalmente, se obtiene una submatriz de la matriz original (imagen al completo) y se hace la media de las componentes RGB de todos los píxeles.
Una vez se tienen los colores, hay que pensar en un método para distinguir el oro del cobre. Para ello se puede aprovechar el siguiente hecho: el color amarillo es la suma a partes iguales de los colores primarios rojo y verde; y el color cobre tendrá mucha más componente roja. Por lo tanto, para las monedas de oro la relación R/G ~ 1 y para las de cobre R/G > 1.

Una vez implementado, nos dimos cuenta de que en la práctica no se obtienen unos resultados concluyentes, ya que las relaciones que obteníamos eran muy parecidas entre ellas y no había manera de separar las monedas. Después de intentar numerosas relaciones basándonos en la misma idea, se probó utilizando la siguiente fórmula: relación = exp(R / 0.4*G). De esta manera, las variaciones en el exponente se verán reflejadas en una variación aún mayor en la relación.

Aun obteniendo unos mejores resultados, tampoco se obtuvieron unos resultados concluyentes, ya que el umbral de separación entre los colores dependería mucho de la luminosidad del ambiente, por no mencionar los posibles reflejos y sombras de las monedas. Llegados a este punto, se pensó una manera de hacer el color independiente de la luminosidad, es decir, normalizar cada componente dividiendo por el sumatorio de las otras tres y volviendo a multiplicar por 255 para volver a tener valores dentro del intervalo original [0, 255] y poder aplicar la relación explicada anteriormente. Tampoco se obtuvieron buenos resultados.


Figura 1: Espacio de color RGB





martes, 30 de abril de 2013

Problema de dobles contornos

Hay un hecho que había pasado desapercibido hasta el momento, y es la detección de contornos dobles, es decir, para una misma moneda se pintan dos elipses muy parecidas entre ellas. Esto se debe a que a veces se detecta la sombra de la moneda como un contorno y el borde de la moneda en sí como otro. 

Figura 1: Problema de elipses dobles

Para solventar dicho problema, se ha diseñado un algoritmo recursivo que calcula la distancia euclídea entre los centros de todas las monedas presentes en la imagen y, si esta distancia es menor que su radio, la elipse con mayor eje menor se elimina. De esta manera, se elimina el problema de las sombras en algunos casos además del ya comentado.

Figura 2: Problema solucionado

Justificación de la elección final

A continuación se presentan un conjunto de gráficos comparativos entre los distintos métodos probados anteriormente. Para la recogida de los datos utilizados en los gráficos se han utilizado un número total de 10 monedas variadas en tamaño y color, repitiendo cada prueba 50 veces. Por lo que los resultados representados se corresponden a la media de todas las medidas.

Figura 1: Limitaciones.

El porcentaje de las limitaciones que suponen la utilización de un método u otro se han calculado en base a las siguientes condiciones: Si el método necesita de un fondo con un determinado color, se le añade un 50%; Si para el correcto funcionamiento las monedas han de estar en una determinada posición, se añade  otro 50%.

Las limitaciones que presenta la transformada de Hough se deben a que las monedas necesitan estar distribuidas de tal forma que no formen una circunferencia entre ellas (se detectaría una falsa elipse).

Figura 2: Tiempo de ejecución.

El tiempo de ejecución se ha medido en base a lo siguiente: el inicio del contador coincide con el momento en que se recibe un frame de la cámara del dispositivo. Este se detiene cuando la aplicación dibuja en pantalla las elipses correspondientes a las monedas detectadas.

Figura 3: Precisión.

La precisión es el porcentaje de monedas acertadas durante la prueba, se puede observar que ningún método es perfecto, ya que siempre hay algún frame en el que no todas son detectadas.

Conclusiones:

El primer factor eliminatorio es la precisión, ya que para nuestro proyecto no es aceptable un porcentaje de detección inferior al 70%, este hecho descarta cuatro de los métodos analizados.

La decisión final entre los métodos restantes se tomó en base a las limitaciones que presentaban, ya que el método Canny no presentaba ninguna de las anteriormente comentadas además de no suponer una diferencia demasiado grande en cuanto a precisión respecto al Otsu. Además, el tiempo de ejecución tampoco es un factor decisivo (como se ha comentado en entradas anteriores).




domingo, 28 de abril de 2013

Detección por contornos: Solución final


Como se comentó en entradas anteriores, nuestro objetivo era detectar las siluetas de las monedas combinando el reconocimiento por color y el procesado Canny. Sin embargo, y tras varias pruebas, hemos realizado un algoritmo de detección basado únicamente en el procesado Canny y otros procedimientos que se detallarán más adelante. De este modo, se evitan las complicaciones asociadas al color (brillos, sombras y zonas de colores similares que provocan detecciones falsas).

Tras el pre-procesado de la imagen, se le aplica el algoritmo Canny para obtener una imagen binaria con los contornos definidos, que se almacenan en una lista de puntos. La imagen en este momento tiene el siguiente aspecto:

Figura 1: Contornos detectados.

Como se puede observar en la imagen, aparecen muchos contornos que no tienen que ver con las monedas en sí, por lo que se hace imprescindible aplicar una serie de condiciones a todos los contornos de la imagen para que puedan pasar a formar parte de la lista de contornos definitivos. Dichas condiciones se detallan a continuación:

- Si el contorno está formado por un número reducido de puntos, se elimina de la lista. Con esta condición se quitaran de la imagen todos los contornos pequeños.

- Se dibuja una elipse que contenga a los puntos, si el eje mayor o el menor son mayores que un número grande de puntos, se elimina el contorno asociado a la elipse. Esta condición permite eliminar elipses que se salgan del plano de la cámara, ya que no serán monedas sino detecciones falsas debido a los contornos.

- Si la relación axial (eje mayor/eje menor) de la elipse es menor o mayor que unos determinados umbrales, se elimina. Esto se debe a que normalmente las monedas tendrán una relación axial cercana a la unidad que se correspondería con una circunferencia, y por mucho que se incline la cámara dicha relación no superará nunca los umbrales mencionados.

- Finalmente, se dibuja el contorno (en blanco) sobre una imagen con fondo azul y la elipse asociada al mismo (en blanco) sobre una imagen con fondo negro. Se comparan las dos imágenes con el operador lógico AND, que devolverá una matriz con los puntos que coinciden entre la elipse y su contorno. Si el número de puntos coincidentes es menor que un umbral, es porque en realidad no se trataba de una moneda, sino una falsa detección.

La imagen resultante es la que se muestra a continuación: 

Figura 2: Imagen resultante RGB


Notas:

• Los números y umbrales comentados anteriormente se han calculado a partir de numerosas pruebas experimentales. 

• El algoritmo Canny, requiere dos umbrales (superior e inferior), que se fueron modificando mediante dos "seekbar" introducidas en la aplicación con este propósito y a través de las cuales se pudo concretar cuáles eran sus valores óptimos. En las siguientes imágenes se muestran tres ejemplos con distintos umbrales:

Figura 3: Imagen con demasiados contornos

Figura 4: Imagen con los umbrales seleccionados

Figura 5: El contorno de una moneda se pierde

Hay que tener en cuenta que las condiciones anteriores se aplican sobre cada contorno de la imagen, por lo que cuantos más contornos aparezcan en ella, mayor será el tiempo necesario para procesar la imagen. Por lo tanto, se tuvo que hacer también un balance entre tiempo de procesado y precisión del detector para identificar el valor de los umbrales.

Para ilustrar este hecho, se han representado en el siguiente gráfico el número de contornos obtenidos de una imagen con tres monedas sobre fondo blanco frente al tiempo de ejecución (se ha desarrollado así para mantener lo más constantes posibles el resto de las variables que influyen en la detección de los contornos y, por lo tanto, en el tiempo de ejecución).

Figura 6: Comparativa de tiempos

Se puede ver que el tiempo de ejecución aumenta exponencialmente con el número de contornos detectados en la imagen, por lo que se hace necesario elegir unos umbrales que generen el mínimo número de contornos posible sin perder los contornos necesarios para identificar a las monedas.

Detección por contornos: Pruebas intermedias


Alguna de las principales tareas que estamos llevando a cabo para nuestro proyecto están siendo la investigación y numerosas pruebas en el campo de la visión artificial, por lo que en esta entrada nos dedicaremos a enumerar los distintos métodos empleados para intentar realizar una detección correcta de las siluetas de las monedas antes de llegar a la solución final:


1) Detección sobre una imagen en blanco y negro:

Este método trata de transformar una imagen en escala de grises a blanco y negro, la idea era mantener con color negro únicamente las monedas y el resto de la imagen en blanco para así poder calcular los contornos con mayor facilidad.

Para su implementación es necesario establecer un umbral, se analizará la imagen pixel por pixel y en función de su color (si supera o no el umbral definido) se sobrescribirá cada pixel con un valor binario (blanco o negro). En este caso, el fondo sobre el que se encuentren las monedas es de vital importancia para su correcta detección. Se decidió colocarlas sobre un fondo blanco y establecer un umbral bastante alto, de esta manera el fondo se quedaría como está (blanco) y la moneda pasaría a ser mayormente negra (descartando los brillos que, obviamente aparecerían blancos).

Una vez implementado, se hicieron pruebas y aparecieron varios problemas: los contornos que se detectaban incluían las sombras que pudieran tener las monedas, por lo que la medida de los ejes para la identificación por tamaños sería una medida falsa. Además al cambiar a otro ambiente con distinto nivel de luz, el umbral dejaba de ser válido y era necesario ajustarlo manualmente. La idea para evitar este último problema era utilizar de alguna manera un umbral dinámico, el método de Otsu. 


2) Detección mediante el método de Otsu:

Dicho método convierte una imagen en grises a blanco y negro calculando el umbral óptimo en cada momento a partir del histograma de la misma. De esta manera, se obtiene un umbral dinámico válido para cualquier situación de luminosidad. Sin embargo, seguía apareciendo el problema de la detección de sombras con otra complicación añadida, como el método calcula el histograma de la imagen, no puede haber en toda la imagen algún objeto oscuro, ya que esto provocaría un descenso del umbral y por lo tanto los pixeles correspondientes a las monedas se tomarían como blancos y la detección sería errónea.

Para evitar la complicación de las sombras, se optó por cambiar el fondo de la imagen a negro, de manera que las monedas quedarían como superficies blancas y las sombras pasarían a formar parte del fondo. Finalmente, se invertiría el valor de cada pixel de la imagen para volver a la situación anterior (monedas negras y fondo blanco).

Figura 1: Imagen binaria mediante el método Otsu

  Figura 2: Imagen RGB resultante



Esta última modificación hizo que la detección fuera lo suficientemente buena para el clasificador por tamaños, pero estaba limitada a tener un fondo oscuro, por lo que se probaron otros métodos para poder hacer la detección independiente del fondo.


3) La transformada de Hough:

Este método se tuvo que implementar enteramente en C++ incluyendo el código en la carpeta jni del proyecto debido a que las librerías OpenCv para Java no incluían los métodos necesarios para su desarrollo.  

Este método se propuso inicialmente para detectar líneas rectas, pero con el paso del tiempo se ha utilizado para la detección de todo tipo de figuras geométricas siempre que sus ecuaciones se puedan describir con unos cuantos parámetros. En nuestro caso se empezó con la detección de círculos por su mayor simplicidad, pudiendo después extrapolarlo a elipses si los resultados obtenidos eran buenos. Los parámetros a tener en cuenta en este caso son tres: la posición del centro de la circunferencia (valores x e y) y el tamaño del radio, que son los parámetros que se utilizarán para su representación y para hacer la clasificación. 
Con este método evitaríamos además la necesidad de estar obligados a disponer de un fondo de un determinado tono.

Figura 3: Transformada de Hough


Figura 4: Problema de no-detección.


Una vez implementado, se hizo evidente el primer problema,  el método requiere un cálculo computacional demasiado grande como para poder hacerlo a tiempo real en un dispositivo móvil. Aun así esto no supondría un problema, ya que se podrían hacer dos hebras en la aplicación: una en la que solo se mostraran las imágenes captadas y otra que sería la encargada de coger unos cuantos frames del flujo continuo de vídeo y hacer los cálculos sobre ellos, siendo este comportamiento totalmente transparente al usuario.

Tras varias pruebas e intentos de mejora descartamos la implementación del método para elipses, ya que aunque las detecciones que hacía eran muy precisas, un gran número de las monedas presentes en la imagen no eran detectadas y en ocasiones aparecían falsas monedas.

Nota: En todos los métodos anteriores es necesario incluir un pre-procesado de la imagen como por ejemplo un filtro gaussiano para eliminar ruido o una normalización de la escala de grises (de 0 a 255).

jueves, 21 de marzo de 2013

Próximos pasos: Implementación.


Una vez diseñados los algoritmos de detección y reconocimiento, vamos a proceder a su implementación. A pesar de que por separado, hemos probado ya gran parte de las funcionalidades que componen dicho algoritmo, no queremos retrasarnos mucho en la implementación completa, ya que somos conscientes de que por el camino van a aparecer numerosas dificultades que van a complicar nuestro proyecto.

Ya hemos probado la detección de contornos por color, y el procesado Canny, de tal manera que nuestro próximo paso será establecer el pegamento de unión entre los dos procesados. En concreto, nuestro próximo paso es, una vez obtenida la matriz blanco y negro de los contornos tras el procesado Canny, detectar en ella siluetas elípticas, para poder posteriormente analizar los colores en su interior e introducir dichos parámetros dinámicamente en el detector por colores.

Nuestro Algoritmo IV


Como ya se comentó, una vez obtenido el algoritmo que nos permitirá detectar los contornos de las monedas, el siguiente paso es el de su reconocimiento. Para ello, mi compañero y yo nos hacemos la siguiente pregunta: ¿Qué es lo que nos permite reconocer una moneda? La respuesta es: Color, tamaño, y grabado.

Como ya se comentó, la detección por comparación de patrones se torna rápidamente demasiado compleja para el caso de las monedas, por su tamaño y bajo relieve, por lo que mi compañero y yo decidimos reconocerlas por color y tamaño.

Así, de un primer vistazo, rápidamente obtenemos la manera de obtener monedas de 1€ y 2€: Su color. Respectivamente, sus colores son, en el caso de 1€, plateado en el interior y dorado en el exterior, y en 2€, justo lo contrario, de tal manera que, tras pasar el algoritmo de detección, simplemente recorriendo una línea (una columna, por ejemplo) de píxeles que sea secante al contorno obtenido, podemos unívocamente reconocer estas monedas.

Para el resto, sin embargo, parece ser más complicado, ya que los colores son compartidos por varias monedas. Así, una vez diferenciados dos grupos (doradas y cobrizas) mi compañero y yo decidimos pasar al reconocimiento por el tamaño.

Sin embargo, como ya se ha comentado, los tamaños absolutos de las monedas son relativos a la distancia a la que se encuentre la cámara. Así, llegamos a la conclusión de que detectaríamos las monedas por sus tamaños relativos:

En el caso de las monedas cobre (1, 2 y 5 céntimos) sus diámetros son:
5 cent: 21.25 mm
2 cent: 18.75 mm
1 cent: 16.25 mm

Y, en el caso de las doradas:
50 cent: 24.25 mm
20 cent: 22.25 mm
10 cent: 19.75 mm

Teniéndose así que, para cada color, las relaciones de tamaño entre las monedas son únicas de cada par. Así, dadas dos monedas, no importa la distancia de la cámara respecto a estas, ya que simplemente dividiendo sus tamaños, se obtiene una relación única que reconocerá unívocamente dichas monedas. No sólo eso, sino que además, a partir del conocimiento de una única moneda (ya sea de una de 1€ o 2€, o un par de las demás) se pueden usar como referencia de tamaños y de manera inmediata quedan graduadas el resto de las monedas, pudiendo pasar a una detección inmediata de ellas.

miércoles, 20 de marzo de 2013

Nuestro Algoritmo III


Tras una discusión y prueba de cómo vamos a implementar los algoritmos de detección de contornos, hemos llegado a la conclusión de implementar ambos, aprovechando las ventajas de cada uno y cubriendo las dificultades del otro.

Como ya dijimos en anteriores post, el principal problema asociado a la detección por color era la gran variabilidad que tenían los colores de las monedas en función del entorno, a veces incluso entre las mismas monedas de un mismo lugar, haciendo imposible una determinación a priori del rango de colores en el que se iba a trabajar. Así, se hace necesaria una dinamización de dichos parámetros, de tal manera que, en tiempo real, podamos detectar los contornos de una manera rápida y, sobre todo, que esté adaptada a cualquier entorno en el que se quiera usar la aplicación. Dicha dinamización se basará, precisamente, en la detección por silueta.

Dicha detección, a pesar de mostrar más o menos contorno de la moneda en función del contraste, nos da la suficiente información (y lo suficientemente precisa) como para poder conjeturar elipses, y, aunque realmente no estén siendo detectadas de forma completa, poder determinar la región espacial en la que se encontrarán. Es decir, dados unos cuantos trazos de una silueta de una elipse, podemos imaginar donde se encontrará esa elipse. Y es precisamente ese el secreto de nuestra dinamización en los parámetros cromáticos. Una vez conocida la región en la que se encontrará nuestra moneda, muestrearemos los colores que se encuentren en ella, tomando, por ejemplo, unos cuantos puntos a lo largo de la región comprendida dentro de la silueta,y, una vez tomados, utilizarlos como parámetros para la detección de contornos por color, que, con los parámetros adecuados, devuelve unas estructuras (matrices de puntos) mucho más faciles de trabajar y procesar, además de ser contornos completos y cerrados, no como ocurría con el método anterior.

No se descarta, sin embargo, una dinamización también del umbral que marca la detección de siluetas por el algoritmo Canny, ya que, como se comentó, bajo determinadas condiciones es imposible reconocer la silueta de una moneda. En esos momentos, se bajaría el umbral para permitir su detección.

Así finalmente, parece que ya hemos estructurado cómo será nuestro algoritmo de detección de monedas. El paso siguiente que debemos dar ahora es, una vez obtenido dichos contornos, cómo nuestra aplicación va a reconocerlos.

martes, 19 de marzo de 2013

Nuestro Algoritmo II


En nuestra búsqueda de nuevos métodos y algoritmos con los que podamos detectar monedas se nos ocurrió la idea que, debido al relieve que estas tienen, y al color diferenciado con el entorno, podríamos detectar las monedas por su silueta.

Para ello, hicimos lo siguiente:
OpenCv permite obtener frame por frame los flujos de video provenientes de una cámara de un dispositivo android. 

Sin embargo, nos dimos cuenta de que aplicando directamente métodos de detección de contornos sobre dispositivos android, no nos ofrecía un rendimiento adecuado. Por ello, antes de aplicarles dichos métodos, realizamos el siguiente preprocesado:
OpenCv, por defecto, nos devuelve una matriz de 3 canales en RGB, con toda la información proveniente de la cámara a color. Sobre ella, aplicamos una transformación a blanco y negro con el método ImgProc, para acelerar el procesado posterior. 

Tras él, convolucionamos con un filtro gaussiano de 3x3 para eliminar parte del ruido en el frame, y, una vez hecho esto, le pasamos un algoritmo de detección de contornos, en un principio con un umbral estándar.
Una vez preprocesado el frame, lo vamos a pasar por un algoritmo de obtención de contornos y siluetas, concretamente el algoritmo Canny.

Así, lo que finalmente obtenemos es una imagen en blanco y negro, en el que todos los contornos vienen marcados en blanco. 

Una vez probado, podemos analizar los resultados obtenidos:
Parece ser bastante fiable a la hora de detectar contornos, sobre todo en cuanto a la forma que tienen estos últimos, que son en gran medida más redondeados que los obtenidos mediante la detección por colores.

Sin embargo, asociados a éste método nos encontramos numerosos problemas:
Lo obtenido de este algoritmo son de nuevo matrices, imágenes, sobre las que deberíamos aplicar un procesado posterior en busca de formas elípticas.

Las monedas aparecen a menudo con diferentes contornos concéntricos, lo que dificultaría escoger cuál de todos ellos se corresponde con el tamaño real (con el determinado por el contorno exterior) y cual no.
Por factores de luminosidad, nos encontramos de nuevo que los colores cobrizos de las monedas de 1, 2 y 5 céntimos se diferecian tan poco del color de la mano que provoca que los contornos no figuren completos sino a trozos (solo aparecen algunos sectores)

Así, una vez terminado el análisis de los resultados de la aplicación de este método, mi compañero y yo pasaremos a la discusión de su inclusión o no en nuestro algoritmo principal, y, de ser así, como lo llevaremos a cabo.

Nuestro Algoritmo I


Como ya se comentó en el último post, mi compañero y yo nos enfrentábamos a una gran cantidad de problemas que complicarían la detección y reconocimiento de las diferentes monedas. Así, hasta llegar al diseño del algoritmo final, pasaremos por diferentes etapas de diseño y prueba en nuestro dispositivo Android, con el fin de ir testeando y mejorando nuestro algoritmo, hasta llegar a lo que creamos que es nuestro algoritmo de detección final.

Lo primero que hemos pensado es en el uso de un algoritmo de detección de contornos mediante el color. 

Lo que necesitamos es que, a partir de una superficie mínima, a partir de la cual no consideraremos que es una moneda, por ser tan pequeña, ir testando píxel a píxel el color de los vecinos del píxel inicial. Si éstos se encuentran dentro del rango de colores que deseamos, incluirlos en la superficie, y testear sus vecinos. Así, se continuaría hasta que los vecinos fueran de diferente color, y se marcaría esa separación como contorno de los objetos.

Dicha función existe en OpenCv y es la que usaremos para una detección inicial de contornos.

Sin embargo, dicho algoritmo es insuficiente por sí solo, por las siguientes razones:

Los colores de las monedas cambian tanto de un entorno a otro, en función de la luminosidad, que hacen que a menudo no sea capaz de detectar una moneda.

Debido al parecido del color cobre con la mano, si ampliamos el rango de los tonos de cobre que aceptamos, la mano se introduce dentro de los contornos.

A causa de los reflejos existentes sobre la moneda los contornos no quedan completos, sino recortados, exactamente sobre la zona de la moneda que no está reflejada por una fuente intensa de luz.

Así, como se puede apreciar, suficientes problemas nos han surgido con éste método como para que nos obligue a buscar, o bien alternativas, o bien otros métodos de detección que apoyen el método comentado.

lunes, 18 de marzo de 2013

Herramientas, objetivos y problemas


Descubriendo el OpenCV

Una vez instaladas las herramientas básicas para el desarrollo de aplicaciones con visión artificial para Android, mi compañero y yo pasamos al estudio tanto de la visión artificial general como de qué es lo que las librerías OpenCv pueden ofrecernos de ello.

Para comenzar, vimos que OpenCv simplifica enormemente el trabajo duro y pesado de obtener imágenes y flujos continuos de vídeo en tiempo real de la cámara Android. Además, al estar implementado en Java, nos permite trabajar con cualquier tipo de cámara, venga del dispositivo que venga.

OpenCv trabaja a varios niveles, y por eso ofrece diferentes librerías, dependiendo de la funcionalidad deseada. Así, encontramos Core, para el trabajo a más bajo nivel, ImgProc, que incluye métodos para el procesado de imágenes, etc.

Mi compañero y yo apreciamos que, a pesar de ofrecer una enorme cantidad de funciones y filtros que operan sobre imágenes, debido al bajo nivel al que actuan, debíamos diseñar nosotros mismos un algoritmo de detección y reconocimiento de monedas.

Para ello, teníamos que tener en cuenta los siguientes factores:
El tamaño de las monedas que aprecia una cámara no tiene por qué ser el real, ya que dependerá de la distancia a la que el usuario tenga la cámara de las monedas, o la perspectiva que éstas tengan.

Las monedas son superficies metálicas y, por tanto, reflectantes, lo que provoca que los colores de éstas, no solo varíen en gran cantidad de un entorno a otro, sino que, debido a los reflejos, una moneda de color cobre puede ser confundida fácilmente por una de color oro, etc.

El usuario, sin quererlo, va a estar moviendo la cámara constantemente, por lo que a cada frame, la posición, tamaño y, posiblemente color de las monedas irá variando.

Los relieves de las monedas, además de ser extremadamente variados, son lo suficientemente poco marcados como para complicar enormemente una detección basada en comparación de patrones, además de la dificultad que requeriría el hecho de realizar dicha detección cuando la moneda se encuentra en perspectiva.

Así, una vez planteados nuestros objetivos, nuestras herramientas y los principales problemas a los que nos vamos a enfrentar, dedicaremos ésta semana al diseño de nuestro algoritmo

domingo, 17 de marzo de 2013

Integración en Java de código en C++


El principal problema del uso de las librerías OpenCv para nuestro proyecto fue el hecho de que éstas vienen escritas para el lenguaje orientado a objetos c++, y no para Java.

Sin embargo, el entorno de desarrollo de Oracle permite la importación de bibliotecas escritas en c++ mediante el llamado JNI (Java Native Interface). Mediante éste método, y siguiendo los pasos desritos adelante, se pueden importar en cualquier archivo escrito en java bibliotecas escritas en c/c++, de igual manera que si éstas estuvieran escritas en java, pudiendo hacer uso de objetos, métodos, etc.

Para la consecución de lo comentado anteriormente, se necesitó, evidentemente, guardar en un directorio todas aquellos ficheros en c/c++ que queramos incluir en nuestro código Java. Fue necesario que se guardaran de la siguiente manera:

NombreDelFichero_jni.cpp

Después de ello, se procedió a su preparación para incluirlas en el código Java. Para ello una pieza fundamental fueron los ficheros .mk. Dichos ficheros deben estar incluidos en el directorio (que de ahora en adelante llamaremos jni) en el que se encontraban los ficheros escritos en código nativo. En dichos ficheros fue necesario incluir el siguiente código:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#OPENCV_CAMERA_MODULES:=off
#OPENCV_INSTALL_MODULES:=off
#OPENCV_LIB_TYPE:=SHARED
include ../../sdk/native/jni/OpenCV.mk

LOCAL_SRC_FILES  := <NombreDelFichero_jni.cpp>
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_LDLIBS     += -llog -ldl

LOCAL_MODULE     := <Nombre_Del_Fichero>

include $(BUILD_SHARED_LIBRARY)

En los que sustituyó lo encerrado por llaves por los ficheros que deseamos convertir a biblioteas listas para usar en Java.

Una vez hecho esto, fue necesario hacer el "build" de ellas para poder incluirlas en Java. Dicho proceso lo realiza el ndk-build. El fichero se encuentra en el NDK que se instalaró anteriormente, directamente en el Path principal. Para su uso, y dado que es un fichero Linux, se hizo uso de la herramienta Cygwin.

Cygwin es una herramienta para Windows que permite usar la BashShell (y otra Shell cualquiera) típica de UNIX en un entorno Windows. Puede obtenerse directamente desde:
Una vez descargado, procedimos a su instalación. Por defecto, nos instalará únicamente aquellas funcionalidades descritas como "default". Sin embargo, dado que en nuestro caso necesitaremos hacer un build de un código escrito en c/c++,  descargamos también todo lo relacionado con el apartado Dev (de development). Allí encontramos compiladores de c++ que utilizamos para compilar el código.
Una vez instalado y funcionando, escribimos en línea de comandos:

cd <path del jni>

para situarnos en donde tenemos el código escrito en c++.
Una vez allí, debimos encontrar y ejecutar directamente el ndk-build que encontramos en el path del ndk. En nuestro caso, éste se encontraba en:

C:\Development\android-ndk-r8d-windows\android-ndk-r8d\ndk-build

Una vez hecho esto, es muy común que nos aparezca el siguiente error:

<Path del ndk>/build/gmsl/__gmsl:512: *** non-numeric
second argument to `wordlist' function: ''.  Stop.

Esto es debido a una configuración no estandarizada del ndk-build. Para ello debimos hacer lo siguiente:
Buscamos en el path indicado el fichero llamado __gmsl. Allí, en la línea 512, donde se encuentra la función que nos da error con el segundo argumento, teníamos:

int_encode = $(__gmsl_tr1)$(wordlist 1, $1,$(__gmsl_input_int))

Como nos daba un error por ser éste segundo argumento no numérico, hicimos lo siguiente:
Cambiamos la declaración de la función por:

int_encode = $(__gmsl_tr1)$(wordlist 1,$(words $1),$
(__gmsl_input_int))

Guardamos el fichero y volvimos a ejecutar el ndk-build.
Una vez hecho esto, funcionó correctamente.
Así, con todo realizado correctamente, solo hizo falta cargar las librerías generadas en la carpeta "libs"
Para ello, dentro de nuestro código Java, solo faltaba cargar la librería con el Nombre que escogimos (sin el _jni). Para ello, escribimos:

static{
System.loadLibrary("Nombrede la librería");
}

y, más adelante en el código, declarar como nativos todos los métodos que vamos a usar:

private native double NombreMetodo(args)

viernes, 15 de marzo de 2013

Instalación del Opencv en Eclipse y dispositivo Android

Eclipse

En primer lugar hay que descargar el "Opencv Android SDK" y descomprimirlo en una carpeta para después importarlo desde eclipse, esto generará una serie de paquetes, el primero es la librería Opencv en sí donde están todos los métodos disponibles en Java y el resto son una serie de ejemplos.

Imagen 1: Opencv

Uno de los problemas que nos hemos encontrado llegados a este punto es que todos los proyectos de ejemplo salían con errores. Después de investigar nos dimos cuenta de que no vienen con un Target predeterminado y hay que marcarlo manualmente entrando en las propiedades de cada proyecto y seleccionando la pestaña de Android. En nuestro caso marcamos Android 3.2 (API level 13) que es una de las que funcionan, ya que ni las más antiguas ni la última versión lo hacen.

Una vez corregido, seguían apareciendo varios proyectos con errores, que justamente coincidían con los que utilizaban código en C++ (los que incluyen la carpeta jni). Debido a su extensión, publicaremos la solución a este problema en la siguiente entrada del blog.

Dispositivo Android

En el teléfono móvil o tablet en el que se deseen instalar las aplicaciones hay que descargar el "Opencv Manager", que se puede descargar directamente desde Google Play e instalar como otra aplicación cualquiera. Si no se ha instalado saldrá un error indicando que el paquete no se ha encontrado.

lunes, 11 de marzo de 2013

Conexión PC-Dispositivo Android

En el móvil

Es necesario activar el modo debugger o depuración de USB y permitir ubicaciones falsas. Para ello hay que ir a Ajustes, Aplicaciones y pulsar en Desarrollo. De ésta manera al conectar el movil con el PC se podrán instalar las aplicaciones que se estén desarrollando en eclipse sin tener que generar un .apk, simplemente haciendo clic en el botón "Run" se instalarán.

En el ordenador

Desde el administrador de dispositivos (en Windows) hay que buscar un dispositivo desconocido para encontrar el móvil, desinstalar los drivers del mismo e instalar los que ya se mencionaron en entradas anteriores.
Para ello hay que seleccionar la opción de buscar driver en el ordenador e introducir la ruta donde se ha descargado el Google USB driver (dentro de Android SDK, extras). En nuestro caso, lo probamos en dos dispositivos diferentes, en uno de ellos no se podía instalar, por lo que probamos a modificar el archivo .inf de la siguiente manera:

1.- Haciendo clic con el botón derecho en el dispositivo conectado se elige propiedades, detalles, hardware Ids y se copia la linea que tiene un aspecto parecido a: USB\VID_XXXX&PID_XXXX&MI_XX.
2.- Abriendo el archivo android_winusb.inf hay que localizar lo siguiente (para Windows 64 bits):

[Google.NTamd64]

;S5830
%CompositeAdbInterface% = USB_Install, USB\VID_04E8&PID_689E

;Google Nexus One
%SingleAdbInterface% = USB_Install,USB\VID_18D1&PID_0D02
%CompositeAdbInterface% = USB_Install,USB\VID_18D1&PID_0D02&MI_01
%SingleAdbInterface% = USB_Install,USB\VID_18D1&PID_4E11
%CompositeAdbInterface% = USB_Install,USB\VID_18D1&PID_4E12&MI_01
....

y añadir después de [Google.NTamd64] lo copiado anteriormente de manera que quede:

;Nombre del dispositivo
%CompositeAdbInterface% = USB_Install,USB\VID_XXXX&PID_XXXX&MI_XX

Esta sería la solución a nuestro problema, pero al volver a instalar los drivers ocurría lo mismo de antes, por lo que buscando en Internet encontramos unos drivers específicos para móviles Samsung que si funcionaban actualizándolos como se indicó al principio.


viernes, 8 de marzo de 2013

Instalación de Eclipse y configuración

Instalación de Eclipse

Eclipse es un programa que no necesita instalación, por lo tanto basta con descargarlo , descomprimirlo en algún lugar del equipo y ejecutarlo desde allí. Nosotros hemos elegido la versión Eclipse IDE for Java Developers.

Imagen 1: Eclipse


Instalación de JDK y Android SDK

Simplemente hay que elegir el sistema operativo, descargar la última versión y ejecutar el instalador.
En el caso de Android SDK, se elige la última versión en el siguiente enlace y desde Eclipse, Window preferences, seleccionando la pestaña Android, se elige el Path para el SDK. A continuación se ejecuta el SDK Manager (ver imagen 2) y se selecciona la versión Android deseada, la carpeta "tools" y dentro de la carpeta "extras" el paquete "Google USB driver" para poder conectar el dispositivo más adelante.

Imagen 2: Android SDK Manager

Instalación de ADT y NDK

Dentro de Eclipse hacemos click en "Help", "Install new software" y donde dice "Work with" debemos introducir la siguiente dirección: ADT - https://dl-ssl.google.com/android/eclipse. Si da error hay que probar con la conexión no segura, es decir, sustituyendo https:// por http://. Seleccionamos todo, aceptamos los términos de licencia y reiniciamos eclipse.

Imagen 3: ADT y NDK

martes, 5 de marzo de 2013

Presentación del proyecto

¿Cuáles son nuestros objetivos?


El objetivo principal de nuestro proyecto es el desarrollo de una aplicación Android para móvil o tablet que, a partir del flujo de información recogido por la cámara estándar del dispositivo, ayude y facilite la vida diaria a personas que posean algún tipo de discapacidad sensorial.

En nuestro caso hemos decidido centrarnos en el campo de la visión artificial y, por tanto, de las discapacidades visuales. Dentro de éste grupo se engloban personas con pérdida parcial o total de la visión, personas con deficiencias visuales grandes, y ancianos.

En concreto, nuestra aplicación irá dirigida a facilitar la labor de la diferenciación y reconocimiento de las monedas. De ahí nació la idea de Virtual Wallet, una aplicación que simularía un monedero normal y corriente, que permite de una pasada con el móvil el reconocimiento y cuenta de las diferentes monedas, así de un control de cuánto dinero se tiene. Utilizando la cámara del móvil, se podrá, por ejemplo echarse las monedas sobre la mano, y de una pasada, contar cuántas hay, o, romper una hucha, y, con todo el dinero esparcido, saber el total exacto de dinero que hay en ese momento.

Para implementar la visión artificial necesaria para el reconocimiento, tanto de manera estática, de las diferentes monedas, como de manera dinámica, ya que se va a realizar el reconocimiento de manera contínua sobre un flujo de vídeo, se hará uso de las bibliotecas OpenCV, que incluyen funcionalidades que nos ayudarán a la consecución de nuestra idea.

Por ello, y dado que el entorno de desarrollo en el que vamos a trabajar es muy complejo y requiere gran cantidad de complementos e instalaciones, vamos a utilizar las 2 primeras semanas de trabajo en la instalación, preparación y familiarización con dichas herramientas, para conocer qué podemos y qué no podemos hacer en nuestro dispositivo móvil

viernes, 1 de marzo de 2013

Presentación

Bienvenidos a nuestro blog

Somos Ricardo Gómez Gómez e Iván Duque Camacho, estudiantes de la Escuela Técnica Superior de Ingenieros de Telecomunicación. Hemos creado este blog con el fin de llevar un seguimiento de los progresos realizados en nuestro proyecto para la asignatura Sistemas Digitales 2. En las siguientes entradas iremos describiendo el proyecto que  tenemos pensado realizar así como los pasos que vamos a seguir para llevarlo a cabo.

Un saludo y gracias por vuestra atención.