Mostrando entradas con la etiqueta OpenCV. Mostrar todas las entradas
Mostrando entradas con la etiqueta OpenCV. Mostrar todas las entradas

miércoles, 9 de abril de 2014

La funcion Gaussiana en visión artificial II


Como decíamos en la anterior entrada dedicada a la gaussiana, su empleo  puede servir de ayuda para encontrar características en una imagen sin tener que preocuparnos por la escala. Para ver como funciona vamos a recordar un nuevo operador ampliamente conocido en el mundo de la física, se trata del operador laplanciano:


$\Delta f =\nabla^2f$

O en dos dimensiones y en coordenadas cartesianas:

$\Delta f =\nabla^2f = \frac{\partial^2f}{\partial x^2}+\frac{\partial^2f}{\partial y^2}$ 

Cuando es aplicado sobre imágenes, la función f es $I(x,y)$ y designa la intensidad del pixel de coordenadas x, y. En OpenCV podemos fácilmente calcular el resultado del operador laplanciano si tenemos en cuenta que este puede calcularse como una convolución con un kernel determinado. El siguiente ejemplo muestra lo que hace el operador laplanciano, en este caso con un kernel de 5x5:


Como puede verse la segunda derivada ofrece una gran sensibilidad al ruido. Si lo que se desea es calcular los bordes de los objetos el uso del operador laplanciano  pude ser de utilidad, pero la excesiva sensibilidad al ruido lo hace de difícil uso. No obstante podemos emplear, antes del operador laplanciano, un filtrado gaussiano. Esta nueva convolución es conocida como LoG. A grandes rasgos el filtrado LoG tendrá el siguiente comportamiento al aplicarlo a una imagen:

- Cero a lo largo de los contornos.
- Positivo justo a un lado del contorno.
- Negativo al otro lado del contorno.
- Cero en el interior del contorno.

Cuando se eligen valores de sigma pequeños el filtro LoG es capaz de captar los bordes que se encuentran a menos escala, mientras que con valores grandes se capturan los de mayor tamaño. El siguiente ejemplo muestra un LoG con un sigma de 3.


La parte correspondiente a la cabeza y al borde de las alas se distinguen con facilidad, sin embargo otras zonas mas sutiles son menos nítidas - las antenas, por ejemplo -. Si reducimos el valor de sigma, digamos a 1, tenemos el siguiente resultado:



Ahora logramos que otras características se hagan visibles. Aplicando este método, usando con cuidados los valores del tamaño del kernel y el de sigma, podemos descubrir muchas de las características notables de una imagen. Ahora bien, el LoG es costoso en tiempo de computación, aunque existe una solución si empleamos un camino opcional calculando las diferencias de convoluciones gaussianas con dos diferentes sigmas, por ejemplo $\sigma$ y $k\sigma$. Esto es una aproximación al cálculo de LoG más rápida. Esto se hace para varios valores de sigma, en forma piramidal y disminuyendo la escala - o aumentándola si partimos de un valor de sigma pequeño -. Una vez realizadas las diferencias se buscan los máximos comparándolos con 8 los píxeles vecinos, así como los 9 píxeles de los niveles de escala posteriores y anteriores. De esta forma se consiguen los extremos locales, que potencialmente pueden ser keypoints. Una vez obtenidos estos máximos locales se puede refinar aún más nuestro conjunto de keypoints desechando los que estén por debajo de un cierto umbral. Esta descripción corresponde al método SIFT, descrito en 2004, D.Lowe, University of British Columbia, Came up with a new algorithm, Scale Invariant Feature Transform. El algoritmo esta disponible en OpenCV por lo que no necesitamos implementarlo nosotros mismos. Ahora describimos en pocas líneas como usar SIFT en openCV desde Python:
 
sift = cv2.SIFT()
keypoints, descriptor = self.sift.detectAndCompute(image,None) 

Para representar los keypoints sobre la imagen podemos recurrir a  drawKeypoints, que si le añadimos DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS como flag, nos dibujará los keypoints  con su orientación y un radio proporcional a la "fuerza" del mismo. El resultado es el siguiente:





En la siguiente entrada veremos como podemos recurrir a un algoritmo aún más eficiente que el SIFT.

martes, 8 de abril de 2014

La función Gaussiana en visión artificial I



¿Cuanto se le debe a Johann Carl Friedrich Gauss?. Imposible de cuantificar la aportación del brillante matemático al mundo de la física , de las matemáticas o de la astronomía. Desde los fractales, pasando por el electromagnetismo y como no, la visión artificial son, entre otras muchas ramas de la ciencia, deudoras del ingenio del "príncipe de las matemáticas". La visión por computador es posible hoy en día por estar, en términos newtonianos, aupada a hombros de gigantes como Gauss. En esta entrada vamos a explicar en que consiste la función Gaussiana, y que importancia tiene en la visión por computador.

