jueves, 28 de febrero de 2013

Laboratorio 4

Laboratorio 4 - Detección de diagonales

Hola, esta entrada corresponde a la cuarta actividad del laboratorio de visión computacional. La actividad consiste en localizar y pintar las diagonales de los objetos de una imagen.
En esta misma entrada primero hablaré sobre la detección de lineas horizontales y verticales (esto fue la tarea 4 de la misma materia) continuar con el seguimiento y explicar de donde viene la detección de lineas en diagonal.

Detección de líneas 

Para la detección de lineas usamos la transformada de Hough, lo que realizamos fue hacer el cálculo de los gradientes vertical y horizontal de la imagen usando máscaras de convolución (como lo hicimos en la actividad de detección de bordes) seguido de obtener los gradiente antes mencionados,  continuamos calculando los valores de theta de los pixeles de la imagen.

Cuando tenemos los gradientes, calculamos el ángulo de cada pixel, yo usé una imagen binarizada sin filtro, porque cuando la pasaba por mi filtro tendía a poner demasiadas lineas verticales y horizontales.
Bueno, obtuvimos los ángulo de los pixeles con la siguiente fórmula:


Las variables de esta fórmula son arctan (arcotangente), gy (gradiente y), gx (gradiente x).
Después de calcular los ángulos, se calcula la variable rho usando al fórmula siguiente:


El valor de rho es guardado en una lista con el valor del ángulo theta.

Seguido de esto obtuvimos las frecuencias de cada "theta, rho" usando un diccionario, esto con el fin de determinar qué pixeles son lineas, tomando en cuenta que cuando un par (theta, rho) tiene más repeticiones, quiere decir que hay más probabilidad de que el par sea una línea (mismo ángulo.)

Para terminar pintamos pixeles pertenecientes a las líneas (azules para horizontales, rojo para verticales y verde para las líneas diagonales)

Para las líneas diagonales solo agregué una condición que cuando el ángulo no fuera ni 0 ni 90 ni 180 ni 270 lo tomara como una diagonal (y lo pinté de verde).
A continuación los highlights del código:
'''
Convolucion y deteccion de lineas
'''

def convolucion(im,g):
 w, h = im.size
 pixeles = im.load()
 imConvolucion = Image.new('RGB', (w,h))
 pix = imConvolucion.load()
 
     for x in range(w):
         for y in range(h):
          suma = 0
              for i in range(x-1, x+2):
                  for j in range(y-1, y+2):
                       try:
                           suma += g[i - (x-1)][j - (y-1)] * pixeles[i,j][1]
                       except:
                           pass
              pix[x,y] = (suma,suma,suma)
     return imConvolucion
 
def frecuentes(histo, cantidad):
 frec = list()
 for valor in histo:
  if valor is None:
       continue
  
  frecuencia = histo[valor]
  acepta = False
  if len(frec) <= cantidad:
   acepta = True
  if not acepta:
   for (v, f) in frec:
    if frecuencia > f:
               acepta = True
               break
  if acepta:
   frec.append((valor, frecuencia))
   frec = sorted(frec, key = lambda tupla: tupla[1])
   if len(frec) > cantidad:
           frec.pop(0)
     incluidos = list()
     for (valor, frecuencia) in frec:
         incluidos.append(valor)
 
     return incluidos
 
def deteccionDeLineas():
    umbral = 10
    im = Image.open("meh3.jpg")

    sobelx = [[-1, -1, -1], [2, 2, 2], [-1, -1, -1]]
    sobely = [[-1, 2, -1], [-1, 2, -1], [-1, 2, -1]]
    imx = convolucion(im, sobelx)
    imy = convolucion(im, sobely)
    gx = imx.load()
    gy = imy.load()
    w,h = im.size
    comb = {}
    pixeles = im.load()
    resultado = list()
 
    for x in range(w):
        datos = list()
        for y in range(h):
            hor = gx[x,y][0]
            ver = gy[x,y][0]
 
            if fabs(hor) + fabs(ver) <= 0.0:#nada en ninguna direccion                                                                        
                theta = None
            elif hor == 0 and ver == 255:
                theta = 90
            elif fabs(hor) > 0.0:
                theta = atan(fabs(ver/hor))
            if theta is not None:
                rho = fabs( x * cos(theta) + y * sin(theta))
 
                if x > 0 and x < w-1 and y > 0 and y < h-1:
                    if (rho, theta) in comb:
       comb[ (rho, theta) ] += 1
                    else:
                        comb[ (rho, theta) ] = 1
                datos.append( (rho, theta) )
            else:
                datos.append((None,None))
        resultado.append(datos)
 
    incluir = int(ceil (len(comb) * umbral))
 
    frec = frecuentes(comb, incluir)
 
    for x in range(w):
     for y in range(h):
            if x > 0 and x< w-1 and y > 0 and y < h-1:
    rho, theta = resultado[x][y]
 
   if (rho, theta) in frec:
                    if theta == 0:
                        pixeles[x,y] = (255,0,0)
                    elif theta == 90:
                        pixeles [x,y] = (0,0,255)
              elif theta != 0 and theta != 90 and theta != 180 and theta != 270: #Para diagonales
                    pixeles [x, y] = (0,255,0) #Los pintamos verde
                          print "Soy un pixel diagonal"
    im.save('meh5.jpg')



Ahora algunos resultados con imágenes sencillas (hacer click sobre ellas para ampliarlas):






(Para diagonales)



(Una sección se pinto verde porque traia letras XD)




Mi liga de git: https://github.com/eddypre/VisionTriana

Cualquier duda o aclaración pueden dejarla en comentarios. 

Saludos a todos!


