11 de enero de 2011

OpenCV 2 Computer vision application Programming cookbook

09/Nov/2011

Hace unos cuantos días instale la versión mas reciente de Ubuntu (Ubuntu 11.10 oneiric ocenilot) y la verdad me ha gustado bastante tanto en diseño como en desempeño. Además desinstale completamente de mi portátil  Windows y solo lo uso en mi PC de escritorio para mi trabajo con el kinect.

La nueva versión trae bastantes novedades y para mi agradable sorpresa, la instalación de OpenCV 2.3.1 en Ubuntu 11.10 se realiza exactamente igual que en Ubuntu 10.10, por lo que la instalación en esta versión se puede realizar tal como lo describo aquí.

Volviendo al tema, un amigo del blog (Islen) me recomendó echar un vistazo al libro "OpenCV 2 Computer vision application Programming cookbook" de Robert Laganiere que, para los que no lo saben, es el libro mas reciente dedicado al aprendizaje de OpenCV en su versión 2.x.  

OpenCV 2 Computer vision application programming cookbook


A mi parecer es un libro excelente. Por un lado, y a diferencia del libro que había trabajado hasta el momento en el blog, este utiliza la interfaz C++ de OpenCV. Además es mas apropiado para personas que hasta ahora comienzan ya que cada código incluido en el libro es explicado linea por linea para una total comprensión.


Algo que me pareció demasiado curioso (y desagradable) es que no se nombra por ninguna parte a Python, ni siquiera una simple mención. Así que por el momento, todavía no contamos con bibliografía apropiada para nuestro aprendizaje de OpenCV con Python.

Tenia pensado hacer una reseña a profundidad del libro pero en la plaga Tux  hacen una excelente que recomiendo totalmente.

Por ultimo, voy a comenzar a complementar los códigos que voy convirtiendo del anterior libro con los de este. De esta manera todos vamos a tener mas ejemplos para poder mejorar nuestro aprendizaje en la materia.

¿Como funciona el sensor de una camara digital?

Un elemento vital de cualquier sistema de visión artificial es la cámara digital que permite obtener la imagen que luego sera tratada mediante algoritmos y que finalmente permitirán obtener características relevantes para tomar decisiones en un sistema.

Aunque no es necesario si es importante que conozcamos un poco acerca de como las cámaras digitales pueden transformar los objetos que vemos en nuestro ambiente en matrices llenas de datos.


- ¿Que es un megapixel? 

A pesar de que escuchamos o leemos casi todos los días sobre nuevas cámaras digitales o los nuevos modelos de celulares con cámara y nos maravillamos con sus características, realmente no es de conocimiento general que significa cada una de de esas características que nos nombran al momento de seleccionar un producto de este tipo.

Entre estas características se encuentran el numero de megapixeles con los que cuenta la cámara. ¿Pero que es exactamente un megapixel?... Bueno por una lado sabemos que un píxel es el elemento mas pequeño de una imagen digital ( píxel viene de picture element que significa elemento de imagen) por lo tanto podemos inferir que un megapixel es un millon de pixeles.

Las cámaras digitales tienen una parte conocida como sensor de imagen digital. Este sensor tiene una cantidad muy grande de huecos muy pequeños que cuando se exponen a la luz almacenan fotones. Cada uno de estos huecos va a formar un pixel de la imagen resultante que nosotros vamos a ver. Por lo tanto si una cámara tiene un sensor con 3888 píxeles de ancho y 2592 píxeles de alto quiere decir que la cámara cuenta en total con 10,077,696 píxeles o lo que es lo mismo 10.07 megapíxeles. (3888*2592=10,077,969).

- ¿Como funciona este sensor?

El sensor de una cámara digital se compone básicamente de 3 capas.

1- El substrato del sensor


Este es un material de silicio que mide la intensidad de la luz. Este sensor no es para nada plano, tiene cierta cantidad de huecos que permiten atrapar la luz al momento de tomar la foto para ser medida posteriormente. Cada uno de estos huecos es un píxel en la imagen resultante.

2- Filtro de Bayer


Esta es una capa de colores que esta pegada al substrato para permitir grabar los colores. El sensor solo permite medir la cantidad de fotones que entran mas no permite saber el color de cada uno de estos fotones. Por lo tanto el sensor  por si solo obtiene información en escala de grises.

El filtro de Bayer fue desarrollado por el doctor Bryce E. Bayer en 1976 mientras trabajaba para Kodak. El invento una matriz de colores (rojo, verde y azul) que permite capturar la información del color que entra en el sensor de una camara.

Filtro de Bayer
El filtro de bayer (también conocido como matriz de filtro de colores o CFA "color filter array") actúa como una pantalla que solo deja pasar al sensor los fotones de cierto color. Si se mira la imagen se puede notar que el filtro esta formado por filas que alternan entre el rojo/ verde y azul/verde dando por resultado que los filtro de Bayer estan compuestos en su mayoría por el color verde (hay dos veces mas verde que azul o rojo) esto es debido a que el ojo humano es mas sensible al verde (Investigaciones realizadas por Sony e implementadas en su sensor CCD RGBE demuestran que el ojo humano es mas sensible a una variedad especifica del verde, exactamente el verde esmeralda).

El hecho de que el filtro solo deje pasar un solo color, implica que cada píxel que la cámara obtiene posee un solo color, por lo que se esta perdiendo 2/3 de la información. Pero de esta manera que puede parecer no muy eficiente, cada pixel sabe cuanta cantidad de luz tiene y de que color es. Por ejemplo, si un píxel que tiene encima suyo un filtro rojo y el sensor almaceno 5000 fotones, se puede inferir con seguridad que estos 5000 fotones  que tiene almacenados son de una luz roja por lo que se puede proceder a hacer el calculo de intensidad (cada color se ubica en una escala de 0 a 255 osea 1 byte).

Vista de las cavidades del sensor.

Sensor unido al filtro de Bayer dejando pasar la luz de un solo color por cada píxel.

3- Microlentes