La función gaussiana tiene el siguiente aspecto en dos dimensiones:

$G(x,y;\sigma ) = \frac{1}{2\pi \sigma ^2}e^-\frac{x^2+y^2}{2\sigma ^2}$

Esta misma función, en una dimensión es considerada la función de densidad de una variable aleatoria cuando se entiende a $\sigma ^2$ como la varianza.  En el caso que nos ocupa el valor $\sigma$ representará la "escala interior", cuyo valor siempre será mayor que cero, esto es evidente si consideramos que no es posible realizar un observación a través de una apertura nula. Por otro lado la parte correspondiente a $\frac{1}{2\pi \sigma ^2}$ sirve  para normalizar la función - en la versión de 1 dimensión esto es muy fácil de ver- . Esto último es importante ya que el volumen que encierra la superficie tiene valor la unidad, independientemente del valor de sigma



Ahora expliquemos lo que es la convolución de dos funciones, y que se define de forma discreta como:

$g(m)*f(m)=\sum _ng(n)f(m-n)$

Y que cumple las siguientes propiedades:

- Conmutativa: $w*f = f*g$
- Asociativa: $w*(f*g) = (w*f)*g$
- Distributiva: $w*(f+g) =w*f+w*g$
- Teorema de la convolución $F(w*g) = F(w)*F(g)$, donde F es la transformada de  Fourier

Una propiedad importante es que la convolución de dos funciones gaussiana es también una función gaussiana, esto se denomina autosimilaridad.

$G_1(x,\sigma_1^2)*G_2(x,\sigma_2^2)=G(x,\sigma_1^2+\sigma_2^2)$

Para hacer esta última propiedad más explicita usamos el siguiente parámetro:


$\widetilde{x}=\frac{x}{\sigma\sqrt{2}}$

En ese caso nuestra función para una dimensión queda como:

$G(\widetilde{x},\sigma)=\frac{1}{\sigma\sqrt{2\pi }}e^-\widetilde{x}^2$

Una vez explicada brevemente el aspecto de la función gaussiana vamos a ver como puede emplearse en visión artificial. Para mejorar el aspecto de una imagen, eliminar ruido o destacar algunas características de la misma se pueden emplear distintos tipos de filtros. Un filtro en el dominio del espacio se realiza sobre todos y cada uno de los píxeles de una imagen, de tal forma que para modificar el pixel i,j se tienen en cuenta los valores de los vecinos. Los filtros en el dominio del espacio pueden ser de dos tipos, lo lineales y los no lineales.  Los filtros lineales usan un kernel o mascara $w(x,y)$ para realizar la siguiente operación que transforma cada píxel f(x,y):

$g(x,y)=\sum_{s=-i}^{i}\sum_{t=-j}^{j}w(i,j)f(x+i,y+j)$ 

Como puede comprobarse esta operación es una convolución.
 
Un ejemplo de filtro lineal es el que sustituye el valor del píxel por la suma ponderada de los píxeles vecinos. Para ello, se realiza una convolución donde la mascara o kernel es como el siguiente - en este caso hemos tomado una máscara de 2x2 -:

$\begin{pmatrix}
 1/2&1/2 \\
 1/2&1/2 
\end{pmatrix}$
 
Apliquemos un filtro de convolución como este usando OpenCV para Python:  

 
kernel = np.ones((3,3), dtype=np.float32)/9.0
cv2.filter2D(img,-1,kernel)

En este caso hemos aplicado una convolución tal que cada píxel es transformado en la media de los 9 vecinos, para ello se utiliza el kernel definido en la primera línea. El resultado de aplicar este filtro se muestra a continuación:



Como puede verse este filtro empaña o emborrona la imagen. Lo que puede lograrse mediante este filtro es suavizar la imagen, eliminado detalles dispersos y de pequeño tamaño. Como se deduce inmediatamente parte de su utilidad se encuentra en su capacidad para eliminar ruido.


El filtro de la media tiene algunas desventajas, ya que es muy sensible a cambios locales y puede crear nuevas intensidades que no aparecen en el original. Empleando un kernel más sofisticado pueden obtenerse resultados más óptimos. Para ello podemos recurrir a la función gaussiana:

$G(x,y;\sigma ) = \frac{1}{2\pi \sigma ^2}e^-\frac{x^2+y^2}{2\sigma ^2}$

