La siguiente función es la que recorre la imagen encontrando los bordes continuos y guardando un elemento que contiene una lista de los bordes continuos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def encuentra_figuras(imagen): | |
"""encuentra cada objeto y guarda en una lista | |
los bordes de cada objeto | |
""" | |
alto, ancho = imagen.size | |
pixeles = imagen.load() | |
colores = [] | |
porcentaje = [] | |
elipses = [] | |
for i in range(alto): | |
for j in range(ancho): | |
if pixeles[i,j] == (255,255,255): | |
r = int(random.random()*250) | |
g = int(random.random()*250) | |
b = int(random.random()*250) | |
nuevo_color = (r,g,b) | |
imagen, cont, masa, c = bfs(imagen, (i,j), nuevo_color) | |
elipses.append(c) | |
total_x = 0 | |
total_y = 0 | |
por = (float(cont)/float(alto*ancho))*100 | |
for l in range(len(masa)): | |
total_x = total_x + masa[l][0] | |
total_y = total_y + masa[l][1] | |
x_centro = total_x/len(masa) | |
y_centro = total_y/len(masa) | |
colores.append([nuevo_color,(x_centro, y_centro), por]) | |
porcentaje.append(por) | |
pixeles = imagen.load() | |
masa = [] | |
figura_mayor = max(porcentaje) | |
i = porcentaje.index(figura_mayor) | |
color_mayor = colores[i][0] | |
imagen.save('colores.png', 'png') | |
return imagen, colores, elipses |
Enseguida lo que hice es eligir pares de píxeles, verificando después de llamar esta función que sean puntos no paralelos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def puntos(elipses): | |
"""puntos aleatorios del contorno de cada | |
figura | |
""" | |
puntos_linea = [] | |
for elipse in elipses: | |
punto1 = random.choice(elipse) | |
punto2 = random.choice(elipse) | |
puntos_linea.append((punto1, punto2)) | |
return puntos_linea |
Lo siguiente fue calcular la tangente, la tangente la saqué solamente del punto que se eligió aleatoriamente en el paso anterior, es por eso que quise programar de manera diferente la gradiente para evitar el usar matrices de numpy, etc, ya que no nos interesan todos los puntos de la imagen solo el del punto de borde aleatorio.
Hasta aquí es esto lo que obtengo al correr el programa:
![]() |
los puntos verdes son los elegidos al azar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def gradiente_sobel(punto, pixeles): | |
print punto | |
x = punto[0] | |
y = punto[1] | |
print x, y | |
z1 = pixeles[x-1, y-1][0] | |
z2 = pixeles[x, y-1][0] | |
z3 = pixeles[x+1, y-1][0] | |
z4 = pixeles[x-1, y][0] | |
z5 = pixeles[x, y][0] | |
z6 = pixeles[x+1, y][0] | |
z7 = pixeles[x-1, y+1][0] | |
z8 = pixeles[x, y+1][0] | |
z9 = pixeles[x+1, y+1][0] | |
Gx = ((z3)+(2*z6)+z9)-((z1)+(2*z4)+(z7)) | |
Gy = ((z7)+(2*z8)+z9)-((z1)+(2*z2)+(z3)) | |
G = sqrt((Gx**2)+(Gy**2)) | |
angulo = atan2(Gy, Gx) | |
angulo = angulo -(pi/2) | |
print G, angulo | |
return x,y, angulo |
Y luego calculé las intersecciones de las dos rectas dibujadas, para esto use la formula que viene en Wikipedia. Y se puede ver de la siguiente manera:
http://en.wikipedia.org/wiki/Line-line_intersection |
![]() |
punto azul es la intersección |
Y luego el punto medio con la formula: ym = (punto_1_y+punto_2_y)/2, xm = (punto_1_x+punto_2_x)/2. Y se puede ver de la siguiente manera:
![]() |
El punto amarillo es el punto medio |
![]() |
El punto amarillo es el punto medio |
Después saque la pendiente de el punto de cruce (azul) con el punto medio(amarillo),
El código es:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def encuentra_puntos(im, elipses, matriz): | |
"""Encuentra el punto medio de los dos punto aleatorios, asi como | |
el punto de interseccion y la pendiente | |
""" | |
puntos_linea = puntos(elipses) | |
pixeles = im.load() | |
lineas = [] | |
largo_linea = 60 | |
draw = ImageDraw.Draw(im) | |
for punto_linea in puntos_linea: | |
s = [] | |
k = [] | |
for punto in punto_linea: | |
x,y,angulo = gradiente_sobel(punto, pixeles) | |
x1 = x - largo_linea*cos(angulo) | |
y1 = y - largo_linea*sin(angulo) | |
x2 = x + largo_linea*cos(angulo) | |
y2 = y + largo_linea*sin(angulo) | |
linea = x1, y1, x2, y2 | |
draw.line((x1,y1, x2, y2),fill="red") | |
draw.ellipse((x-3, y-3, x+3,y+3), fill="green") | |
s.append([x1,y1,x2,y2,x,y]) | |
x1, x2, x3, x4 = s[0][0], s[0][2], s[1][0], s[1][2] | |
y1, y2, y3, y4 = s[0][1], s[0][3], s[1][1], s[1][3] | |
Px_num = (x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4) | |
Px_den = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) | |
Py_num = (x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4) | |
Py_den = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) | |
ym = (s[0][5]+s[1][5])/2 | |
xm = (s[0][4]+s[1][4])/2 | |
try: | |
Px = Px_num/Px_den | |
Py = Py_num/Py_den | |
print "La interseccion esta en %s, %s" %(Px, Py) | |
draw.ellipse((Px-3, Py-3, Px+3,Py+3), fill="blue") | |
draw.ellipse((xm-2, ym-2, xm+2,ym+2), fill="yellow") | |
draw.line((xm,ym, Px, Py),fill="red") | |
pendiente = (Py-ym)/(Px-xm) | |
print "Pendiente %s" %pendiente | |
matriz, im = alarga_linea(pendiente,xm, ym, matriz, im, Px, Py) | |
except Exception, e: | |
print str(e) | |
return matriz, im |
Y alargue la línea hacia adentro del elipse para poder ir votando hasta que se llegue a un píxel que ya no sea figura.
Para alargar la línea utilicé la ecuación punto pendiente en donde como variables conocidad tengo la "x1" a la que solamente aumente de uno en uno para obtener continua la línea, la "x0" que es el punto medio al iniciar y después se va actualizando también tengo la "m" que es la pendiente, y tengo la "y0" que es el punto medio al iniciar y que también se va actualizando, entonces lo único que se hace es despejar la y1.
En esa misma función agregue los votos a una matriz, que van desde el punto medio hasta el pixel que ya no pertenece al objeto.
El código es el siguiente:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def alarga_linea(pendiente,x0, y0, matriz, im, Px, Py): | |
"""alarga el punto hasta que se termina el objeto relleno | |
""" | |
draw = ImageDraw.Draw(im) | |
pixeles = im.load() | |
cont = 1 | |
while True: | |
if Px > x0: | |
x = x0 - 1 | |
else: | |
x = x0 + 1 | |
y = (pendiente*(x - x0)) + y0 | |
x0 = x | |
y0 = y | |
y = int(y) | |
print "la y es %s" %y | |
matriz[x,y] = matriz[x,y] + 1 | |
try: | |
#draw.ellipse((x-4, y-4,x-1,y-1), fill="blue") | |
print pixeles[x, y][0], cont | |
if pixeles[x, y][0] == 255: | |
break | |
except: | |
pass | |
cont = cont + 1 | |
return matriz, im |
Entonces ahora llame a varios puntos por imagen para poder obtener los pixeles más votados, y aquel pixel que es el más votado, es el que resulta ser el centro del elipse, el centro de masa me daba valores no muy buenos ya que aparecían entre el top de píxeles más votados algunos alejados del centro y provocaba que el centro real se moviera un poco.
Este es el resultado:
![]() |
Centro encontrado en naranja, con 200 pares de puntos aleatorios |
![]() |
Centro naranja encontrado con 1000 pares de puntos aleatorios |
![]() |
El punto naranja es el centro encontrado |
Después de tener ya el centro que fue el candidato ganador, se calculó la distancia mínima y máxima como del centro a los puntos que se generaron aleatorios que ayudaron a votar, entonces estos son los semi-diámetros y se calculan el angulo.
![]() |
Los puntos rojos son los máximos y mínimos encontrados |
Teniendo estos datos, ahora se crea la ecuación del elipse
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def dibuja_ec_elipse(temp, centro, maxi, mini): | |
"""dibuja el elipse deacuerdo al los puntos | |
centro y maximos y minimos encontrados | |
""" | |
draw = ImageDraw.Draw(temp) | |
a = 0.0 | |
while True: | |
x = centro[0]+(maxi*cos(a)) | |
y = centro[1]+(mini*sin(a)) | |
draw.ellipse((x-1, y-1, x+1,y+1), fill="yellow") | |
a = a + .01 | |
if a > 2*pi: | |
break | |
return temp |
![]() |
Casi circulo |
Después chequé que porcentaje del borde detectado con el primer filtro que apliqué laplaciano coincide en cierto porcentaje con puntos del elipse que yo dibujé entonces si es elipse si no no es,
Y ahora si con las indicaciones de
- punto azul en el centro
- etiqueta del elipse
- colores anaranjados para pintar el elipse
- porcentaje de las diagonales
Código completo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
from PIL import Image, ImageDraw | |
import random | |
import filtros | |
import time | |
from math import sqrt, fabs, atan2, pi, cos, sin, ceil, degrees | |
import sys | |
import numpy | |
def dibuja_elipse(num,imagen): | |
"""dibuja elipses aleatorios para la imagen inicial | |
""" | |
draw = ImageDraw.Draw(imagen) | |
x_imagen, y_imagen = imagen.size | |
rangox = 20*2 | |
rangoy = 100*2 | |
for i in range(num): | |
ancho = random.randint(rangox, rangoy) | |
largo = random.randint(rangox, rangoy) | |
x = random.randint(ancho, x_imagen-(ancho)) | |
y = random.randint(largo, y_imagen-(largo)) | |
draw.ellipse((x,y, x+ancho,y+largo),fill="black") | |
return imagen | |
def bfs(imagen, origen, color): | |
"""colorea todo el objeto recibe como parametros el | |
nuevo color con el que se pinta,la coordenada de inicio y | |
la imagen, y regresa un arreglo con la masa y la imagen | |
""" | |
c = [] | |
cola = [] | |
cont = 0 | |
masa = [] | |
pixeles = imagen.load() | |
alto, ancho = imagen.size | |
cola.append(origen) | |
original = pixeles[origen] | |
edges = [] | |
while len(cola) > 0: | |
(x, y) = cola.pop(0) | |
actual = pixeles[x, y] | |
if actual == original or actual == color: | |
for dx in [-1, 0, 1]: | |
for dy in [-1, 0, 1]: | |
candidato = (x + dx, y + dy) | |
pix_x = candidato[0] | |
pix_y = candidato[1] | |
if pix_x >= 0 and pix_x < alto and pix_y >= 0 and pix_y < ancho: | |
contenido = pixeles[pix_x, pix_y] | |
if contenido == original: | |
pixeles[pix_x, pix_y] = color | |
masa.append((pix_x,pix_y)) | |
imagen.putpixel((pix_x, pix_y), color) | |
cont += 1 | |
cola.append((pix_x, pix_y)) | |
c.append((pix_x, pix_y)) | |
imagen.save('prueba', 'png') | |
return imagen, cont, masa, c | |
def encuentra_figuras(imagen): | |
"""encuentra cada objeto y guarda en una lista | |
los bordes de cada objeto | |
""" | |
alto, ancho = imagen.size | |
pixeles = imagen.load() | |
colores = [] | |
porcentaje = [] | |
elipses = [] | |
for i in range(alto): | |
for j in range(ancho): | |
if pixeles[i,j] == (255,255,255): | |
r = int(random.random()*250) | |
g = int(random.random()*250) | |
b = int(random.random()*250) | |
nuevo_color = (r,g,b) | |
imagen, cont, masa, c = bfs(imagen, (i,j), nuevo_color) | |
elipses.append(c) | |
total_x = 0 | |
total_y = 0 | |
por = (float(cont)/float(alto*ancho))*100 | |
for l in range(len(masa)): | |
total_x = total_x + masa[l][0] | |
total_y = total_y + masa[l][1] | |
x_centro = total_x/len(masa) | |
y_centro = total_y/len(masa) | |
colores.append([nuevo_color,(x_centro, y_centro), por]) | |
porcentaje.append(por) | |
pixeles = imagen.load() | |
masa = [] | |
figura_mayor = max(porcentaje) | |
i = porcentaje.index(figura_mayor) | |
color_mayor = colores[i][0] | |
imagen.save('colores.png', 'png') | |
return imagen, colores, elipses | |
def puntos(elipses, ptos): | |
"""puntos aleatorios del contorno de cada | |
figura | |
""" | |
puntos_linea = [] | |
for elipse in elipses: | |
punto1 = random.choice(elipse) | |
punto2 = random.choice(elipse) | |
ptos.append(punto1) | |
ptos.append(punto2) | |
puntos_linea.append((punto1, punto2)) | |
return puntos_linea, ptos | |
def crea_imagen(ancho, largo): | |
"""crea una imagen con cierta dim, en blanco | |
""" | |
im = Image.new('RGB', (ancho,largo), (255,255,255)) | |
return im | |
def gradiente_sobel(punto, pixeles): | |
#print punto | |
x = punto[0] | |
y = punto[1] | |
#print x, y | |
z1 = pixeles[x-1, y-1][0] | |
z2 = pixeles[x, y-1][0] | |
z3 = pixeles[x+1, y-1][0] | |
z4 = pixeles[x-1, y][0] | |
z5 = pixeles[x, y][0] | |
z6 = pixeles[x+1, y][0] | |
z7 = pixeles[x-1, y+1][0] | |
z8 = pixeles[x, y+1][0] | |
z9 = pixeles[x+1, y+1][0] | |
Gx = ((z3)+(2*z6)+z9)-((z1)+(2*z4)+(z7)) | |
Gy = ((z7)+(2*z8)+z9)-((z1)+(2*z2)+(z3)) | |
G = sqrt((Gx**2)+(Gy**2)) | |
angulo = atan2(Gy, Gx) | |
angulo = angulo -(pi/2) | |
#print G, angulo | |
return x,y, angulo | |
def alarga_linea(pendiente,x0, y0, matriz, im, Px, Py): | |
"""alarga el punto hasta que se termina el objeto relleno | |
""" | |
draw = ImageDraw.Draw(im) | |
pixeles = im.load() | |
cont = 1 | |
while True: | |
if Px > x0: | |
x = x0 - 1 | |
else: | |
x = x0 + 1 | |
y = (pendiente*(x - x0)) + y0 | |
x0 = x | |
y0 = y | |
y = int(y) | |
#print "la y es %s" %y | |
matriz[x,y] = matriz[x,y] + 1 | |
try: | |
#draw.ellipse((x-4, y-4,x-1,y-1), fill="blue") | |
#print pixeles[x, y][0], cont | |
if pixeles[x, y][0] == 255: | |
break | |
except: | |
pass | |
cont = cont + 1 | |
return matriz, im | |
def dibuja_ec_elipse(temp, centro, maxi, mini): | |
"""dibuja el elipse deacuerdo al los puntos | |
centro y maximos y minimos encontrados con su ecuacion | |
""" | |
puntos = [] | |
draw = ImageDraw.Draw(temp) | |
a = 0.0 | |
color = (random.randint(175,255), random.randint(114, 196), 0) | |
while True: | |
x = centro[0]+(maxi*cos(a)) | |
y = centro[1]+(mini*sin(a)) | |
puntos.append((x,y)) | |
draw.ellipse((x-1, y-1, x+1,y+1), fill=color) | |
a = a + .01 | |
if a > 2*pi: | |
break | |
return temp, puntos | |
def encuentra_puntos(im, elipses, matriz, ptos): | |
"""Encuentra el punto medio de los dos punto aleatorios, asi como | |
el punto de interseccion y la pendiente | |
""" | |
puntos_linea, ptos = puntos(elipses, ptos) | |
pixeles = im.load() | |
lineas = [] | |
largo_linea = 60 | |
draw = ImageDraw.Draw(im) | |
for punto_linea in puntos_linea: | |
s = [] | |
k = [] | |
for punto in punto_linea: | |
x,y,angulo = gradiente_sobel(punto, pixeles) | |
x1 = x - largo_linea*cos(angulo) | |
y1 = y - largo_linea*sin(angulo) | |
x2 = x + largo_linea*cos(angulo) | |
y2 = y + largo_linea*sin(angulo) | |
linea = x1, y1, x2, y2 | |
draw.line((x1,y1, x2, y2),fill="red") | |
draw.ellipse((x-3, y-3, x+3,y+3), fill="green") | |
s.append([x1,y1,x2,y2,x,y]) | |
x1, x2, x3, x4 = s[0][0], s[0][2], s[1][0], s[1][2] | |
y1, y2, y3, y4 = s[0][1], s[0][3], s[1][1], s[1][3] | |
Px_num = (x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4) | |
Px_den = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) | |
Py_num = (x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4) | |
Py_den = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) | |
ym = (s[0][5]+s[1][5])/2 | |
xm = (s[0][4]+s[1][4])/2 | |
try: | |
Px = Px_num/Px_den | |
Py = Py_num/Py_den | |
#print "La interseccion esta en %s, %s" %(Px, Py) | |
draw.ellipse((Px-3, Py-3, Px+3,Py+3), fill="blue") | |
draw.ellipse((xm-2, ym-2, xm+2,ym+2), fill="yellow") | |
draw.line((xm,ym, Px, Py),fill="red") | |
pendiente = (Py-ym)/(Px-xm) | |
#print "Pendiente %s" %pendiente | |
matriz, im = alarga_linea(pendiente,xm, ym, matriz, im, Px, Py) | |
except Exception, e: | |
#print str(e) | |
pass | |
return matriz, im, ptos | |
def main(): | |
"""funcion principal | |
""" | |
num = 2 | |
largo = 500 | |
ancho = 700 | |
im = crea_imagen(ancho, largo) | |
x, y = im.size | |
diagonal = sqrt((x-0)**2 + (y-0)**2) | |
im = dibuja_elipse(num,im) | |
temp = im.copy() | |
im.save("original.png") | |
im = filtros.hacer_gris(im) | |
mascara = [[0,1,0],[1,-4,1],[0,1,0]] | |
lap = filtros.convolucion(im, mascara) | |
lap.save("lap.png") | |
c = 0 | |
im1, colores, elipses =encuentra_figuras(lap) | |
#matriz = [[0 for i in range(y)] for j in range(x)] | |
for figura in elipses: | |
c = c + 1 | |
matriz_1 = numpy.zeros(x*y).reshape((x,y)) | |
#print matriz_1 | |
tem = [] | |
dic = {} | |
tem.append(figura) | |
ptos = [] | |
for i in range(2000): | |
matriz_1, im, ptos = encuentra_puntos(im, tem, matriz_1, ptos) | |
i,j = numpy.unravel_index(matriz_1.argmax(), matriz_1.shape) | |
#print matriz_1[i,j] | |
draw = ImageDraw.Draw(temp) | |
draw.ellipse((i-5, j-5, i+5,j+5), fill="blue") | |
#print ptos | |
for pto in ptos: | |
x_0 = pto[0] | |
y_0 = pto[1] | |
distancia = sqrt((x_0-i)**2 + (y_0-j)**2) | |
dic[pto] = distancia | |
#print dic | |
key1,value = max(dic.iteritems(), key=lambda x:x[1]) | |
draw.ellipse((key1[0]-5, key1[1]-5, key1[0]+5,key1[1]+5), fill="red") | |
deltay = j-key1[1] | |
deltax = i-key1[0] | |
angulo1 = abs(degrees(atan2(deltay, deltax))-180) | |
key2,value = min(dic.iteritems(), key=lambda x:x[1]) | |
draw.ellipse((key2[0]-5, key2[1]-5, key2[0]+5,key2[1]+5), fill="red") | |
deltay = j-key2[1] | |
deltax = i-key2[0] | |
angulo2 = abs(degrees(atan2(deltay, deltax))-180) | |
diagonal_elipse = sqrt((key1[0]-key2[0])**2 + (key1[1]-key2[1])**2) | |
p = (diagonal_elipse*100)/diagonal | |
print "*****************************************************" | |
print "Se encontro elipse con centro en (%s, %s)" %(i, j) | |
print "el radio maximo es de %s con angulo de %s" %(dic[key1], angulo1) | |
print "el radio minimo es de %s con angulo de %s" %(dic[key2], angulo2) | |
print "tiene porcentaje de %s" %p | |
print "*****************************************************" | |
centro = (i, j) | |
maxi = dic[key1] | |
mini = dic[key2] | |
if (maxi >= 315 and maxi <= 45) or (maxi >= 135 and maxi <= 225): | |
a = maxi | |
b = mini | |
else: | |
a = mini | |
b = maxi | |
temp, puntos = dibuja_ec_elipse(temp, centro, a, b) | |
draw.text(centro, str(c), fill=(255,255,255)) | |
l=0 | |
for s in puntos: | |
if s in ptos: | |
l = l + 1 | |
if l > 30: | |
print "si es elipse" | |
temp.show() | |
main() |
Referencias
Elipses
Ecuación elipse
Muy bien; 5 pts.
ResponderEliminar