miércoles, 20 de febrero de 2013

Laboratorio 3

Laboratorio 3 - Convex hull

Hola, en esta actividad de laboratorio de Visión computacional realizaremos la actuvidad de convex hull,  que no es más que una región convexa que abarca una figura entera (sobre convexa).

Esta actividad la realizaré a partir del resultado de las actividades anteriores, la base sobre la que se trabajará es una imagen binarizada con bordes identificados, donde los bordes están pintados en color blanco y el resto de la imagen en color negro.

Existen diversos métodos para obtener la cerradura convexa, pero el que usé yo (porque casi todos los compañeros usamos ese) es el de Jarvis, este algoritmo parte de un punto vértice, luego lo que hace es envolver todos los puntos contenidos. 

En este sitio se explica muy bien el funcionamiento, además vienen animaciones muy buenas para entenderlo: http://www.cs.princeton.edu/courses/archive/spr09/cos226/demo/ah/JarvisMarch.html 

Mi código:





Este es el resultado, marqué en rojo los puntos determinados por jarvis y las líneas verdes es el convex hult, pueden observar que hubo muchos errores ya que existen puntos donde no deberían, de esa manera al pintar las lineas tomaba muchos puntos de referencia, intentaré solucionarlo después.


Cualquier duda o aclaración pueden ponerla en comentarios.
Saludos a todos.

martes, 19 de febrero de 2013

Tarea 2

Tarea 2 - Detección de Figuras

Hola, la siguiente actividad de la clase de visión computacional consiste en implementar una subrutina del algoritmo BFS, para después detectar las distintas figuras presentes en una imagen, así como los centros de masa.
El requisito para realizar esta actividad es tener una imagen con los bordes detectados, y también tiene que estar binarizada (es lo correspondiente a la tarea 1, antes entregada).

Mediante el algoritmo BFS encontramos las distintas figuras que componen la imagen, esto recorriendo los pixeles totales de la imagen, el algoritmo parte de un nodo raiz y luego se van recorriendo los nodos vecinos, introduciendo en una cola.

Como ya tenemos la imagen con bordes y binarizada el algoritmo va a estar delimitado por los rangos que corresponden a las lineas blancas (los bordes), al ir recorriendo los pixeles de una imagen lo voy pintando con un color random, dejando siempre el fondo (parte de mayor porcentaje de la imagen) en color gris (requerido en la tarea).
Este mismo proceso es para detectar todos los objetos o figuras de la imagen. Así que en cada pixel negro de la imagen se buscará la figura, siempre respetando los contornos o los bordes de cada figura.

A continuación la subrutina bfs():

#Implelementacion de bfs ***************************************************
#***************************************************************************

def bfs(im, origen, color):
 n = 0 
 pixel = im.load()
 width, height = im.size
 col = []
 xsum = []
 ysum = []
 col.append(origen)
 original = pixel[origen]
 largo = len(col) 
 
 while largo > 0:
        (x, y) = col.pop(0)
        actual = pixel[x, y]
        if actual == original or actual == color:
                for dx in [-1, 0, 1]:
           for dy in [-1, 0, 1]:
       i, j = (x + dx, y + dy)
       if i >= 0 and i < width and j >= 0 and j < height:
          contenido = pixel[i, j]
          if contenido == original:
             pixel[i, j] = color
             xsum.append(i)
             ysum.append(j)
             n += 1
             col.append((i, j))
 #im.save('bla01.jpg')
 return n, xsum, ysum # el regreso para la funcion formas
 
#Para clasificar los objetos**********************************************
#*****************************************************************************
#*************************************************************************


Un video muy bueno que ayuda a programar el BFS

 

Ahora para pintar objetos, centros de masa y porcentajes:
#Para clasificar los objetos**********************************************
#*****************************************************************************
#*************************************************************************

def formas():

 im = Image.open("meh5.jpg") #Imagen con bordes y binarizada
 width, height = im.size
 total = width * height
 porcentajes = []
 centro = []
 corr = 0 ##Solo para corroborar porcentajes
 conteo = 0
 pixel = im.load()
 temp = []
 colorsR = [] #Para guardar colores R
 colorsG = [] #Para guardar colores G
 colorsB = [] #Para guardar colores B
 conteoColores = 0
 for i in range(width):   #Recorriendo imagen
      for j in range(height):  #Recorriendo imagen
         if pixel[i, j] == (0, 0, 0): #Ir pintando pixeles (negros obviamente)
    r, g, b = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)  #Asignando pixeles random
    
    n, x, y = bfs(im,(i,j),(r,g,b))    
    ptemp = float(n)/float(total) * 100.0 #Obteniendo porcentajes
    if ptemp > 0.1:
       centro.append((sum(x) / len(x), sum(y) / len(y)))   #Localizando centros
       porcentajes.append([ptemp, (r, g, b)])
       conteo += 1
           else:
              temp.append((x, y))
 fondo = porcentajes.index(max(porcentajes))
 color = porcentajes[fondo][1]
 for i in range(width):
      for j in range(height):
         if pixel[i, j] == color:
           pixel[i, j] = (150, 150, 150)
 
   for i in range(len(centro)):
      if i == fondo:
          pixel[centro[i]] = (255, 0, 0) #Para poner centros de masa
   #Aqui va lo de las tags, proximamente en Tkinter o Pygame
      else:
         pixel[centro[i]] = (0, 0, 0) 

 im.save('bla02.jpg') #Imagen definitiva
   conteo = 0
   for ptemp in porcentajes:
      print "Porcentaje de figura %s: %.2f"%(conteo, ptemp[0])
      conteo = conteo+ 1
  corr = ptemp[0] + corr 
 #print "Corroborando porcentaje: ", corr