El valor de $sigma$ determina la anchura de la campana. Hay que destacar que en virtud de las propiedades vistas anteriormente el filtro gaussiano es separable, es decir podemos aplicar un filtro 2D aplicando dos mascaras, una horizontal y otra vertical.

Si aplicamos un filtro gaussiano, empleando OpenCV:
cv2.GaussianBlur(img,(0,0),1)

Donde el valor (0,0) designa el tamaño del kernel o máscara y el valor 1 es el correspondiente a sigma. Si el valor del tamaño del kernel es cero - como en el caso que nos ocupa - la evaluación se realiza empleando el valor de sigma. También es posible dar distintos valores para la sigma de x y la sigma de y. El resultado es:



Uno de los usos más interesantes de este tipo de filtros es el que va encaminado a facilitar la detección de características. Los algoritmos para extraer esquinas como el propuesto por Harris son invariantes ante las rotaciones pero no lo son con respecto a los cambios de escala, es decir, no detectara esquinas y bordes que no estén el la misma escala. En estos casos se puede recurrir a un nuevo tratamiento, como por ejemplo el algoritmo SIFT - Scale-Invariant Feature Transform - que hace uso del filtrado con kernel gaussiano y que veremos en la siguiente entrada.        
 

lunes, 18 de noviembre de 2013

OpenCV con Qt Creator



Para desarrollar en C++ con las librerías de OpenCV se puede usar cualquier entorno de desarrollo, tal como Visual Studio. Sin embargo, si desarrollamos desde Linux, hay que buscar una opción alternativa entre las muchas que hay, incluida, por su puesto, Vim junto con CMake y un Debugger. Si estamos pensamos en algo más sofisticado, considero que uno de los mejores IDES's multi-plataforma es Qt Creator por diversas razones. Qt es mucho más que una biblioteca para desarrollar interfaces gráficas de usuario, ya que incluye múltiples librerías para diversos propósitos. Ciertamente no es necesario emplear Qt creator para usar la biblioteca de Qt, pero es tentador instalarse todo el paquete, incluyendo el IDE Qt Creator. Una vez que lo pruebas, es fácil engancharse al entorno. No voy a describir aquí las bondades de Qt, para ello es mejor visitar la página de Qt project y comprobar en que nos puede beneficiar. 

Hay dos formas de instalarse Qt en Ubuntu, la primera es vía repositorios y la segunda instalando la última versión desde la página de descarga. La primera es la más sencilla, pero la segunda resulta más interesante si se desea instalar una versión más reciente. Si se opta por esta última, hay que acudir a la página de Qt project y descargar la versión que más nos interese. La instalación de OpenCV es algo más tediosa ya que hay que compilar las fuentes y no voy a describir aquí como realizarla, para ello recomiendo visitar la guía de instalación. Solo añadir un dato, a la hora de llamar a cmake debemos incluir la opcion -D WITH_QT=ON.

Una vez instalado todo, podemos abrir Qt Creator y en file --> New File o Project ... En ese punto tendremos que elegir el tipo de proyecto, lo más sencillo Qt Console Application. Una vez creado el proyecto, tendremos un archivo de proyecto (distinguible por la extensión .pro). A ese archivo debemos añadirle las siguientes líneas:




Ahora, para probar la compilación, podemos escribir el siguiente código en el main




Al compilar y ejecutar tendría que salirnos el mensaje "Hola" en la consola de Qt. Si todo ha salido correctamente nos queda lo más importante e interesante y es darle funcionalidad a nuestro código.


sábado, 13 de abril de 2013

Trabajando con contornos en OpenCV


Nudes with conts. Mike Rubbo


Manejar contornos con soltura es muy importante cuando se hacen trabajos en visión artificial. El contenedor <vector> de la biblioteca estándar facilita mucho el trabajo 


La biblioteca estándar de C++ proporciona una plantilla, denominada vector que se encuentra definida en el espacio de nombres std. La versión de C++ de OpenCV la emplea en numerosas ocasiones, ya sea como vector o como matriz (vector de vectores). Cuando se está trabajando con contornos es imprescindible manejar estos contenedores con soltura.

Por lo general, los contornos que calculamos pueden resultar excesivos, generándonos un gran número que no resultan ser representativos. Las técnicas ya vistas, combinación de  erode y dilate, junto con otras muchas, pueden resolver parte del problema. No obstante, una vez obtenido un conjunto de vectores que representan los contornos, podemos trabajar directamente con ellos. Por ejemplo, la siguiente imagen muestra los contornos obtenidos a partir de una imagen tratada adecuadamente (básicamente transformada en una imagen binaría y tratada con erode y dilate)  :

