11 de enero de 2011

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:

6 comentarios:

  1. Muy bueno, excelente para aprender python con opencv

    ResponderEliminar
  2. Muchas gracias por poner todo esto de OpenCV y Python, me ayudó bastante este semestre para visión artificial.
    Me escribiste en mi blog hace mucho rato, perdón por no responder, no ví el comentario ^^U
    Binding: un enlace a una librería de forma dinámica, los reconoces porque casi siempre se "enlazan" usando lo .h o así.
    Warpper: una envoltura, lo que hace es llamar aplicaciones(o métodos de estas) si necesidad de estar realmente enlazadas (como era el caso de MplayerCtrl).

    Saludes :)

    ResponderEliminar
  3. Me encanta tu blog!!!!

    Hace unas semanas que he empezado con python y OpenCV y me parece apasionante. Animo! Gracias a tu blog muchos vemos luz en el camino.

    ResponderEliminar
  4. Excelente artículo, felicidades... comparto aquí otro link que tiene un ejemplo de clasificación de un objeto a partir de una imagen en matlab usando segmentación por color. Espero sea útil.

    http://www.tecnohobby.net/ppal/index.php?option=com_content&view=article&id=29:uso-de-procesamiento-de-imagenes-en-rp&catid=41:pitopicosgenerales&Itemid=21

    Saludos

    ResponderEliminar
  5. Hola, alguien me podria decir porque cuando pego el codigo en opencv, no me sirve? ya le cambie la ubicacion de la imagen.

    ResponderEliminar