# FIN ********************************************************************


Resultados obtenidos:

Para realizar las pruebas tuve que poner imágenes en las que su contorno estuviera bien definido, ya que con algunas pruebas simplemente no era posible detectar las figuras que componian la imagen.

1) Prueba 1

(Hacer click sobre la imagen para ampliar)



Procesos  de conversión en GIF:

  

2) Prueba 2

(Hacer click sobre la imagen para ampliar



Procesos de conversión en GIF:


3) Prueba 3

(Hacer click sobre la imagen para ampliar




Procesos de conversión en GIF:


Para obtener GIF's, por si les interesa: http://andalinux.wordpress.com/2008/09/24/crear-gif-animados-en-ubuntu-linux/

Mi liga de git: https://github.com/eddypre/VisionTriana

Cualquier duda o aclaración pueden ponerla en comentarios. 

Saludos a todos!

miércoles, 13 de febrero de 2013

Laboratorio 2

Laboratorio 2 - Ruido sal y pimienta, normalización y binarización

Hola, en esta entrada voy a presentar el trabajo realizado para el laboratorio 2 de Visión computacional.

Lo primero (obligatorio) que hice fue generar ruido de sal y pimienta en imágenes a colores, la manera en la que puse los puntos blancos y negros sobre la imagen fue muy sencilla. Cuando ejecuto el python, paso como parámetro 2 valores, el primero es la intensidad (ruido a generar en la imagen), y luego la polarización (que tan blancos o negros van a ser las sales y pimientas). El valor de la intensidad lo pongo dentro de un random para que la probabilidad en que salgan puntos (ya sea blancos o negros) dependan del valor dado.

Luego el valor de la polarización lo determino mediante condicionales, para ver que tan negros o blancos pongo los ruidos, tomando en cuenta que el valor 0 corresponde a negro y el valor 255 corresponde a blanco, con el valor leido de polarización solo vario entre esos 2 números.

Bueno, aquí muestro la subrutina con la que realicé el ruido:

def salPimienta(): # Subrutina para poner sal y pimienta a la imagen binarizada
 #tiempoInicial = time.time()
 i = 0
 x = 0
 y = 0

 #Valores para instensidad y polarizacion respectivamente
 intensidad = int(argv[1]) # Nivel de pixeles (cantidades)
 polarizacion = int(argv[2]) # Que tan negros y blancos son los puntos en porcentaje... donde 0 es negro y 255 es blanco

 im = Image.open("bla.jpg")
  pix = im.load()
 for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   #pix = im.load()
   tupla = pix[x, y]
   azar = int(random.randint(0,intensidad))

   if azar == 9: #Aqui podria ser perfectamente cualquier numero (no necesariamente 9), seria la misma probabilidad: 1/intensidad
    if polarizacion > 25 and polarizacion < 75: 
     a = 128 + 64
     b = 128 + 64  # Para puntos "blancos"
     c = 128 + 64

     # Fabricando la nueva tupla
     tupla1 = (a, b, c)
     
     # Poniendo pixel
     im.putpixel((x, y), tupla1)
    
    elif polarizacion >= 75:
     a = 255
     b = 255 # Para puntos "blancos"
     c = 255
     
     # Fabricando la nueva tupla
     tupla1 = (a, b, c)

     #Poniendo pixel
     im.putpixel((x, y), tupla1)

    elif polarizacion <= 25:
     a = 128 + 20
     b = 128 + 20 # Para puntos "blancos"
     c = 128 + 20
     
     # Fabricando la nueva tupla 
     tupla1 = (a, b, c)

     #Poniendo pixel
     im.putpixel((x, y), tupla1)
  
   elif azar == 7:
    if polarizacion > 25 and polarizacion < 75: 
     d = 128 - 64
     e = 128 - 64 # Para puntos "negros"
     f = 128 - 64
     tupla2 = (d, e, f)
     im.putpixel((x, y), tupla2)

    elif polarizacion >= 75:
     d = 0
     e = 0 # Para puntos "negros"
     f = 0
     tupla2 = (d, e, f)
     im.putpixel((x, y), tupla2)
    
    elif polarizacion <= 25:
     d = 128 - 20
     e = 128 - 20 # Para puntos "negros"
     f = 128 - 20
     tupla2 = (d, e, f)
     im.putpixel((x, y), tupla2)
     

   else:
    a = tupla[0]
    b = tupla[1]
    c = tupla[2] 
    
    newTupla = (a,b,c)
    im.putpixel((x, y), newTupla)   

   #print tupla, "--", a,",",b,",",c
 im.save('meh6.jpg')
 #tiempoFinal = time.time()
 #transcurso = tiempoFinal - tiempoInicial
 #print "Tiempo de escala de grises = ", transcurso 


Ahora les muestro imágenes con distintos niveles de intensidad y polarización:



(Intensidad 60%, polarización 50%)



(Intensidad 90%, polarización 100%)



Ahora para quitar ese ruido generado hice otra subrutina, que lo que hace es compara pixel por pixel de la imagen, cuando detecta que el pixel es blanco o negro, genera un promedio con los valores de pixeles de los vecinos, seguido de eso el promedio obtenido se lo asigna al pixel en el que está posicionado.

A continuación les muestro la subrutina que realiza este proceso:

def quitarSalPimienta():
 
 i = 0
 x = 0
 y = 0
 im = Image.open("meh6.jpg")
 pix = im.load() 
 width, height = im.size
 promedio = 0
 width = width-1
 height = height-1

 #x = j y y = i
 for x in range(height):
  for y in range(width):
   
   tupla = pix[y, x]
   pR = tupla[0]
   pG = tupla[1]
   pB = tupla[2]
  
   print tupla   
   
   #if (pB == 0 or pR == 255): 
   if (pR >= 240 or pG >= 240 or pB >= 240) or (pR <=10 or pG <=10 or pB <=10):
    #esquina superior izquierda
    if y == 0 and x == 0:
     #promedio = (sum(pix[y + 1,x])/3 + sum(pix[y,x + 1])/3 + sum(pix[y,x])/3)/3
     promedio1 = (pix[y + 1,x][0] + pix[y,x + 1][0])/2
     promedio2 = (pix[y + 1,x][1] + pix[y,x + 1][1])/2
     promedio3 = (pix[y + 1,x][2] + pix[y,x + 1][2])/2
    #esquina superior derecha 
    if y == width and x == 0:
     #promedio = (sum(pix[y,x+1])/3 + sum(pix[y-1,x])/3 + sum(pix[y,x])/3)/3
     promedio1 = (pix[y,x+1][0] + pix[y-1,x][0])/2
     promedio2 = (pix[y,x+1][1] + pix[y-1,x][1])/2 
     promedio3 = (pix[y,x+1][2] + pix[y-1,x][2])/2

    if y == 0 and x == height:
      #promedio = (sum(pix[y,x-1])/3 + sum(pix[y+1,x])/3 + sum(pix[y,x])/3)/3
     promedio1 = (pix[y,x-1][0] + pix[y+1,x][0])/2
     promedio2 = (pix[y,x-1][1] + pix[y+1,x][1])/2
     promedio3 = (pix[y,x-1][2] + pix[y+1,x][2])/2

    if y == height and x == width:
      #promedio = (sum(pix[y - 1,x])/3 + sum(pix[y,x - 1])/3 + sum(pix[y,x])/3)/3
     promedio1 = (pix[y - 1,x][0] + pix[y,x - 1][0])/2
     promedio2 = (pix[y - 1,x][1] + pix[y,x - 1][1])/2
     promedio3 = (pix[y - 1,x][2] + pix[y,x - 1][2])/2


    if y > 0 and y < width and x == 0:
     #promedio = (sum(pix[y+1,x])/3 + sum(pix[y-1,x])/3 +sum(pix[y,x+1])/3+ sum(pix[y,x])/3)/4
     promedio1 = (pix[y+1,x][0] + pix[y-1,x][0] + pix[y,x+1][0])/3
     promedio2 = (pix[y+1,x][1] + pix[y-1,x][1] + pix[y,x+1][1])/3
     promedio3 = (pix[y+1,x][2] + pix[y-1,x][2] + pix[y,x+1][2])/3
  
    if y > 0 and y < width and x == height:
     #promedio = (sum(pix[y -1,x])/3 + sum(pix[y,x-1])/3 +sum(pix[y+1,x])/3+ sum(pix[y,x])/3)/4
     promedio1 = (pix[y -1,x][0] + pix[y,x-1][0] + pix[y+1,x][0])/3
     promedio2 = (pix[y -1,x][1] + pix[y,x-1][1] + pix[y+1,x][1])/3
     promedio3 = (pix[y -1,x][2] + pix[y,x-1][2] + pix[y+1,x][2])/3

    if x >0 and x 0 and x < height:
     #promedio = (sum(pix[y - 1,x])/3 + sum(pix[y,x-1])/3 + sum(pix[y,x +1])/3+ sum(pix[y,x])/3)/4
     promedio1 = (pix[y - 1,x][0] + pix[y,x-1][0] + pix[y,x +1][0])/3
     promedio2 = (pix[y - 1,x][1] + pix[y,x-1][1] + pix[y,x +1][1])/3
     promedio3 = (pix[y - 1,x][2] + pix[y,x-1][2] + pix[y,x +1][2])/3

    if y > 0 and y< width and x>0 and x< height:
     #promedio = (sum(pix[y,x])/3 + sum(pix[y + 1,x])/3 + sum(pix[y - 1,x])/3 + sum(pix[y,x + 1])/3 + sum(pix[y,x -1])/3)/5 
     promedio1 = (pix[y + 1,x][0] + pix[y - 1,x][0] + pix[y,x + 1][0] + pix[y,x -1][0])/4
     promedio2 = (pix[y + 1,x][1] + pix[y - 1,x][1] + pix[y,x + 1][1] + pix[y,x -1][1])/4
     promedio3 = (pix[y + 1,x][2] + pix[y - 1,x][2] + pix[y,x + 1][2] + pix[y,x -1][2])/4
   #-----------------------------
   #tupla = pix[x, y]
   
    a = promedio1
    b = promedio2
    c = promedio3 
    miTupla = (a,b,c) #Eso no es una tupla, pero asi le digo yo :P
    im.putpixel((y, x), miTupla) #Esto si es una tupla
    #pix[y, x] = (a,b,c)
   
   else: 
    tuplaOriginal = (pR, pG, pB) #Esto tampoco es una tupla, pero asi le digo yo :P
    im.putpixel((y, x), tuplaOriginal) #Esto si es una tupla XD
   '''
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla) 
   '''  

   #print tupla, "--", a,",",b,",",c
 im.save('meh7.jpg')


Ahora les muestro imágenes con ruido generado comparadas con las imágenes después de pasar por el fitro que limpia el ruido:


  
   
Normalización y binarización

Bien, para los puntos extra, hice una imagen difusa, luego realicé la diferencia entre la imagen original y la difusa y al final realicé la normalizacion y binarización.

Los métodos para realizar estos filtros los describí en el post anterior (Tarea 1), pero aquí explico superficialmente que la difusa la realicé con los promedios de los vecinos que se van visitando en cada pixel, la diferencia es tan solo una resta entre los valores de los pixeles que componen la imagen original y la imagen difusa. La normalización y binarización se realizan tan solo poniendo umbrales y comparando que tan oscuros o claros son los valores de los pixeles, para luego ponerlos en una categoría de blanco o negro (0 o 255).

A continuación les muestro la subrutina del proceso de difusa:


def filtrada(): #Filtrada por el metodo de los vecinos
 #tiempoInicial = time.time()
 i = 0
 x = 0
 y = 0
 im = Image.open("meh.jpg")
 width, height = im.size
 pix = im.load() 
 promedio = 0
 width = width-1
 height = height-1

 #x = j y y = i
 for x in range(height):
  for y in range(width):
   #esquina superior izquierda
   if y == 0 and x == 0:
    promedio = (sum(pix[y + 1,x])/3 + sum(pix[y,x + 1])/3 + sum(pix[y,x])/3)/3
   #esquina superior derecha
   if y == width and x == 0:
    promedio = (sum(pix[y,x+1])/3 + sum(pix[y-1,x])/3 + sum(pix[y,x])/3)/3

   if y == 0 and x == height:
     promedio = (sum(pix[y,x-1])/3 + sum(pix[y+1,x])/3 + sum(pix[y,x])/3)/3

   if y == height and x == width:
     promedio = (sum(pix[y - 1,x])/3 + sum(pix[y,x - 1])/3 + sum(pix[y,x])/3)/3

   if y > 0 and y < width and x == 0:
    promedio = (sum(pix[y+1,x])/3 + sum(pix[y-1,x])/3 +sum(pix[y,x+1])/3+ sum(pix[y,x])/3)/4
  
   if y > 0 and y < width and x == height:
    promedio = (sum(pix[y -1,x])/3 + sum(pix[y,x-1])/3 +sum(pix[y+1,x])/3+ sum(pix[y,x])/3)/4

   if x >0 and x 0 and x < height:
    promedio = (sum(pix[y - 1,x])/3 + sum(pix[y,x-1])/3 + sum(pix[y,x +1])/3+ sum(pix[y,x])/3)/4

   if y > 0 and y< width and x>0 and x< height:
    promedio = (sum(pix[y,x])/3 + sum(pix[y + 1,x])/3 + sum(pix[y - 1,x])/3 + sum(pix[y,x + 1])/3 + sum(pix[y,x -1])/3)/5 
 


   #-----------------------------
   #tupla = pix[x, y]
   
   a = promedio
   b = promedio
   c = promedio 
   '''
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   '''
   #prom = int((a+b+c)/3)
   pix[y, x] = (a,b,c)
   #tup = pix[]
   #im.putpixel((y, y), pix)   

   #print tupla, "--", a,",",b,",",c
 im.save('meh2.jpg')
 #tiempoFinal = time.time()
 #transcurso = tiempoFinal - tiempoInicial
 #print "Tiempo transcurrido durante la filtracion = ", transcurso


Luego la diferencia y la binarización:

def diferencia(): #O normalizacion
 #tiempoInicial = time.time()
 imagen1 = Image.open("meh.jpg")
 imagen2 = Image.open("meh2.jpg")
 width, height = imagen1.size
 pix = imagen1.load()
 pix2 = imagen2.load()

 for y in range(width):
  for x in range(height):
   (a,b,c) = pix[y, x]
   (d,e,f) = pix2[y, x]
   promedio = a+b+c/3
   promedio1 = d+e+f/3
   promedio2 = promedio - promedio1

   if promedio2 > 115:
    promedio2 = 255
   else:
    promedio2 = 0
   a = promedio2
   b = promedio2
   c = promedio2  
   pix[y, x] = (a,b,c)
 imagen1.save("meh3.jpg")
 #tiempoFinal = time.time()
 #transcurso = tiempoFinal - tiempoInicial
 #print "Tiempo transcurrido durante la normalizacion = ", transcurrido


Subrutina para binarización:

def umbrales():
 #tiempoInicial = time.time()
 i = 0
 x = 0  
 y = 0
 umbInferior = 77
 umbSuperior = 177
 im = Image.open("meh4.jpg")
 
 for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   pix = im.load()
   tupla = pix[x, y]
 
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   
   prom = int((a+b+c)/3)
   if prom < umbInferior:
    prom = 0
   elif prom >= umbSuperior:
    prom = 255
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla)   

   #print tupla, "--", a,",",b,",",c
 im.save('meh5.jpg')
 #tiempoFinal = time.time()
 #transcurrido = tiempoFinal - tiempoInicial
 #print "Tiempo transcurrido durante la binarizacion = ", transcurrido