La tercera capa que compone el sensor de una cámara son los microlentes. Estos son pequeños lentes ayudan a captar la máxima luz posible en cada píxel.
Como se ve en las imágenes anteriores existe un espacio entre píxel y píxel por lo que la luz que llega a estos espacios es información que se esta perdiendo. Por esta razón se colocan lentes que ayuden a compensar la existencia de estos espacios y ayuden a redireccionar la mayor cantidad de luz posible.

Sensor con el filtro de Bayer y los microlentes.

---------------------------------------------------------------------------------------------------------------------------------

- Si los sensores solo captan 1/3 de la luz y sus colores, ¿Como es que nosotros vemos las imágenes digitales con todos los colores?

Existen métodos que permiten reconstruir la información faltante basándose en la información existente que provee el sensor de la cámara.

El método usado se llama interpolación cromática (Gracias por el termino al amigo que lo puso en los comentarios). En simples términos el proceso consiste en lo siguiente: Imaginemos que la cámara trata cada conjunto de píxeles de 2x2 como si fuera uno solo (dos verdes, uno azul y uno rojo) de la siguiente manera

Sección que toma la cámara para determinar el color real.
Usando estos 4 píxeles la cámara puede estimar el color real basándose en los niveles de intensidad de color de cada píxel.

Para explicar el proceso cada píxel va a tener un nombre. G1, B1, R1 y G2.
Después de tomarse la foto los píxeles están llenos de fotones, en ese momento comienzan los cálculos (¿se dan cuenta que cuando se toma una foto con una cámara digital, pasa un tiempo entre tomar la foto y ver la foto en el display de la cámara?... ese es el tiempo que se toma la cámara en realizar el procedimiento que describiré a continuación).

Tomemos como referencia G1 y miremos que sucede. G1 habla con B1, averigua cuantos fotones azules tiene y los suma a sus fotones verdes. Luego G1 habla con R1 y con G2 y hace exactamente lo mismo.Ahora G1 usa esa información que recogió  para mostrar el color verdadero que le corresponde haciendo una especie de promedio. A la vez, mientras G1 le preguntaba a cada píxel su cantidad de fotones, también les entregaba la información de cuantos fotones tenia él para que los otros píxeles realizaran el mismo calculo que el hizo.

Pero esa no es toda la historia, ahora imaginemos que la cámara hace grupos de 3x3 para realizar el procedimiento anterior lo cual quiere decir que cada píxel va a tener mas información para realizar su calculo por lo que va a ser mas exacto (cada píxel tiene 8 alrededor que le van a brindar información para realizar sus cálculos).
Basándonos en la distribución de los colores del filtro de Bayer podemos saber que se van a presentar 3 casos.

Cuando el píxel del centro es el verde

Cuando el píxel del centro es el rojo

Cuando el píxel del centro es el azul
Esta no es la historia completa, pero es lo básico. La forma en que cada cámara realiza los cálculos varia según el modelos, la marca y el tipo de sensor.

- ¿Y que pasa con los píxeles que están en los bordes?, ¿Como realizan los cálculos si tienen menos píxeles alrededor?


Digamos que nos paramos en el píxel de la esquina superior izquierda del sensor. Si lo miramos bien, nos daremos cuenta que solo tiene 3 píxeles a quienes pedirle la información. De todo esto nos damos cuenta que los píxeles en las esquinas y en los bordes están en desventaja al tener menos información para realizar sus cálculos.
Esta es precisamente la diferencia entre los píxeles reales y los píxeles efectivos. Los píxeles reales son la cantidad de píxeles que tiene un sensor. Sin embargo no todos los píxeles son usados para formar la imagen digital. Los píxeles de los bordes y las esquinas son usados para brindar información a los píxeles de adentro mas no para ser mostrados en la imagen ya que carecen de información lo cual los hace mas inexactos. Discriminando estos píxeles podemos afirmar que cada píxel usado para crear la imagen contiene la misma cantidad de información para crear su color real.

Por esta razón, alguna veces cuando leemos las especificaciones de una cámara, dice píxeles efectivos: 10.1 megapíxeles , número total de píxeles: 10.5 megapíxeles.
 
Los 400,000 píxeles de diferencia son los usados para crear la imagen pero no son mostrados en la imagen resultante. Por lo que ahora sabemos que una cámara tiene mas píxeles de los que son mostrados realmente en la imagen que nos provee.

Traducción y Adaptación. Rafael Poveda

Fuentes:
http://www.sensorland.com/HowPage090.html
http://www.cambridgeincolour.com/tutorials/camera-sensors.htm
http://www.imaging-resource.com/NEWS/1181811769.html
http://es.wikipedia.org/wiki/Sensor_de_imagen
Interpolación de imagenes - Diego Mauricio Rivera, Universidad Nacional de Colombia

Mostrar imagen en escala de grises

Existen dos formas principales para poder obtener una imagen a color y convertirla en escala de grises.

1) Cargarla directamente como una imagen en escala de grises

Código:

import cv
imagen_gris=cv.LoadImage("python.png",cv.CV_LOAD_IMAGE_GRAYSCALE)
cv.ShowImage("imagen",imagen_gris)
cv.WaitKey(0)

El código es muy similar al que se usa cuando se quiere cargar una imagen. La única diferencia es el segundo argumento que se le pasa a la función LoadImage, El cual indica que queremos que la imagen se cargue en escala de grises.

2) Cargarla y luego convertirla a escala de grises

Código:

import cv
imagen=cv.LoadImage("python.png")
imagen_gris=cv.CreateImage(cv.GetSize(imagen),cv.IPL_DEPTH_8U, 1)
cv.CvtColor(imagen,imagen_gris,cv.CV_RGB2GRAY)
cv.ShowImage("imagen",imagen)
cv.WaitKey(0)

Este código nos brinda la posibilidad de mantener la imagen original a color en una variable, y la imagen en escala de grises en otra. Primero debemos crear el contenedor de la imagen en escala de grises con la función cv.CreateImage la cual necesita 3 argumentos: tamaño, profundidad, canales.
El tamaño es una tupla que contiene los valores del alto y el ancho de la imagen, la profundidad se refiere a la profundidad de color que usara la imagen, y por ultimo como nos referimos a una imagen en escala de grises solo necesita un canal a diferencia de las imágenes RGB que necesitan 3.


Luego de creada la imagen, usamos la función cv.CvtColor para convertir  "imagen" a escala de grises y meterla en "imagen_gris".


El resultado en ambos casos es el mismo.

Resultado:  




Descargar el código

21. Primer acercamiento al Kinect

Existen varias opciones para poder trabajar con el kinect entre las mas conocidas se encuentran las librerias libfreenect, OpenNI, y el recientemente lanzado SDK Kinect de microsoft (versión Beta). Cada una de ella tiene sus ventajas y sus desventajas las cuales son importantes tener en cuenta al momento de realizar una elección para trabajar con ellas.

El SDK Kinect de microsoft por ejemplo, es la unica forma de trabajar en la que se tiene soporte para los microfonos del kinect ademas de realizar rastreo de esqueleto sin inicialización entre otras caracteristicas. Como grandes desventajas esta la restricción para solo usar el sdk (software development kit) para usos no comerciales, ademas para hacer uso de esta herramienta hay que tener instalado visual studio 2010 en cualquiera de sus versiones y lo que a mi parecer es la mayor limitación es el hecho de que solo se puede usar en windows 7 nativo (es dedir, nada de maquinas de visualización, ni wine, ni nada por el estilo). La instalación es supremamente sencilla como cualquiera en windows y viene con un par de ejemplos bastantes interesantes en los que destaca un pequeño juego. Otra grandisima desventaja (por lo menos para mi) es el hecho de solo poder trabajar en C++ o C#. Para mayor información sobre el sdk y sus caracteristicas esta es la pagina

Imagen del sdk para el kinect
La otra gran alternativa se llama OpenNI. OpenNI es un framework que permite trabajar con el "juguete" de Microsoft. OpenNI y Sensor Kinect permite obtener las información basica que entrega el kinect como lo son la imagen RGB y la imagen de profundidad. Ademas se cuenta con el middleware NITE que proporciona características de alto nivel. Es decir OpenNI obtiene la información del kinect y NITE toma esa información y realiza los algoritmos de segmentación, tracking y demas.

OpenNI puede ser usado tanto en Windows como en Ubuntu (no estoy seguro sin en Mac funcionara) lo cual se convierte en un punto a su favor además de no tener limitación para su uso en aplicaciones comerciales.

OpenNI no provee acceso a los microfonos del kinect ni tampoco al motor del mismo. Por otro lado y a modo personal no pude hacer funcionar esta herramienta en mi equipo tanto en Windows como Ubuntu por lo que no puedo hablar mucho de ella, solo que la instalación me parece la mas engorrosa de las 3 herramienta. Un proceso largo que incluye descargas, modificación de archivos XML, instalaciones, en fin.

Logo de OpenNI

Por ultimo se encuentra las librerias libfreenect desarrolladas por la comunidad Openkinect. De las 3 es la única que no cuenta (por el momento) con características de alto nivel. Libfreenect solo provee acceso a la imagen RGB y a la de profundidad del kinect, pero compensa esto dando acceso tambien al control del LED del kinect y al motor (cosa que no hace ninguna otra herramienta hasta el momento). Además cuenta con una gran comunidad y multiples wrappers para diferentes lenguajes entre ellos nuestra querida culebrita lo cual hace esta librería la ideal para trabajar con python, kinect, y OpenCV. Como gran desventaja el wrapper de libfreenect para python no funciona en windows actualmente pero puede ser utilizado con otras herramientas como Matlab.

Logo de la comunidad Open Kinect
En esta entrada voy a mostrar la instalación de las librerias freenect en Ubuntu 10.10

Para poder instalar el kinect hay que escribir las siguientes lineas en la terminal de la misma forma que se hizo en la entrada anterior.


sudo apt-get install git-core cmake libglut3-dev pkg-config build-essential libxmu-dev libxi-dev libusb-1.0-0-dev
git clone https://github.com/OpenKinect/libfreenect.git
cd libfreenect
mkdir build
cd build
cmake ..
make
sudo make install
sudo ldconfig /usr/local/lib64/
 
Por ultimo vamos a ingresar nuestro nombre de usuario (importante: Deben cambiar "nomobre_de_usuario" por su
nombre de usuario tal y como aparece en la terminal.) 

sudo adduser nombre_de_usuario video

Vamos a verificar si todo esta en orden. 
 
Primero entro a la carpeta libfreenect para poder acceder a los ejemplos que 
vienen con los drivers. La carpeta por defecto es /libfreenect/build/bin recuerden 
abrir los ejemplos con permisos de root con la instrucción sudo.
 
 
 He aqui otro ejemplo:
 

 
En la proxima entrada explicare el proceso de instalación de los wrapper para python para poder 
usar python, opencv y kinect.
 
Imagen de profundidad obtenida con libfreenect
  
Actualización:
Para los que quieran ver los videos con la resolución original aqui esta el link de descarga
http://www.mediafire.com/?bx3h2io3qxg10gz

Instalando OpenCV 2.3.1 en Ubuntu 11.10

La salida de la nueva versión de ffmpeg trae algunos problemas de compatibilidad con OpenCV 2.3.1 por lo que el anterior tutorial de instalación esta desactualizado.
Gracias a Giancarlo Colosante, un amigo del blog, que creo un script que simplifica el proceso de instalación de OpenCV 2.3.1 y al cual me permití realizarle unas modificaciones.

Podemos descargar el instalador desde aqui.

Primero le damos permisos de ejecución dando clic derecho encima del instalador en la pestaña permisos. Luego le damos doble clic y elegimos ejecutar desde la terminal. Nos pedirá que ingresemos nuestra contraseña de administrador y que aceptemos la instalación.

Alternativamente podemos acceder a la carpeta donde descargamos el script, y luego de darle permisos de ejecución  ingresamos desde la terminal y lo ejecutamos como:

"sudo ./instalacion.sh" y cuando nos pregunte si deseamos instalarlo le damos "s" y enter. Después nos preguntara nuestra clave de administrador y esperamos a que el proceso se complete.




Si quieren realizar el proceso de forma manual, el script esta divido en secciones fáciles de identificar por lo que no sera ningún problema.

Me gustaría recibir sus comentarios para saber si el script funciona correctamente.

Hasta luego

Python (x,y)

Recientemente me encontré con una distribución de Python llamada Python(x,y) el cual es un programa lleno de librerías para Python orientado a facilitar el trabajo de científicos e ingenieros poniendo a la mano todas las herramientas necesarias. El siguiente gráfico resume muy bien todo lo que contiene Python(x,y)

Prácticamente se puede hacer de todo con el, ademas que se instala tan fácil como cualquier programa en windows lo que nos evita el trabajo de estar bajando cientos de librerías y lidiar con sus diferentes formas de instalación.

(Actualización 8/Oct/11)
La versión mas reciente de Python(x,y) la 2.7.0.2 viene con OpenCV 2.3.1 listo para trabajar con Python por lo que el proceso de instalación de OpenCV en windows se vuelve extremadamente sencillo.

En esta entrada voy a explicar paso por paso como se debe instalar Python(x,y) en Windows 7 y como podemos hacer uso de OpenCV 2.3.1.

1) Descargando lo necesario

Vamos a asumir que no tenemos Python o alguna de sus librerías instaladas (si lo están, seria recomendable desinstalarlas todas aunque no creo que exista ningún conflicto). Por defecto Python(x,y)  2.7.0.2 instala Python 2.7.2 por lo tanto todas las librerías están dirigidas hacia esta versión.

Lo único que debemos descargar es Python(x,y) el cual pesa 391.8 mb - Link: python (x,y)  

2) Instalación





Personalmente les recomiendo que realicen las instalación "Full" pero también esta la opción de escoger las librerías que deseamos instalar. Noten en la siguiente imagen la opción de escoger instalar OpenCV 2.3.1



El resto es cuestión de que le demos un par de siguientes y comienza el proceso de instalación.


Y listo ya tenemos instalado Python (x,y) en nuestro computador y podemos tener acceso a cada una de las librerías que nos ofrecen ya que todas están excelentemente documentadas.

Lo único diferente de esta forma de instalación, es la forma en que importamos el modulo de OpenCV en un script de Python. En vez de usar solamente import cv, debemos usar import cv2.cv as cv.


Si se dan cuenta cuando hago import cv, Python me bota un error y me dice que ese modulo no existe. Pero si lo hago con import cv2.cv as cv el programa continua sin ningún problema. Por lo que para poder usar los scripts que hallan realizado con anterioridad deberían modificar la linea de importación.

Por otro lado Python(x,y) viene con un IDE llamado spyder el cual tiene múltiples características como auto completado, coloreado de sintaxis, explorador de variables (como Matlab) y muchas mas. Ademas todas las librerías de Python (x,y) vienen con su correspondiente documentación, ejemplos y algunos con tutoriales.



Pantallazo de spyder funcionando con OpenCV.

En resumen, Python(x,y) es una excelente recopilacion de librerias que hace que el trabajo cientifico con Python extremadamente sencillo en Python. Ademas se convierte en la primera opcion de instalacion de OpenCV 2.3.1 en windows.

Instalando OpenCV 2.3.1 en Fedora 16

13/Nov/11

Esta semana cambie mi distribución gnu/linux por Fedora 16 'verne'. Basicamente porque me encanta Gnome 3 y porque el rendimiento de Ubuntu con esa versión de Gnome me dejo bastante que desear, por lo que decidi cambiarme finalmente.

Logo de Fedora

Aunque todas las distribuciones tengan muchas cosas en común, aun me estoy acostumbrando a todo y lo primero que hice es tratar de instalar OpenCV 2.3.1 y hacerlo funcionar con Python y mi IDE favorito Spyder.

Hasta ahora estoy conociendo Fedora por lo que si hay cosas que no estan correctas espero me disculpen, sin embargo, la idea es funcionar como una especie de guia para las personas que lo necesiten.

1) Lo primero a realizar y si como yo, acaban de instalar Fedora 16 desde un Live CD, es completar los repositorios de software y activar otras cosas.

Aquí esta una excelente guía que nos indica como llevarlo a cabo

2) Con el siguiente comando instalamos todo lo necesario (OpenCV, Python-opencv, Numpy, Scipy, Matplotlib, Sympy, y el IDE Spyder) para tener nuestro entorno listo y funcional.

sudo yum install numpy scipy sympy python-matplotlib opencv opencv-python spyder

Y todo esto apenas pesa 38 Mb.

3) Para probar podemos escribir python en la terminal y tratamos de importar OpenCV.

import cv

Si no muestra ningún error quiere decir que todo esta instalado correctamente.

Aquí esta un pequeño ejemplo en spyder para verificar que todo funciona correctamente

Creación de una ventana con OpenCV 2.3.1 y Python

Segmentación de colores

Antes que nada quiero comentar que en opencv 2.2 existe un bug que no permite usar la camara web ni manejar video. Parece que la utilización de las librerias se puede arreglar modificando una parte del codigo de las librerias usando C, pero no he podido encontrar solución para python por lo que he decidido usar opencv 2.1 y python 2.6 mientras aparece una solución o sale una nueva versión.

Hoy estuve leyendo una guia muy interesante sobre segmentación de color en matlab y me puse a pensar como hacer algo similar en python. La idea es simple: Cargo una imagen y le hago clic en cualquier lugar de ella y de esa manera obtengo los valores que contiene ese pixel que como ya sabemos, para una imagen a color, son tres. Algo muy importante que noté es que las imágenes RGB en opencv no son como uno esperaría. Me refíero a que si tengo una imagen RGB se entiende que el primer canal es el rojo, el segundo el verde y el tercero el azul. Pero resulta que no es así, ya que los canales de una imagen en opencv se organizan de la siguiente manera: BGR (azul, verde y rojo). La verdad no conozco la causa pero es muy importante saberlo y estar conciente de ello.

Voy a mostrar el codigo y el resultado con una imagen con colores exactos.

Codigo:

import cv
tol=30
# defino la función que se ejecuta cuando hago clic en algún lado
def evento_mouse(event,x,y,flags,param):
    if event==cv.CV_EVENT_LBUTTONDOWN:
        pixel=cv.Get2D(imagen,y,x) 
        #En pixel guardo los valores del pixel y luego los separo en las
        #variables azul, verde y rojo
        azul=pixel[0]
        verde=pixel[1]
        rojo=pixel[2]
        print azul,verde,rojo # las imprimo para saber sus valores
        cv.InRangeS(imagen,(azul-tol,verde-tol,rojo-tol),(azul+tol,verde+tol,rojo+tol),temporal)
        # La funcion InRangeS me permite umbralizar las imagenes dando un rango determinado
        # en este caso el rango es el valor de el pixel menos la tolerancia y el valor del pixel
        # mas la tolerancia. Si los resultados no son los esperados se debe modificar la tolerancia
        cv.ShowImage('Color',temporal)
        #Muestro la imagen resultante. La cual va a mostrar en blanco el color que seleccione
        #y en negro cualquier otro color
    
imagen=cv.LoadImage('C://prueba.png')
cv.ShowImage('Prueba',imagen)
temporal=cv.CreateImage(cv.GetSize(imagen),cv.IPL_DEPTH_8U,1)
cv.SetMouseCallback('Prueba',evento_mouse)
cv.waitKey(0)

Resultado:


Cuando le hago clic en uno de los circulos de la imagen a color, en la otra ventana el circulo que es de ese color se vuelve blanco y el resto queda negro. en este ejemplo el resultado es perfecto a causa de que el rojo es puro (solo tiene una tonalidad) voy a usar el mismo codigo pero con otra imagen.
Resultado:

En este seleccione el color piel
En esta seleccione el pelo

Como se puede ver, con esta imagen los resultados tambien son buenos pero no tan exactos como en el de los circulos. Este método es una opcion si el objeto que se quiere obtener de la imagen es de un color uniforme y la iluminación es optima.


El siguiente es un ejemplo de segmentación usando imagenes en tiempo real. Lo que voy a hacer es seguir solamente la luz de mi celular en un fondo con poca luz. (perdón por la calidad del video)

Codigo:

import cv
tol=30
# defino la función que se ejecuta cuando hago clic en algún lado
def evento_mouse(event,x,y,flags,param):
    if event==cv.CV_EVENT_LBUTTONDOWN:
        pixel=cv.Get2D(imagen,y,x)
        #En pixel guardo los valores del pixel y luego los separo en las
        #variables azul, verde y rojo
        azul=pixel[0]
        verde=pixel[1]
        rojo=pixel[2]
        print azul,verde,rojo # las imprimo para saber sus valores
        while True:
            imagen2=cv.QueryFrame(captura)
            cv.InRangeS(imagen,(azul-tol,verde-tol,rojo-tol),(azul+tol,verde+tol,rojo+tol),temporal)
            # La funcion InRangeS me permite umbralizar las imagenes dando un rango determinado
            # en este caso el rango es el valor de el pixel menos la tolerancia y el valor del pixel
            # mas la tolerancia. Si los resultados no son los esperados se debe modificar la tolerancia
            cv.ShowImage('Umbral',temporal)
            #Muestro la imagen resultante. La cual va a mostrar en blanco el color que seleccione
            #y en negro cualquier otro color
            if cv.WaitKey(30)==27:
                break

captura=cv.CaptureFromCAM(0)
while True:
    imagen=cv.QueryFrame(captura)
    temporal=cv.CreateImage(cv.GetSize(imagen),cv.IPL_DEPTH_8U,1)    
    cv.ShowImage('Color',imagen)
    cv.SetMouseCallback('Color',evento_mouse)
    if cv.WaitKey(30)==27:
        break 

Resultado:

Instalación de paquetes para Python

Una de las cosas que mas me gusta de Python es la cantidad de módulos que se le puede instalar para aumentar su funcionalidad. Existen varias formas para poder obtener librerias adicionales y extender la funcionalidad de python de acuerdo a nuestras necesidades.

Por ejemplo, PyPI es una pagina que tiene una recopilación de paquetes con múltiples propósitos listos para descargarse e instalarse. Actualmente PyPI cuenta 16329 paquetes actualizados constantemente. Para poder administrar la instalación de todos estos paquetes existe pip que por medio de la consola de Ubuntu nos facilita enormemente el trabajo de obtener e instalar cualquier paquete que lleguemos a necesitar.

La forma de instalarlo es la siguiente:

1) Instalamos las dependencias y todo lo necesario ingresando lo siguiente en la terminal


sudo apt-get install python-pip python-dev build-essential 

sudo pip install --upgrade pip 


Una vez realizado esto podemos instalar el paquete que necesitemos ingresando una simple instrucción. Por ejemplo voy a instalar los paquetes numpy, scipy y matplotlib usando las siguientes instrucciones.

sudo pip install numpy 
sudo pip install scipy
sudo pip install matplotlib

pip se diferencia de otras herramientas parecidas como easy_install ya que primero descarga el paquete y luego lo instala por lo evitamos tener problemas si en algún momento la conexión a Internet se corta.

Aunque si algún paquete no funciona con pip todavía se puede usar easy_install de la siguiente manera:

sudo easy_install numpy 
sudo easy_install scipy
sudo easy_install matplotlib

Y si todavia no se tiene exito con la tarea de realizar la instalación se puede hacer con la forma clásica de instalar programas en ubuntu de la siguiente forma

sudo apt-get install python-numpy, python-scipy, python-matplotlib

La elección de la forma de instalación depende del programa a instalar por lo que algunas veces sera mas comoda de una manera que de otra por lo que es necesario probar con cualquiera de las 3 que mejor funcione.

Temas Varios


----------------------------------------------------Temas varios-------------------------------------------------------



Ideas sobre programación orientada a objetos en Python Un breve introducción de como funciona la programación orientada a objetos en python.
Python (x,y) Una distribución de python lista con todo lo necesario para realizar trabajo cientifico con python.
CodeBlocks y OpenCV 2.3 (C y C++)  Como configurar Codeblocks y poder probar codigos OpenCV de C o C++
¿Como funciona el sensor de una camara digital? Descripción de como funciona el sensor de una camara digital
Instalación de paquetes para Python Breve reseña de como llevar a cabo la instalación de librerías adicionales para Python
El color en imagenes digitales Explicación de como sucede el fenómeno del color, y sus usos en las imágenes digitales
Serie trigonométrica de Fourier con Python Como se puede realizar la serie trigonométrica de Fourier usando Numpy, Sympy, y Matplotlib



Programación orientada a objetos

Como muchos saben Python es un lenguaje multiparadigma; Esto quiere decir que podemos realizar con el mismo lenguaje programación estructurada, programación orientada a objetos y programación funcional.
Hasta el momento en el blog solo he usado programación estructurada ya que es mas fácil para realizar programas sencillos como los que he hecho hasta el momento pero me parece oportuno comenzar a practicar la programación orientada a objetos ó OOP por sus siglas en ingles.

Que mejor para desempolvar mis conocimientos sobre el tema que leyendo esta conversación que encontré en un blog muy interesante.


En la programación orientada a objetos, todo se reduce, como su nombre lo indica, a los objetos y sus relaciones entre ellos.
Los objetos tal como en el mundo real pueden ser, por ejemplo, una lampara, un carro, un perro. En la conversación hacen el ejemplo con un radio.

Un radio tiene, entre otras cosas, algo que permite seleccionar la emisora o frecuencia y algo para modificar el volumen, estas son las propiedades del objeto las cuales básicamente lo definen. Podríamos incluir otras propiedades como el ancho y el alto del radio o el color, la marca, y muchas mas; Pero para este ejemplo esas son las propiedades que nos interesan.

las propiedades tienen un valor. La frecuencia podría ser 97.9, el volumen = 50, el ancho=20 y así con todas las propiedades.

Cuando nosotros cambiamos la frecuencia de un radio o subimos el volumen, normalmente no estamos interesados en lo que ocurre al interior del objeto. Solo nos interesa saber que si presionamos un botón alguna acción va a ocurrir.

Los botones de subir y bajar volumen, o el de modificar la emisora podemos llamarlos como los métodos del objeto. Los métodos son el algo que realiza una tarea sobre el objeto, como modificar sus propiedades. Por ejemplo, Un método del objeto radio podría ser "subir volumen" y lo que hace es modificar la propiedad "volumen".

Los objetos son definidos por medio de clases, Las clases vienen siendo como una plantilla de objetos. Es decir, que si yo tengo la clase radio con sus atributos y métodos  puedo crear las radios que quiera con las mismas características.

Vamos a crear nuestra clase radio

class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 

Con esas lineas creamos la clase llamada Radio.

__init__  es un método especial. Este se ejecuta cuando se crea un objeto nuevo. Quiere decir que cuando creemos un objeto desde la clase radio, lo primero que se ejecuta va a ser el método __init__ el cual, a su vez, crea unos atributos llamados volumen y frecuencia y les asigna un valor.

Ahora vamos a crear un objeto. Ponemos el siguiente código:

class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 

mi_radio = Radio()
print mi_radio.volumen

Lo que hicimos fue crear el objeto mi_radio e imprimir el atributo volumen de ese objeto. Como en la clase radio le asigne un valor por defecto a volumen, muy seguramente la salida sera:

>> 10

También podría modificar las propiedades de mi objeto. Por ejemplo:


class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 
mi_radio = Radio()
mi_radio.volumen=45
mi_radio.frecuencia=97.9
print mi_radio.volumen
print mi_radio.frecuencia

y mi salida sera:

>> 45
>> 97.9

En esta caso modificamos las propiedades del objeto pero no tiene sentido modificar el código para modificar un atributo de un objeto. Lo correcto seria crear nuestro primer método que haces las veces de "botón" de nuestro radio. Así que vamos a crear un método que suba el volumen el cual quedaría de la siguiente manera:

class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 
        def subir_volumen(self):
                self.volumen +=1
mi_radio = Radio()
print mi_radio.volumen
mi_radio.subir_volumen()
print mi_radio.volumen

Esto nos daría como salida:

>> 10
>> 11

Lo cual es justo lo que queríamos  En vez de modificar directamente las propiedades, creamos un método que lo hiciera por nosotros.

Si quisiera elegir el volumen y la frecuencia (atributos) al momento de crear mi objeto, mi código seria el siguiente.

class Radio:
        def __init__(self,vol,frec):
                self.volumen = vol
                self.frecuencia = frec
        def subir_volumen(self):
                self.volumen +=1
mi_radio = Radio(20,94.9)
print mi_radio.volumen
print mi_radio.frecuencia

Mi salida seria:

>> 20
>> 94.9

Es bastante obvio que en el método __init__ le digo que al momento de crear un objeto Radio le tengo que pasar 2 argumentos tal como hice. Pero si al momento de crear el objeto no le pongo atributos daría un error, por lo que es conveniente ponerle unos valores por defecto a los atributos de la siguiente manera:

class Radio:
        def __init__(self,vol=10,frec=97.9):
                self.volumen = vol
                self.frecuencia = frec
        def subir_volumen(self):
                self.volumen +=1
mi_radio = Radio()
print mi_radio.volumen
print mi_radio.frecuencia

como no asigne nada a mi_radio = Radio() la salida sera

>> 10
>> 97.9

porque las asigne por defecto en la clase. También podría solo haber pasado un argumento así:

mi_radio = Radio(50)

y mi salida seria

>> 50
>> 97.9