Nude. Leopold Reutlinger

Como puede comprobarse hay demasiados contornos que no dicen absolutamente nada. Hay varias formas de eliminar estos indeseables conjuntos, la siguiente función ilustra uno de ellos.



 Básicamente consiste en recorrer cada uno de los contornos, empleando para ello un iterador, a continuación se mide la longitud de cada contorno con:



Donde el booleano indica si el contorno es cerrado o abierto (false). De esta manera podemos controlar la longitud de los contornos con  min y max. El resultado, jugando con estas dos variables, es:


Como puede verse hemos eliminado  los pequeños contornos, quedándonos con los ojos, labios, rostro, parte del torso y los brazos, aunque no hemos logrado quitar algunas arrugas del ropaje. Existen otras formas de eliminar contornos, se puede recurrir, por ejemplo, al número de píxeles de cada contorno, para ello emplearemos:





O también, podemos emplear el área de los contornos como discriminador:




 También podemos intentar una combinación de varios criterios, intentando reducir los contornos a los que consideremos más representativos.

jueves, 11 de abril de 2013

Erode y Dilate




Erosionar y dilatar (erode y dilate) son dos métodos muy útiles en el tratamiento de imágenes que tienen gran utilidad en visión artificial.  

 

Erosionar (erode) es una operación realizada sobre imágenes, binarias o en escala de grises. No explicaré aquí la definición matemática, que puede verse en la correspondiente entrada de la wikipedia, pero si contaré resumidamente lo que hace. En una imagen binaria, representada como una matriz de unos y ceros (Matriz A), el proceso de erosión por un vector V formado por unos, consistiría en ir colocando el 1 central del vector en cada píxel iluminado (es decir, con valor 1 en la matriz); si el vector entra totalmente en la zona iluminada el píxel se respeta, de otra forma se convierte en 0. Es una especie de goma de borrar que solo borra cuando la goma sobresale un poco de la zona pintada. En una imagen con fondo blanco (valor 1) engrosará los píxeles representados en negro (valor 0). El siguiente ejemplo muestra la acción de esta operación; la imagen de la derecha es la original:


     

OpenCV proporciona un método llamado erode que realiza este trabajo. A continuación lo esencial del código  para generar el simple ejemplo de arriba:

                     cv::erode(image, image2, cv::Mat());
                     while(true)
                    {
                          cv::imshow("Erode", image2);
                          cv::imshow("NoErode", image);
                          if(cv::waitKey(30) > 0) break;
                    }


Donde cv::Mat() es en este caso una matriz vacía (la matriz que "erosiona"). Esta matriz vacía, que OpenCV interpreta como una matriz de 3x3, puede ser de cualquier otro tamaño. 

Este método tiene muchas utilidades en el tratamiento de imágenes, desde el punto de vista de la visión artificial se me ocurren también unas cuantas. Supongamos que tenemos una matriz que representa una serie de contornos, pero que por algún motivo algunos han quedado inconexos, aplicando un erode podemos unir algunos de los contornos más cercanos, tal y como muestra el ejemplo siguiente






  
Si una primera erosión no es suficiente podemos repetirla sucesivas veces sobre la misma imagen para lograr unir todos los contornos. No es necesario llamar a la función varias veces, ya que el propio método erode de OpenCV nos permite elegir el número de iteraciones. En ejemplo que nos ocupa y tras tres iteraciones el resultado el siguiente:




Como puede verse hemos unido todos los pequeños contornos, eso sí, a costa de engrosar una serie de puntos no representativos.  

La operación inversa o reciproca es la "dilatación" o dilation. Encontramos una definición matemática muy interesante en la wikipedia que deberíamos revisar si realmente deseamos conocer el proceso. No obstante no debemos tomar al pie de la letra en concepto de operación inversa (en sentido matemático), ya que no siempre la imagen dilatada o erosionada puede transformarse en lo que era aplicando la operación inversa, máxime si el proceso borra grupos enteros de píxeles. Eso le confiere una interesante aplicación a la combinación erode-dilate o dilate-erode. Por ejemplo, el siguiente código aplicado a la imagen de la izquierda permite eliminar los contornos más pequeños, eliminar los puntos y engrosar lo que podríamos considerar como más representativo:

                    cv::dilate(image, image2, cv::Mat(),cv::Point(-1,-1),2);
                    cv::erode(image2, image2, cv::Mat(),cv::Point(-1,-1),5);