Ya por último les muestro las imágenes después de pasar por todos los procesos antes mencionados:
(Original)



(Escala de grises)



(Difusa)

(Diferencia)



(Binarización)



Git sobre todo el trabajo realizado hasta ahora: https://github.com/eddypre/VisionTriana
 
 
Cualquier duda o aclaración pueden dejarla en comentarios.

Saludos a todos!

 

lunes, 11 de febrero de 2013

Tarea 1 (Clase) Visión Computacional

Detección de bordes por medio del operador Sobel

Hola, esta es la tarea 1 de la clase de visión computacional, la actividad consiste en detectar bordes en distintas imágenes, mediante la utilización de filtros, umbrales, máscaras (técnica del operador de Sobel, o cualquier otra técnica).

Una vez detectados los bordes con las técnicas requeridas, realicé la actividad con 3 fotografías distintas, y para cada fotografía calculé el promedio que se llevaba de tiempo en ejecutar cada subrutina usada para obtener los bordes;
las subrutinas las corrí 30 veces para cada imagen (como el requisito de la tarea lo menciona).

Bien, dicho esto voy  a mencionar detalladamente el trabajo que realicé.

Escala de grises

Lo primero que realicé fue generar una imagen a escala de grises a partir de la imagen original. Está técnica la expliqué previamente en el laboratorio 1 de esta misma materia, pero repetiré en que consiste el procedimiento.
Consiste en que se recorre todos y cada uno de los pixeles de la imagen, en cada pixel que se va recorriendo, se va sacando un promedio de los valores RGB de cada uno, para luego ese promedio asignarlo como el nuevo valor de pixel en los 3 valores de RGB, de manera que cada pixel recorrido tendrá los 3 valores iguales.
Por ejemplo supongamos que tenemos un pixel con los componentes RGB (35, 100, 150), el procedimiento sería sumar los 3 valores (35+100+150), luego dividirlos entre 3 (promedio) para luego el resultado obtenido asignárselo al mismo pixel en sus 3 posiciones RGB.

A continuación les muestro la subrutina que hace este procedimiento, además de la imagen original con la que trabajé y la imagen a escala de grises obtenida.

def transformar():
 #tiempoInicial = time.time()
 i = 0
 x = 0
 y = 0
 im = Image.open("bla.jpg")
 
 for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   pix = im.load()
   tupla = pix[x, y]
 
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   
   prom = int((a+b+c)/3)
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla)   

   #print tupla, "--", a,",",b,",",c
 im.save('meh.jpg')
 #tiempoFinal = time.time()
 #transcurso = tiempoFinal - tiempoInicial
 #print "Tiempo de escala de grises = ", transcurso 



Imagen original (660x495)
 


Imagen a escala de grises




Filtro

El siguiente procedimiento que realicé fue el de filtrado de la imagen, consiste en hacer un efecto blur en la imagen procesada anteriormente (escala de grises), para realizar esto, usé la técnica de los vecinos, que consiste en realizar un promedio con los valores de los pixeles de los vecinos que colindan con el pixel sobre el que estamos posicionados (recordemos que ahora cada pixel tiene los valores iguales de RGB, porque ahora está en escala de grises), seguido de esto, se asigna a ese pixel el valor obtenido.
Dicho esto, podemos concluir que el número máximo de vecinos que se pueden tener son 4, y el mínimo número de vecinos son 2 (los pixeles de las esquinas).

A continuación les muestro la subrutina con la que hice este proceso, además de la imagen obtenida después del filtrado:

def filtrada(): #Filtrada por el metodo de los vecinos
 #tiempoInicial = time.time()
 i = 0
 x = 0
 y = 0
 im = Image.open("meh.jpg")
 width, height = im.size
 pix = im.load() 
 promedio = 0
 width = width-1
 height = height-1

 #x = j y y = i
 for x in range(height):
  for y in range(width):
   #esquina superior izquierda
   if y == 0 and x == 0:
    promedio = (sum(pix[y + 1,x])/3 + sum(pix[y,x + 1])/3 + sum(pix[y,x])/3)/3
   #esquina superior derecha
   if y == width and x == 0:
    promedio = (sum(pix[y,x+1])/3 + sum(pix[y-1,x])/3 + sum(pix[y,x])/3)/3

   if y == 0 and x == height:
     promedio = (sum(pix[y,x-1])/3 + sum(pix[y+1,x])/3 + sum(pix[y,x])/3)/3

   if y == height and x == width:
     promedio = (sum(pix[y - 1,x])/3 + sum(pix[y,x - 1])/3 + sum(pix[y,x])/3)/3

   if y > 0 and y < width and x == 0:
    promedio = (sum(pix[y+1,x])/3 + sum(pix[y-1,x])/3 +sum(pix[y,x+1])/3+ sum(pix[y,x])/3)/4
  
   if y > 0 and y < width and x == height:
    promedio = (sum(pix[y -1,x])/3 + sum(pix[y,x-1])/3 +sum(pix[y+1,x])/3+ sum(pix[y,x])/3)/4

   if x >0 and x 0 and x < height:
    promedio = (sum(pix[y - 1,x])/3 + sum(pix[y,x-1])/3 + sum(pix[y,x +1])/3+ sum(pix[y,x])/3)/4

   if y > 0 and y< width and x>0 and x< height:
    promedio = (sum(pix[y,x])/3 + sum(pix[y + 1,x])/3 + sum(pix[y - 1,x])/3 + sum(pix[y,x + 1])/3 + sum(pix[y,x -1])/3)/5 
 


   #-----------------------------
   #tupla = pix[x, y]
   
   a = promedio
   b = promedio
   c = promedio 
   '''
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   '''
   #prom = int((a+b+c)/3)
   pix[y, x] = (a,b,c)
   #tup = pix[]
   #im.putpixel((y, y), pix)   

   #print tupla, "--", a,",",b,",",c
 im.save('meh2.jpg')
 #tiempoFinal = time.time()
 #transcurso = tiempoFinal - tiempoInicial
 #print "Tiempo transcurrido durante la filtracion = ", transcurso



Imagen filtrada



Aplicación de máscara

En el proceso de la aplicación de la máscara, es en el que obtendremos los bordes de la imagen, existen distintos métodos para la obtención de bordes, sin embargo, el que yo usé es el del operador de Sobel, que es una  máscara típica de detección de bordes, perteneciente a las técnicas de gradiente diferencial (DG).

El procedimiento consiste en ir recorriendo toda la imagen pixel por pixel con una máscara o matriz de dimensiones 3x3 (por lo menos es de 3x3 con este método), y la idea es ir obteniendo los productos de la matriz que abarca la máscara en la imagen por los elementos de la misma matriz, de esa manera obtenemos un único valor de gradienteX y uno de gradiente Y, al final se obtiene un gradiente resultante (obviamente se obtiene apartir de los componentes de los gradientesX y gradientesY). El gradiente resultante será el nuevo valor del pixel sobre el que estamos posicionados.


Las siguientes son las matrices para obtener gradiente x (a la izquiera) y gradiente y (a la derecha), donde la A es la imagen original (en mi caso la imagen después de ser pasada por el filtro)


Luego después de obtener los componentes de los gradientes, obtenemos el gradiente resultante mediante la siguiente ecuación:



Si quieren obtener información más detallada de este operador Sobel, pueden visitar la siguiente liga: http://es.wikipedia.org/wiki/Operador_Sobel

Bien, a continuación les dejo la subrutina correspondiente a este procedimiento, además de la imagen obtenida después del proceso (nótese que la imagen tiene una línea blanca en su contorno, esto es el resultado de centrar el campo de trabajo, para que la mascara abarque siempre las dimensiones en la imagen, osea una matriz de 3x3):

def convolucion():
 #tiempoInicial = time.time()
 '''         ---       ---                     ---    ---
             | -1  0  1  |                    | 1  2  1  |
 SOBEL: Sx = | -2  0  2  |               Sy = | 0  0  0  |
             | -1  0  1  |                    |-1 -2 -1  |
             ---       ---                     ---    ---

 S = raiz(Sx2+Sy2)

 ''' 
 
 im = Image.open("meh2.jpg")
 width, height = im.size
 pix = im.load()
 resultado = 0
 gradienteX = ([-1, 0, 1], [-2, 0, 2], [-1, 0, 1])  #Valores establecidos por medio del operador Sobel
 gradienteY = ([1, 2, 1], [0, 0, 0], [-1, -2, -1])  #Para gradiente de y, el de arriba es el gradiente de x.
 sumasX = 0
 sumasY = 0  

 for x in range(height):
  for y in range(width):
   sumasX = 0
   sumasY = 0
   if x != 0 and y != 0 and y != width and x != height: #Para obtener un centrado de la mascara, evita la primera linea de pixeles de la imagen por los 4 lados
    #x = a
    #y = b   
    for a in range(3): #Debido a que la matriz de los gradientes es de 3x3
     for b in range(3):
      try:
       productosGX = gradienteX[a][b]*pix[y+b, x+a][1] #Obteniendo el valor de gradiente X
       productosGY = gradienteY[a][b]*pix[y+b, x+a][1] #Obteniendo el valor de gradiente Y
   
      except:
       productosGX = 0
       productosGY = 0
     
      sumasX = productosGX+sumasX #Adicionando los valores del gradiente X
      sumasY = productosGY+sumasY #Adicionando los valores del gradiente Y
   
    potenciaGradienteX = pow(sumasX, 2) #Obteniendo el cuadrado del gradiente X
    potenciaGradienteY = pow(sumasY, 2) #Obteniendo el cuadrado del gradiente Y
    Gradiente = int(sqrt(potenciaGradienteX+potenciaGradienteY)) #Para obtener el gradiente por medio de los componentes x, y
    #resultado = Gradiente

    if Gradiente > 255: #Por si se pasan los valores
     Gradiente = 255

    if Gradiente < 0: #Para estar dentro del rango (0, 255) 
     Gradiente = 0
  
    pix[y,x] = (Gradiente, Gradiente, Gradiente) # Creando el pixel nuevo con el gradiente obtenido   
 
 im.save('meh4.jpg')     
 #tiempoFinal = time.time() 
 #transcurrido = tiempoFinal - tiempoInicial
 #print "Tiempo transcurrido durante la convolucion = ", transcurrido


Imagen con bordes



Binarización

Para pulir los detalles y además hacer más notables los negros y blancos de la imagen, binaricé la imagen, aplicando umbrales preestablecidos, de esa manera, mi imagen ahora solo tendría pixeles negros y blancos.
El cambio en la imagen si es bastante notable y se aprecia todavía mejor el contorno.

A continuación les dejo la subrutina correspondiente a este procedimiento (no es necesario explicarla ya que el funcionamiento es demasiado sencillo), además de la imagen obtenida:

def umbrales():
 #tiempoInicial = time.time()
 i = 0
 x = 0  
 y = 0
 umbInferior = 77
 umbSuperior = 177
 im = Image.open("meh4.jpg")
 
 for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   pix = im.load()
   tupla = pix[x, y]
 
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   
   prom = int((a+b+c)/3)
   if prom < umbInferior:
    prom = 0
   elif prom >= umbSuperior:
    prom = 255
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla)   

   #print tupla, "--", a,",",b,",",c
 im.save('meh5.jpg')
 #tiempoFinal = time.time()
 #transcurrido = tiempoFinal - tiempoInicial
 #print "Tiempo transcurrido durante la binarizacion = ", transcurrido



Imagen binarizada



Por último ejecuté cada subrutina 30 veces para poder formar un promedio de tiempo de ejecución de cada una. Así es como se ve la impresión de los tiempos de la imagen anteriormente presentada.


Y también hice lo mismo con 2 imágenes más, de distinto tamaño y contenido.

Original (900x904)


Escala de grises


Filtrada

Aplicando máscara


Binarización


Promedio de tiempos para cada subrutina

Noten que tarda más tiempo que la primera imagen, esto debido a que las dimensiones de esta imagen son más grandes


Ahora otra imagen de menores dimensiones que las 2 anteriores:

Imagen original (256x256)

Escala de grises

Filtro

Aplicando máscara

Binarización

 

Promedio de tiempo de ejecución

Nótese que los tiempos de ejecución de cada subrutina son menores que las 2 imágenes anteriores, debido a que las dimensiones de esta imagen son menores que las pasadas.


Bueno, esto es todo para esta actividad. Cualquier duda o aclaración pueden dejarla en los comentarios.

Saludos a todos.


lunes, 4 de febrero de 2013

Laboratorio 1

Laboratorio 1 - Escala de grises y umbrales 

Hola, para esta primer entrada del laboratorio de Visión Computacional se nos pidió realizar un programa que dada una imagen cualquiera, obtener una copia de la imagen a escala de grises, luego por medio de umbrales, asignar pixeles, blanco, negro y gris (el mismo color) a la imagen.
Bien, dicho esto, voy a proceder a explicarles el método que usé para convertir mi imagen a escala de grises (creo que todos usamos el mismo método). Primeramente, obtuve de cada uno de los pixeles los valores de RGB, luego realicé un promedio con esos 3 valores. Por ejemplo:
Pixel1 = (65, 30, 14), el promedio es (65 + 30 + 14) / 3 = 36.33.
Seguido de eso igualé los valores de RGB del pixel al promedio obtenido, es decir:   
NuevoPixel = (36, 36, 36)


Algo así:

for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   pix = im.load()
   tupla = pix[x, y]
 
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   
   prom = int((a+b+c)/3)
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla)   

   print tupla, "--", a,",",b,",",c
 im.save('meh.jpg')


Luego por medio de la función putpixel formo la imagen con los nuevos valores de RGB de cada pixel.


La imagen anterior es la imagen original que uso para realizar esta práctica, cuando ejecuto mi programa primero me sale la imagen original, luego en terminal me pregunta lo que deseo hacer, me dice que presione 1 para realizar la función de escala de grises o que presione 2 para usar la función de umbrales.

Si elegimos la opción 1 se ejecutará la siguiente función (transformar):

def transformar():
 i = 0
 x = 0
 y = 0
 im = Image.open("bla.jpg")
 
 for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   pix = im.load()
   tupla = pix[x, y]
 
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   
   prom = int((a+b+c)/3)
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla)   

   print tupla, "--", a,",",b,",",c
 im.save('meh.jpg')

Si elegimos la opción 2 se ejecutará la siguiente función (umbrales):

#Funcion umbrales
#Se accede a ella presionando 2
def umbrales():
 i = 0
 x = 0  
 y = 0
 umbInferior = 77
 umbSuperior = 177
 im = Image.open("bla.jpg")
 
 for x in xrange(im.size[0]):
  for y in xrange(im.size[1]):
   pix = im.load()
   tupla = pix[x, y]
 
   a = tupla[0]
   b = tupla[1]
   c = tupla[2]
   
   prom = int((a+b+c)/3)
   if prom < umbInferior:
    prom = 0
   elif prom >= umbSuperior:
    prom = 255
   newTupla = (prom,prom,prom)
   im.putpixel((x, y), newTupla)   

   print tupla, "--", a,",",b,",",c
 im.save('meh2.jpg')


Cuando ejecutamos la primera opción el resultado de la imagen es la siguiente:


La imagen a a escala de grises, luego cuando ejecutamos la segunda opción el resultado de la imagen es la siguiente: 

La imagen a blanco y negro con algunos pixeles en gris, los pixeles en gris son porque quedaron en medio de los umbrales superior e inferior que puse. Es decir, si el pixel resultaba menor al umbral inferior (85), lo pintaba en negro, si el pixel resultaba mayor al umbral superior (170) lo pintaba de blanco, y si quedaba entre los valores de los umbrales el pixel permanecia en el valor gris.

Jugando con los umbrales

Bien, después de esto realicé algunas modificaciones a los valores de umbrales y pixeles para visualizar los cambios de las imagenes resultantes, en la siguiente imagen apliqué lo contrario, es decir, si el valor del pixel era menor al umbral inferior, al nuevo pixel le daba el valor de 255, y viceversa, de esa manera esperaba el resultado contrario (cambiar negros por blancos y blancos por negros).
El resultado es el siguiente:



Luego cerré un poco más los valores de los umbrales para observar el efecto resultante, lo que yo esperaba (obvio) era menos valores de grises, y que se aumentaran los pixeles blancos y negros, esto debido a que ya habría menos cabida para los pixeles grises, ya que se cerraron más los umbrales.
El resultado es el siguiente:
El resultado fue el esperado, una imagen con un mayor número de pixeles negros y blancos que la umbralización normal (umbral inferior = 85, umbral superior 170).

Después de esto, abrí el rango de los umbrales, osea, le di más cabida a los pixeles grises, pasé de los valores 117 para los umbrales inferiores y 137 para los umbrales inferiores a un 77 y 177 para los umbrales inferiores y superiores respectivamente. 

Y este fue el resultado (obviamente con un mayor número de pixeles grises que la imagen anterior, quedando con menos pixeles negros y blancos):


Bueno, ese fue todo mi trabajo para esta entrada 1 del laboratorio, la liga a mi git con el trabajo  es la siguiente: https://github.com/eddypre/VisionTriana/blob/master/Laboratorio1

Ahí mismo también pueden encontrar las versiones que fui realizando (son 3). Y aprovecho para comentarles que en ese mismo pool subiré todos mis avances de proyecto de esta materia.

Cualquier duda o aclaración pueden dejarla en comentarios.

Saludos!