Si quisiera solo asignar la frecuencia y dejar el volumen por defecto tendría que especificar el nombre del atributo

mi_radio = Radio(frec=104.9)

y la salida seria:

>> 10
>> 104.9

Ahora como ejercicio voy a modificar la clase Radio de tal manera que el método subir_volumen me permita escoger cuanto quiero subir y si no le digo cuanto, me suba 1.

class Radio:
        def __init__(self,vol=10,frec=97.9):
              self.volumen = vol
              self.frecuencia = frec
        def subir_volumen(self,aumento=1):
                self.volumen +=aumento
mi_radio = Radio()
mi_radio.subir_volumen(10)
print mi_radio.volumen

salida:
>> 20


Ahora vamos a observar otro método especial llamado __str__.
Si hacemos la prueba de imprimir un objeto por medio del siguiente código:

class Radio:
        def __init__(self,vol=10,frec=97.9):
              self.volumen = vol
              self.frecuencia = frec
        def subir_volumen(self,aumento=1):
                self.volumen +=aumento
mi_radio = Radio()
print mi_radio

nos va a salir esto:

>> <__main__.Radio instance at 0x011F4940>

Esto nos dice que "mi_radio" es una instanciación de la clase Radio.
Mediante el método __str__ podemos indicar que queremos mostrar en el momento que se le haga un print al objeto mi_radio o a cualquier instancia de la clase Radio.

Voy a poner el mismo ejemplo de la conversación:

class Radio:
        def __init__(self,vol=10,frec=97.9):
                self.volumen = vol
                self.frecuencia = frec
        def subir_volumen(self,aumento=1):
                self.volumen +=aumento
        def __str__(self):
                return "Volumen: " + str(self.volumen) + "\nFrecuencia: " + str(self.frecuencia)
mi_radio = Radio()
print mi_radio

Mi salida sera

>>Volumen: 10
>>Frecuencia: 97.9

____________________________________________________________

Vamos a practicar un poco. Voy a realizar una clase que me permita crear un punto con sus correspondientes coordenadas X y Y, si no le paso argumentos al momento de crear el objeto que me de un punto en (0,0). Ademas crear un método __str__ que me muestre las coordenadas del punto.

Codigo:

class punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'
    
a=punto(4,3)
print a

Ahora voy le voy a agregar un método que me sume otro punto a mi punto existente.

Codigo:

class punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'

    
    def sumarpuntos(self,b): 
        return punto(self.x+b.x,self.y+b.y)
        
    
a=punto(4,3)
b=punto(5,7)
c=a.sumarpuntos(b)

print c

Podría realizarlo de otra manera usando un método especial llamado __add__

Codigo:

class Punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'

    
    def __add__(self,b): 
        return Punto(self.x+b.x,self.y+b.y)
        
a=Punto(4,3)
b=Punto(5,7)
c=a+b
print c

Podría extender el código con una resta usando el método especial __sub__ de la misma manera que uso el __add__

El siguiente y ultimo ejemplo es crear un método que calcule la distancia entre dos puntos

Codigo:

import math
class Punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'

    
    def __add__(self,b): 
        return Punto(self.x+b.x,self.y+b.y)
    
    def distancia(self,b):
        c=(b.x-self.x,b.y-self.y)
        c=(c[0]**2)+(c[1]**2)
        c=math.sqrt(c)
        return c
    
a=Punto(4,8)
b=Punto(5,7)
c=a.distancia(b)


Numpy y OpenCV

Como lo explique en la anterior entrada, desde la versión 2.2 de OpenCV es requisito el uso de la librería numpy. Esta librería provee una sintaxis parecida a Matlab y permite realizar operaciones con vectores y matrices de una forma optimizada y cómoda. Numpy posee un objeto llamado "array" que es muy similar a las listas de Python, sin embargo, una lista puede contener cualquier tipo de datos en la misma lista. Por ejemplo

a=[ 1, 'Perro', 3.5]

Como vemos esta lista contiene tres datos de diferente tipo. Un entero, un cadena y un decimal.

El objeto array de numpy se parece en su forma a las listas pero solo permite tener un tipo de dato en la misma lista, por lo que pueden ser llamadas "listas homogéneas". Esto permite a python hacer cálculos a gran velocidad sobre los "arrays". Por otro lado numpy tiene funciones para manejo de álgebra linear, transformadas de Fourier y generación de números aleatorios.

La mejor forma para demostrar el funcionamiento de numpy es con ejemplos

- Creación de un vector






importo el modulo numpy y le digo que quiero llamarlo en mi programa como np osea que cuando llame una función de numpy debería comenzar con el prefijo np. de la misma manera como lo vengo haciendo con opencv que es el prefijo cv. También podría importarlo como "import numpy" pero me tocaría iniciar cada función escribiendo numpy. 
Se puede apreciar que cree un array con la funcion array y le asigne 5 valores. Después imprimo el vector.

- Creación de una matriz








Es básicamente lo mismo. Solo ahí que poner atención en los corchetes para definir una matriz.

- Seleccionar un elemento de la matriz

el índice es muy sencillo. Primero se escribe la fila y después la columna. En numpy las índices comienzan desde cero.

Numpy tiene muchísimas funciones para el manejo de matrices las cuales son explicadas en detalle aquí.

Cuando leí que OpenCV desde la versión 2.2 trataba las imágenes como matrices numpy, pensé de inmediato que podría manejar las imágenes mas fácilmente, y no estaba equivocado. ( recordar que una imagen digital es una matriz de 3 dimensiones. En una imagen de color un plano es para el color rojo, otro para el verde y el otro para el azul. En una imagen en escala de grises solo se tiene un plano)

Voy a mostrar un par de pruebas que hice para comenzar a trabajar con numpy. Pero las posibilidades que abre esta nueva opción son muchísimas. Sera cuestión de probar.
_____________________________________________________________________













Este código me muestra lo que hay en el pixel [10,10] de la imagen el cual se ve claramente que es blanco y como se esperaba, en ese pixel hay 3 valores uno para cada color que compone el pixel.
_______________________________________________________________________






















En este ejemplo creo 2 matrices. Las dos son de 200x200 pixeles pero una es llenada con unos y otra con ceros mediante las funciones ones y zeros respectivamente. Las muestro y en las dos imprimo el valor en el pixel 10,10. Como se ve a diferencia de la anterior imagen, estas solo tienen un valor en el pixel, a diferencia del otro que tenia 3 valores. Esto es debido a que las matrices que creen son unidimensionales y no imágenes RGB.

_______________________________________________________________________


Estos ejemplos sencillos permiten ver la versatilidad que le agrega la inclusión de numpy a opencv. Ahora solo queda seguir probando y encontrando muchas mas opciones.

OpenCV






CodeBlocks y OpenCV 2.3 (C y C++) en Ubuntu

Para los que no lo conocen Codeblocks es una IDE open source, gratuito y multiplataforma enfocado para el desarrollo de programas en C++ o C. La verdad es una de los mejores IDE gratuitos que he visto por las funcionalidades que ofrece y su excelente interfaz gráfica.

Aunque codeblocks no tenga nada que ver con python todos sabemos que la mayoría de código existente haciendo uso de OpenCV esta escrito en lenguaje C o C++ por lo que me parece necesario tener una herramienta que nos permita compilar este tipo de código para observar su funcionalidad y poder realizar nuestros programas en python ya que muchas veces al ver un programa en ejecución, su código resulta mas fácil de leer y por lo mismo de traducir a python.

En esta entrada voy a realizar el proceso de instalación de el IDE codeblocks  en Ubuntu  y como se enlaza con OpenCV 2.3.

1) Instalación en Ubuntu 10.10

- Instalar el compilador y el IDE
Lo primero para poder desarrollar cualquier programa en C++ es tener un buen compilador en este caso vamos a hacer uso del compilador g++.

Poner esto en la terminal:

sudo apt-get install g++ codeblocks

de esta manera instalamos tanto el compilador como el IDE codeblocks en ubuntu el cual se encuentra en el menú aplicaciones -> programación.

2) Enlazar OpenCV 2.3 con CodeBlocks

El paso que voy a realizar aquí es lo que realmente menos me gusta de usar opencv con c++ y es que con cada ide que valla a trabajar el procedimiento de enlace es diferente. A diferencia de python en el que si se instala opencv correctamente puedes trabajarlo desde el entorno de desarrollo de tu preferencia.

Voy a colocar un video sobre como es el procedimiento: (Para ver los videos es mejor darle doble clic y verlos en youtube modo pantalla completa)



Como pueden ver creamos un proyecto nuevo y ponemos unas lineas de configuración.

En Linker settings -> Other linker options ponemos:


`pkg-config opencv --libs`
 

Nota: no olvidar las comillas

Y en Compiler settings-> Other options ponemos:


`pkg-config opencv --cflags`
 
 
Nota: no olvidar las comillas

Eso seria todo ahora vamos a poner este código y ver como funciona:

#include 
#include 
using namespace cv;

int main( int argc, char** argv )
{
Mat image;

image = imread("python.png");
namedWindow( "Display Image", CV_WINDOW_AUTOSIZE );
imshow( "Display Image", image );
waitKey(0);
}

Nota: No incluir la linea 14 del codigo

Resultado:



Fuente: http://opencv.willowgarage.com/wiki/CodeBlocks

Instalando OpenCV 2.3 en Windows 7

04/Sep/11

Desde que comencé a usar Linux ya casi no hacia nada en Windows hasta ahora que por cuestiones de mi trabajo de grado ha sido necesario. Resulta que tengo que hacer uso del kinect con el SDK oficial de Windows y este tiene restricciones bastante estrictas acerca de las herramientas con las que se debe contar (Windows 7 sin virtualizar, Visual Studio 2010, entre otras), ademas de todo esto también tengo que usar C# por lo que mi estudio de Python ha tenido que esperar un poco. A pesar de esto no tengo pensado dejar de usar Python y OpenCV por lo que me he visto en la necesidad de instalarlos en mi computador bajo windows y la verdad ha sido un martirio por la falta de información que existe.

############################################
(Actualización 8/oct/11)  La mejor forma de instalar OpenCV 2.3.1 bajo windows 7 esta explicada en la entrada de Python(x,y). Aún así voy a conservar esta entrada por si alguno quiere intentarlo de esta manera.
############################################

Requisitos: Para poder usar Python y OpenCV es necesario tener Python 2.6 o Python 2.7 y Numpy (La versión de numpy depende de la versión de python que tengamos instalada).

1) Descargamos las librerías OpenCV 2.3.1

Aquí esta el link de descarga

2) Descomprimimos el archivo descargado

El archivo que descargamos es .exe por lo tenemos que hacerle doble clic y escoger la ubicación donde queremos descomprimirlo. Yo descomprimí en el disco C


Archivo descomprimido en la carpeta "C://opencv"

3) Copiando archivos

Entramos a la carpeta C:\opencv/build/python  y nos vamos a encontrar dos carpetas una llamada "2.6" y la otra "2.7", estas hacen referencia a la versión de python que tenemos. Como python (x,y) viene con python 2.6 entonces entro a la carpeta 2.6.

Dentro de esta hay dos archivos uno llamado "cv.py" y el otro llamado "cv2.pyd". Copiamos ambos archivos

Archivos que deben ser copiados

4) Ponerlos en la ruta correcta

Vamos a la carpeta "C:\Python26\Lib\site-packages" o la ruta equivalente para ustedes y en ese lugar pegan los dos archivos.

Y pueden probar las librerias en IDE favorito de python

Podemos ver que muestra la imagen e imprime los valores RGB de la imagen en el pixel [10,10]

Y al parecer eso es todo. He probado varios códigos y ninguno ha fallado pero la sencillez del método me hace pensar que tiene algo mal por lo que si encuentro algún problema, o alguna otra forma de instalación estaré pendiente de publicarla.