Laboratorio de Visión Computacional
Actividad 6
Actividad 6
A diferencia del código mostrado en la publicación de la clase, este código diferencia entre elipses y círculos, ya que el método de cuerda-tangente puede ser usado para la detección de ambas figuras.
En este código una vez detectado el centro de la figura, a partir de este punto se usa BFS para ir coloreando todos los pixeles dentro del contorno del elipse. A cada elipse y círculo se le asigna un número y se imprimen sus semidiámetros o radios, según corresponda, y por último se imprime para cada uno un porcentaje del tamaño con respecto al tamaño total de la imagen.
Código
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 bfs(self, image, start_pixel_pos, color): | |
pixels = image.load() | |
width, height = get_image_size(image) | |
queue = [] | |
copy = [] | |
count = 0 | |
queue.append(start_pixel_pos) | |
original = pixels[start_pixel_pos] | |
while 0 < len(queue): | |
(x, y) = queue.pop(0) | |
current = pixels[x, y] | |
if current == original or current == color: | |
for pos_x in [-1, 0, 1]: | |
for pos_y in [-1, 0, 1]: | |
pixel_x = x + pos_x | |
pixel_y = y + pos_y | |
if pixel_x >= 0 and pixel_x < width and pixel_y >= 0 and pixel_y < height: | |
pixel_data = pixels[pixel_x, pixel_y] | |
if pixel_data == original: | |
pixels[pixel_x, pixel_y] = color | |
queue.append((pixel_x, pixel_y)) | |
copy.append((pixel_x, pixel_y)) | |
count += 1 | |
return image, count, copy | |
def detect_forms(self, image): | |
pixels = image.load() | |
width, height = get_image_size(image) | |
percentages = [] | |
all_colors = [] | |
can_be_ellipses = [] | |
for i in range(width): | |
for j in range(height): | |
if pixels[i, j] == (255, 255, 255): | |
r = random.randint(150, 255) | |
g = random.randint(150, 255) | |
b = random.randint(150, 255) | |
image, count, copy = self.bfs(image, (i, j), (r, g, b)) | |
per = float(count)/float(width * height) | |
percentages.append(per) | |
all_colors.append((r, g, b)) | |
pixels = image.load() | |
can_be_ellipses.append(copy) | |
return can_be_ellipses, image | |
def search_ellipse(self, can_be_ellipses, image, Gx, Gy): | |
width, height = get_image_size(image) | |
pixels_gx = Gx.load() | |
pixels_gy = Gy.load() | |
l = 80 | |
ellipses_found = [] | |
for ellipse in can_be_ellipses: | |
pixels = image.load() | |
votes = [] | |
for i in range(width): | |
votes.append([0] * height) | |
for i in range(100): | |
P1 = random.choice(ellipse) | |
P2 = random.choice(ellipse) | |
px1 = P1[0] | |
py1 = P1[1] | |
px2 = P2[0] | |
py2 = P2[1] | |
Mx = (px1 + px2)/2 | |
My = (py1 + py2)/2 | |
gx1 = pixels_gx[px1, py1][0] | |
gy1 = pixels_gy[px1, py1][0] | |
gx2 = pixels_gx[px2, py2][0] | |
gy2 = pixels_gy[px2, py2][0] | |
if abs(gx1) + abs(gy1) <= 0: | |
theta = None | |
else: | |
theta = math.atan2(gy1, gx1) | |
theta -= math.pi/2 | |
x0 = px1 - l * math.cos(theta) | |
y0 = py1 - l * math.sin(theta) | |
x1 = px1 + l * math.cos(theta) | |
y1 = py1 + l * math.sin(theta) | |
if abs(gx2) + abs(gy2) <= 0: | |
theta = None | |
else: | |
theta = math.atan2(gy2, gx2) | |
theta -= math.pi/2 | |
x2 = px2 - l * math.cos(theta) | |
y2 = py2 - l * math.sin(theta) | |
x3 = px2 + l * math.cos(theta) | |
y3 = py2 + l * math.sin(theta) | |
try: | |
Tx = ((x0*y1-y0*x1)*(x2-x3)-(x0-x1)*(x2*y3-y2*x3))/((x0-x1)*(y2-y3)-(y0-y1)*(x2-x3)) | |
Ty = ((x0*y1-y0*x1)*(y2-y3)-(y0-y1)*(x2*y3-y2*x3))/((x0-x1)*(y2-y3)-(y0-y1)*(x2-x3)) | |
Kx = Tx - Mx | |
Ky = Ty - My | |
m = Ky/Kx | |
x0 = Mx | |
y0 = My | |
while True: | |
x = int(x0 + 1) | |
y = int(m*(x - x0) + y0) | |
if pixels[x, y] == (0, 0, 0): | |
votes[x][y] += 1 | |
x0 = x | |
y0 = y | |
else: | |
break | |
except: | |
pass | |
morevotes = 0 | |
for x in range(width): | |
for y in range(height): | |
v = votes[x][y] | |
if v > morevotes: | |
morevotes = v | |
center_x = x | |
center_y = y | |
for coord in ellipse: | |
x, y = coord | |
if center_x == x: | |
ydiameter = abs(center_y - y) | |
if center_y == y: | |
xdiameter = abs(center_x - x) | |
ellipses_found.append((center_x, center_y, xdiameter, ydiameter)) | |
return ellipses_found | |
def draw_ellipses_found(self, image, ellipses_found): | |
draw = ImageDraw.Draw(image) | |
max_w, max_h = get_image_size(image) | |
counter_circle = 1 | |
counter_ellipse = 1 | |
for ellipse in ellipses_found: | |
x, y, xd, yd = ellipse | |
r = random.randint(100, 255) | |
g = random.randint(100, 255) | |
b = random.randint(100, 255) | |
image, count, copy = self.bfs(image, (x, y), (r, g, b)) | |
percentage = (count * 100)/(SIZE**2) | |
if abs(xd - yd) < 20: | |
print '\nCircle %d detected at center (%d, %d)' % (counter_circle, x, y) | |
print '> Radio', xd | |
print '> %.1f%% of all the image' % percentage | |
draw.ellipse((x-xd, y-xd, x+xd, y+xd), outline=(0, 150, 255), fill=None) | |
draw.ellipse((x-1, y-1, x+1, y+1), fill=(255, 255, 0), outline=(255, 255, 0)) | |
draw.text((x-20, y-15), 'Circle '+str(counter_circle), fill=(255, 100, 0)) | |
counter_circle += 1 | |
else: | |
print '\nEllipse %d detected at center (%d, %d)' % (counter_ellipse, x, y) | |
print '> Semidiameters:', xd, yd | |
print '> %.1f%% of all the image' % percentage | |
draw.ellipse((x-xd, y-yd, x+xd, y+yd), outline=(0, 150, 255), fill=None) | |
draw.ellipse((x-2, y-2, x+2, y+2), fill=(255, 255, 0), outline=(255, 255, 0)) | |
draw.text((x-20, y-15), 'Ellipse '+str(counter_ellipse), fill=(255, 100, 0)) | |
counter_ellipse += 1 | |
return image | |
def action(self): | |
original_image = Image.new('RGB', (SIZE, SIZE), (255, 255, 255)) | |
original_image = self.draw_some_ellipses([(100, 40, 120, 50), (70, 70, 220, 220), (30, 90, 50, 200)], original_image) | |
original_image = grayscale(original_image) | |
# detect all the edges | |
h = [[0, 1, 0], [1, -4, 1], [0, 1, 0]] | |
image = self.convolution(h, original_image) | |
image.save('original.png', 'png') | |
can_be_ellipses, image_bfs = self.detect_forms(image) | |
# gradient | |
sobely = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]] | |
sobelx = [[1, 2, 1], [0, 0, 0], [-1, -2, -1]] | |
Gx = self.convolution(sobelx, original_image) | |
Gy = self.convolution(sobely, original_image) | |
image_bfs = average_allneighbors(image_bfs) | |
image_bfs.save('bfs.png', 'png') | |
ellipses_found = self.search_ellipse(can_be_ellipses, image_bfs, Gx, Gy) | |
image = self.draw_ellipses_found(original_image, ellipses_found) | |
image.save('result.png', 'png') | |
self.update_image(image) |
Pruebas
Imagen después de binarizar y obtener bordes de las figuras.
Imagen obtenida.
Salida en el terminal.
Imagen después de haber encontrado los bordes y haber binarizado, antes de mandarse al método que buscará posibles elipses y círculos.
Resultado obtenido.
Y por último una captura de la ventana desplegada junto a la terminal donde se muestran las figuras detectadas, en este caso elipses y círculos, y se muestra en donde se encontró su centro, si es un elipse los semidiámetros o si es un círculo su radio, así como el porcentaje que ocupa la figura en relación a la totalidad de pixeles de la imagen.
Bien; 10 pts, pero casi te quito un punto por usar un constante arbitrario 20. Mejor que depende de la resolución de la imagen o algo así.
ResponderEliminar