Encontrando las líneas de una imagen

6 06 2009

Bueno pues últimamente he estado interesado por el procesamiento digital de imágenes y aquí dejo un script que hice para encontrar líneas en una imagen con la transformada de Hough.

Hay un problema con el script, se basa en el ángulo de las líneas y en la distancia a la esquina superior izquerda de la pantalla, pero lo malo de esto es que siempre hay dos líneas que tengan el mismo ángulo y la misma distancia al origen y hay que decidir cuál de las dos es. Abajo el código (en Python).

from PIL import Image
from math import pi,cos,sin,fabs,tan,sqrt
from sys import argv,exit,stdout
from Numeric import zeros
MINPUN=100 ## CANTIDAD DE PUNTOS QUE NECESITA UNA LINEA PARA SER CONSIDERADA
MINDIF=100 ## DIFERENCIA MINIMA DE COLOR PARA CONSIDERAR UN PUNTO COMO POSIBLE MIEMBRO DE UNA LINEA
def noruta(nombre):
	tmp=""
	for i in range(len(nombre)):
		if nombre[-(i+1)]=="/":
			return tmp
		tmp=nombre[-(i+1)]+tmp
def luminancia(tupla):
	return .299*tupla[0]+.587*tupla[1] +.114*tupla[2]
def rad(theta):
	return theta*pi/180
def radio(x,y,theta):
	"Devuelve el radio minimo que pueda tener un circulo"
	"con centro en (0,0) y una linea de angulo theta (en radianes) que pasa"
	"por el punto (x,y)"
	if theta==rad(90):
		return x
	elif theta==0:
		return y
	theta=-theta
	x1=-ordenada(x,y,theta)/(tan(theta)+1/tan(theta))
	y1=tan(theta)*x1+ordenada(x,y,theta)
	return sqrt(x1**2+y1**2)
def ordenada(x,y,theta):
	"Devuelve el valor de la ordenada al origen de una linea"
	"con angulo theta (en radianes) que pasa por los puntos (x,y)"
	return (y-tan(theta)*x)
def uso():
	print "Encuentra las lineas de una imagen con la transformada de Hough"
	print "Uso: %s <imagen>"%argv[0]
	exit("")
if len(argv)<2:
	uso()
try: img=Image.open(argv[1])
except: exit("No se pudo abrir el archivo")
ancho,alto=img.size
maxr=int(round(sqrt(ancho**2+alto**2)))
lineas=zeros((maxr,180))
print "Encontrando las lineas"
for y in range(alto-1):
	for x in range(ancho-1):
		grisxy=luminancia(img.getpixel((x,y)))
		difx=fabs( grisxy - luminancia(img.getpixel((x+1,y))) )
		dify=fabs( grisxy - luminancia(img.getpixel((x,y+1))) )
		if difx+dify>MINDIF:
			img.putpixel((x,y),(0,0,255))
			for theta in range(180):
				r=int(round(radio(x,y,rad(theta))))
				if r<maxr:
					lineas[r][theta]+=1
	stdout.write("\b"*20+"%3.2f Completado"%(100.0*(y)/(alto-1)))
	stdout.flush()
print ""
resultado=[]
max=0
for y in range(180):
	for x in range(maxr):
		if lineas[x][y]>MINPUN:
			resultado.append((x,rad(y)))
print "Encontradas %d lineas"%len(resultado)
for line in resultado:			
 	r,theta=line[0],line[1]
	if theta==0:
		m=0
		b=r
	else:
		m=tan(theta)
		b=r/cos(theta)
#		if theta>rad(90): ## problemas aquí
#			b=-b		  ## y aquí
	if len(resultado)<=5:
		print "radio="+str(r)
		print "angulo="+str(theta*180/pi)
		print "ecuacion de la recta"
		print "y=%.2f*x+(%.2f)"%(m,b)
	if 45<theta*180/pi<135:
		for y in range(alto):
			x=int(round((y-b)/-m))
			if 0<=x<ancho:
				img.putpixel((x,y),(255,0,0))
	else:
		for x in range(ancho): 
			y=int(round(-m*x+b))
			if 0<=y<alto:
				img.putpixel((x,y),(255,0,0))

img.show("%d lineas"%len(resultado))
#img.save("TH-"+noruta(argv[1]))

Cuando el ángulo es menor de 90º esto no es problema ya que sólo una de las dos líneas pasa por la pantalla, pero cuando es mayor a 90º, las dos líneas pueden pasar por la pantalla y a veces no funciona.

Una imagen de ejemplo:
TH-avion

Las soluciones que se me han ocurrido no son muy elegantes pero weno. Si alguien sabe cómo solucionarlo o tiene alguna cosa que decir, los comentarios son muy aceptados, saludos😛