Como se puede intuir las aplicaciones son múltiples; eliminar ruido, aislar elementos o unir zonas próximas, localizar los puntos más intensos, etc... Cada uno de los dos métodos aquí expuestos tienen un número mayor de opciones que aquí no se contemplan, para una referencia más exhaustiva no hay que dejar de visitar la página de la documentación de OpenCV.  




 

miércoles, 10 de abril de 2013

Librerias de visión artificial




Librerias (biblioteca central UNED)

 


Son muchas las librerías que pueden usarse a la hora de desarrollar aplicaciones de visión artificial. 

A continuación citaremos las más importantes: 

 

Es importante elegir una librería como herramienta central a la hora de hacer un desarrollo de visión artificial , no obstante sería un error considerar solo una. Es muy posible que las deficiencias de una pueda ser resueltas por otra, o que para un problema concreto resulte aconsejable usar una librería específica. 

Torch3vision:

Escrita en C++ dispone de procesamiento básico de imágenes, algoritmos de extracción de características, así como detección de caras empleando  Haar-like features. Es libre con licencia BDS. 

En su página podemos encontrar documentación, tutoriales  y algunos ejemplos. El problema es que parece que su desarrollo se encuentra parado, la última versión, la 2.1, data del 2007, toda una eternidad.




VLX:

También esta escrita en C++, incorpora la mayoría de los algoritmos habituales en visión artificial. En realidad no es una única librería, sino más bien un conjunto de ellas que ofrecen una muy completa funcionalidad. Es una opción a tener en cuenta ya que dispone de unas características muy atractivas, una de ellas es la posibilidad de usar únicamente las librerías que nos resulten de utilidad ya que no hay dependencias entre ellas. 

Sigue muy activa y en su página podemos encontrar tutoriales y muy diversa información.



RAVL:

De nuevo escrita en C++, nos proporciona los elementos básicos de una librería de visión artificial. Incorpora algunos elementos diferenciadores tales como soporte para herramientas de audio o interfaces de usuario basadas en GTK. Promete ser de fácil programación y de no requerir grandes conocimientos en C++. 

Actualmente soporta Linux (x86_64/i386) y Windows (solo i386). Su licencia es tipo GNU. 




LTI-lib:

Librería orientada objetos para visión artificial. Dispone de más de 500 clases, entre las que se incluyen clases para  álgebra lineal, clasificación, procesamiento de imágenes y muchas otras características.  

Ha sido testada bajo Linux (gcc) y en Windows NT (Visual C++). Su licencia es GNU Lesser General Public License.

- http://ltilib.sourceforge.net/doc/homepage/index.shtml

OpenCV:

Sin lugar a dudas la más conocida. Más de 500 algoritmos entre los que se incluye funciones de propósito general  para procesamiento de imágenes, descripciones geométricas, segmentación, seguimiento, etc... Bajo mi punto de vista la más completa, activa e imprescindible. Una característica añadida es la posibilidad de emplear las capacidades de computación de las GPU. También permite el uso de las librerías de Intel (Integrated Performance Primitives, IPP) que incluyen una larga lista de funciones optimizadas para procesadores Intel. Si las librerías se encuentran instaladas OpenCV hace uso de las mismas, mejorando la velocidad en los cálculos.

Dispone de muchísima documentación, incluyendo algunos libros. Sobran los ejemplos, dentro y fuera de la página. Está disponible para Linux, Windows y Android. Se puede programar en C++, C, Python y Java. 


Existen algunas librarías más pero no podría decir mucho de ellas. Las citadas son las más completas y seguramente suficientes para la gran mayoría de los propósitos. La información que he ofrecido puede ser es muy somera, pero en los enlaces se podrá localizar toda la información indispensable.

Para comparar de forma más objetiva las distintas librerías podemos recurrir al libro "Learning OpenCV: Computer Vision with the OpenCV Library". Este nos ofrece una evaluación de la velocidad de cálculo para algunos algoritmos habituales, tales como cambiar el tamaño de una imagen, o un Optical Flow para seguimiento de 520 puntos. La siguiente gráfica muestra los resultados, valores mayores indican más tiempo de proceso. El resultado es evidente, OpenCV y OpenCV+IPP ganan por goleada.  
























Las versiones empleadas son un tanto antiguas, OpenCV 1.0, siendo la última versión cuando se escribe esto la 2.4. Debido al gran impulso que esta recibiendo OpenCV estas diferencias seguramente se habrán incrementado. Esto no significa que las librerías más lentas deban desecharse, la comparativa es de velocidad y no de eficiencia. Lo más adecuado sería usar como librería principal OpenCV y auxiliarmente alguna otra cuando encontremos características especiales no presentes en OpenCV.