import string, random
import numpy as np
import Tkinter
from PIL import ImageDraw
import Image
import ImageTk
from sys import argv
import time

def det(p, q, r):
    sum1 = q[0]*r[1] + p[0]*q[1] + r[0]*p[1]
    sum2 = q[0]*p[1] + r[0]*q[1] + p[0]*r[1]
    return sum1 - sum2


def convolucion(imagen, h):
    iwidth, iheight = imagen.size
    imagen = imagen.convert('L')
    im = imagen.load()
    mheight, mwidth = h.shape
    print "Imagen size: ",imagen.size
    print "H: ",h.shape
    g = np.zeros(shape=(iheight, iwidth))
    for x in xrange(iheight):
        for y in xrange(iwidth):
            sum = 0.0
            for j in xrange(mheight):
                zj = ( j - ( mheight / 2 ) )
                for i in xrange(mwidth):
                    zi = ( i - ( mwidth / 2 ) )
                    try:
                        sum += im[y + zi, x + zj] * h[i,j]
                    except:
                        pass
            print x, y
            g[x,y] = sum
    print "Convolucion"
    print g
    return g

def filtro(original):
    width, height = original.size
    print width, height
    original = original.convert('L')
    modificado = Image.new(mode='L', size =(width,height))
    org = original.load()
    mod = modificado.load()
    contador = 0
    min = 0
    max = 0
    for y in xrange(height):
        for x in xrange(width):
            pixel = org[x,y]
            if min >= pixel:
                min = pixel
            if max <= pixel:
                max = pixel
    print "MAX:",max," MIN:",min
    for y in xrange(height):
        for x in xrange(width):
            pixel = org[x,y]
            try:
                pixel += org[x-1,y]
                contador+=1
            except:
                None
            try:
                pixel += org[x+1,y]
                contador+=1
            except:
                None
            try:
                pixel += org[x,y+1]
                contador+=1
            except:
                None
            try:
                pixel += org[x,y-1]
                contador+=1
            except:
                None
            promedio = (pixel) / (contador)
            r = max - min
            prop = 256.0 / r
            p = int((promedio -min) * prop)
            if p <= 90:
                mod[x,y] = 0
            else: 
                mod[x,y] = 255
            print mod[x,y]
            print x,y
            contador = 1
            pixel = 0
    data = np.array(modificado)
    print data
    print data.shape
    im = Image.fromarray(data)
    return im
         
def filtroPorNumeros(im,n):
    for x in xrange(n):
        im = filtro(im)
    return im

def escalaDeGrises(im):
    width, height = im.size
    print width, height
    im = im.convert('RGB')
    pix = im.load()
    promedio = 0.0
    for y in xrange(height):
            for x in xrange(width):
                r, g, b = pix[x, y]
                promedio = (r+g+b)/3.0
                pix[x, y] = int(promedio), int(promedio), int(promedio)
    data = np.array(im)
    im2 = Image.fromarray(data)
    return im2

def nuevaImagen(matriz):
    height, width = matriz.shape
    print matriz.shape
    imagen = Image.new(mode='L', size =(width,height))
    im = imagen.load()
    print imagen.size
    for x in xrange(height):
        for y in xrange(width):
            im[y, x] = matriz[x, y]
    data = np.array(imagen)
    print data
    im = Image.fromarray(data)
    return im

def convexHull(imagen):
    width, height = imagen.size
    imagen = imagen.convert('RGB')
    im = imagen.load()
    contador = 0
    stack = []
    for x in xrange(height):
        for y in xrange(width):
            r, g, b = im[y, x]
            if r < 180:
                im[y, x] = 0, 0, 0
            else:
                contador+=1
                if contador == 500:
                    im[y, x] = 255, 0, 0
                    stack.append((y,x))
                    contador = 0
                else:
                    im[y, x] = 255, 255, 255
    data = np.array(imagen)
    im = Image.fromarray(data)
    return im, stack

def binarizacion(imagen):
    width, height = imagen.size
    imagen = imagen.convert('L')
    im = imagen.load()
    for x in xrange(height):
        for y in xrange(width):
            pixel = im[y, x]
            if pixel < 3:
                im[y, x] = 0
            else:
                im[y, x] = 255
    data = np.array(imagen)
    im = Image.fromarray(data)
    return im

def dibujaPuntos(stack, imagen):
    draw = ImageDraw.Draw(imagen)
    
    draw.polygon(convexH(stack), fill=None, outline=None)
    return imagen

def cambiaAderecha((p, q, r)):
    assert p != q and q != r and p != r        
    if det(p, q, r) < 0:
        return 1
    else:
        return 0

#Calcula el convex hull por medio de una lista de puntos
def convexH(P):
   #Saca una copia de la lista y las acomoda
    points = map(None, P)
    points.sort()
    #toma los primeros valores
    upper = [points[0], points[1]]
    #mientras el angulo se forma por los puntos len(upper)
    #no toma encuenta los demas numeros
    for p in points[2:]:
        upper.append(p)
        while len(upper) > 2 and not cambiaAderecha(upper[-3:]):
                del upper[-2].
    points.reverse()
    lower = [points[0], points[1]]
    for p in points[2:]:
        lower.append(p)
        while len(lower) > 2 and not cambiaAderecha(lower[-3:]):
                del lower[-2]
    #quita los duplicados
    del lower[0]
    del lower[-1]
    return tuple(upper + lower)


def main():
    imagen = Image.open(argv[1])
    original = imagen
    escalaGrises = escalaDeGrises(imagen)
    px = np.array([[-1,0,1], [-1,0,1], [-1,0,1]])
    py = np.array([[1,1,1], [0,0,0], [-1,-1,-1]])
    t1 = time.time()
    gx = convolucion(escalaGrises, px)
    gx = gx ** 2
    gy = convolucion(escalaGrises, py)
    gy = gy ** 2
    g = (gx + gy ) ** 1.0/2.0
    print g
    min = np.min(g)
    max = np.max(g)
    h, w = g.shape
    minimos = np.ones(shape=(h, w))
    minimos *= min
    g = g - min
    print "Restando el minimo", g
    g = g / (max - min)
    print "Dividiendo el max-min",g
    print "Max: ",np.max(g)," Min: ",np.min(g)
    bn = np.ones(shape=(h, w))
    bn *= 255
    g = g * bn
    print "Max: ",np.max(g)," Min: ",np.min(g)
    imagen_nueva = nuevaImagen(g)
    imagen_binaria = binarizacion(imagen_nueva)
    imagen_convexHull, stack = convexHull(imagen_binaria)
    imagen_convexHull = dibujaPuntos(stack, imagen_convexHull)
    root = Tkinter.Tk()
    tkimageModf = ImageTk.PhotoImage(imagen_nueva)
    tkimageOrig = ImageTk.PhotoImage(imagen_binaria)
    tkimageConvexHull = ImageTk.PhotoImage(imagen_convexHull)
    #Tkinter.Label(root, image = tkimageModf).pack(side="left")
    Tkinter.Label(root, image = tkimageOrig).pack(side="right")
    Tkinter.Label(root, image = tkimageConvexHull).pack(side="left")
    t2 =time.time()
    print "Tiempo total: ",t2-t1
    root.mainloop()
main()