from PIL import Image
from math import pi,cos,sin,fabs,tan,sqrt
from sys import argv,exit,stdout
from Numeric import zeros
MINPUN=100 ## CANTIDAD DE PUNTOS QUE NECESITA UNA LINEA PARA SER CONSIDERADA
MINDIF=100 ## DIFERENCIA MINIMA DE COLOR PARA CONSIDERAR UN PUNTO COMO POSIBLE MIEMBRO DE UNA LINEA
def noruta(nombre):
tmp=””
for i in range(len(nombre)):
if nombre[-(i+1)]==”/”:
return tmp
tmp=nombre[-(i+1)]+tmp
def luminancia(tupla):
return .299*tupla[0]+.587*tupla[1] +.114*tupla[2]
def rad(theta):
return theta*pi/180
def radio(x,y,theta):
“Devuelve el radio minimo que pueda tener un circulo”
“con centro en (0,0) y una linea de angulo theta (en radianes) que pasa”
“por el punto (x,y)”
if theta==rad(90):
return x
elif theta==0:
return y
theta=-theta
x1=-ordenada(x,y,theta)/(tan(theta)+1/tan(theta))
y1=tan(theta)*x1+ordenada(x,y,theta)
return sqrt(x1**2+y1**2)
def ordenada(x,y,theta):
“Devuelve el valor de la ordenada al origen de una linea”
“con angulo theta (en radianes) que pasa por los puntos (x,y)”
return (y-tan(theta)*x)
def uso():
print “Encuentra las lineas de una imagen con la transformada de Hough”
print “Uso: %s <imagen>”%argv[0]
exit(“”)
if len(argv)<2:
uso()
try: img=Image.open(argv[1])
except: exit(“No se pudo abrir el archivo”)
ancho,alto=img.size
maxr=int(round(sqrt(ancho**2+alto**2)))
lineas=zeros((maxr,180))
print “Encontrando las lineas”
for y in range(alto-1):
for x in range(ancho-1):
grisxy=luminancia(img.getpixel((x,y)))
difx=fabs( grisxy – luminancia(img.getpixel((x+1,y))) )
dify=fabs( grisxy – luminancia(img.getpixel((x,y+1))) )
if difx+dify>MINDIF:
img.putpixel((x,y),(0,0,255))
for theta in range(180):
r=int(round(radio(x,y,rad(theta))))
if r<maxr:
lineas[r][theta]+=1
stdout.write(“\b”*20+”%3.2f Completado”%(100.0*(y)/(alto-1)))
stdout.flush()
print “”
resultado=[]
max=0
for y in range(180):
for x in range(maxr):
if lineas[x][y]>MINPUN:
resultado.append((x,rad(y)))
print “Encontradas %d lineas”%len(resultado)
for line in resultado:
r,theta=line[0],line[1]
if theta==0:
m=0
b=r
else:
m=tan(theta)
b=r/cos(theta)
# if theta>rad(90): ## problemas aquí
# b=-b ## y aquí
if len(resultado)<=5:
print “radio=”+str(r)
print “angulo=”+str(theta*180/pi)
print “ecuacion de la recta”
print “y=%.2f*x+(%.2f)”%(m,b)
if 45<theta*180/pi<135:
for y in range(alto):
x=int(round((y-b)/-m))
if 0<=x<ancho:
img.putpixel((x,y),(255,0,0))
else:
for x in range(ancho):
y=int(round(-m*x+b))
if 0<=y<alto:
img.putpixel((x,y),(255,0,0))

img.show(“%d lineas”%len(resultado))
#img.save(“TH-“+noruta(argv[1]))


Acciones

Information

4 responses

6 06 2009
maryoh

Genial!!! no cabe duda que eres genial, solo lamento no ser de mucha ayuda =( pero ahora estoy entendiendo más tus ideas y conceptos jeje. Mm no se, es como encontrar la simetría, ángulos y los vectores a las cosas de la vida real, como encontrarle una respuesta matemática a un reflejo, o simplemente disfrutar de las coincidencias =)

Jejeje eres genial!!!!!!!! =P y que bueno que te das una vuelta por aquí, solo espero que alguien pueda ayudarte un poco y así logres corregir esa duda =) jeje animo!!!!!!!! =P

26 10 2009
Rommel Sanchez

OpenCV tiene ya incorporada muchas de estas funciones hay binding pra Python. Lo has probado ? Un saludo!

11 05 2012
Hector

Donde esta Numeric?
Gracias

11 05 2012
Betoman

De numeric importa la función zeros, lo usé en la línea 42, si es lo que preguntas.
Saludos

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s




A %d blogueros les gusta esto: