Visión Computacional
Tarea 1
Tarea 1
Existen varias máscaras que se pueden aplicar para la detección de bordes en una imagen. Al experimentar con varias de ellas, encontré una llamada Operador Laplaciano que fue el que me dio mejor resultado y fue el que utilice para la detección de bordes con mi programa.
\begin{bmatrix}{0}&&{1}&&{0}\\{1}&&{-4}&&{1}\\{0}&&{1}&&{0}\end{bmatrix}
Podemos ver que esta máscara es simétrica, por lo que no fue necesario aplicar más de una vez el método de convolución, ya que comúnmente se suelen utilizar por lo menos dos máscaras, una para bordes horizontales y una para bordes verticales.
Utilizando esta máscara podemos ver resultados como el siguiente:
Para que los bordes resultaran contínuos, experimente con la aplicación de algunos filtros simples como el de difuminado y una versión de umbrales, que yo llamo blanco y negro, donde recibe un parámetro que indica el valor del umbral al cual hay que compararse para determinar si el pixel se convierte a blanco o negro.
La desventaja de aplicar estos filtros es que algunos de los bordes menos detallados se pierden, pero es posible arreglarlo cambiando la forma en que los umbrales son aplicados a diferentes escalas de grises en la imagen, pero por ahora he dejado un valor predeterminado en este método de umbrales, ya que logra resaltar las características más sobresalientes de las imágenes en general.
La experimentación fue visual, y los filtros aplicados para resaltar los bordes aveces variaban según la imagen con la que estaba haciendo pruebas, pero el filtro de difuminado y después aplicación de umbrales fue el que logró hacer diferenciar los bordes para la mayoría de las imágenes.
Se realizó una prueba de tiempo de ejecución de la rutina completa para obtener los bordes de la imagen incluyendo el tiempo que tarda el filtro para escala de grises antes de aplicar la máscara, y los filtros de difuminado y umbrales después de aplicar la máscara, así como el tiempo que tarda en aplicar la propia máscara.
Se hizo un promedio del tiempo de 30 repeticiones para diferentes imágenes y diferentes dimensiones.
Ahora veamos la comparativa de la imagen original contra la imagen después de la detección de bordes, y la tabla con los resultados de la prueba de tiempo de ejecución.
Perry
Los resultados de la prueba de tiempo para la imagen perry-el-ornitorrinco.jpg fue la siguiente:
Dimensión | Tiempo de ejecución |
---|---|
300x200 | 2.10296666622 |
500x400 | 7.42993968725 |
700x550 | 14.6455515027 |
Snoopy
Los resultados de la prueba de tiempo para la imagen snoopy.jpg fue la siguiente:
Dimensión | Tiempo de ejecución |
---|---|
200x100 | 0.539839204152 |
300x200 | 2.12622498671 |
400x300 | 5.17025875251 |
Fundidora
Los resultados de la prueba de tiempo para la imagen fundidora.jpg fue la siguiente:
Dimensión | Tiempo de ejecución |
---|---|
300x200 | 2.14322346449 |
500x400 | 7.40442377329 |
1000x700 | 28.0735495091 |
Código
Los filtros que ya había creado anteriormente los coloque en un solo script de python con nombre filters_methods.py, y en el script edges.py lo importo para poder usar el método del filtro deseado.
El programa edges.py recibe como parámetro el nombre del archivo de una imagen, y dos números correspondientes al ancho y alto con que se desea ver la imagen.
Ejemplo:
~$ python edges.py images/snoopy.jpg 400 300
Una vez que se muestra la ventana y después de presionar el botón "Convolution" podremos visualizar lo 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
from Tkinter import * | |
import sys, os, time, Image, ImageTk | |
from filters_methods import * | |
class Edges: | |
def __init__(self, image_file_path, w, h): | |
self.image_file_path = image_file_path | |
self.w = w | |
self.h = h | |
image = self.open_image(self.image_file_path) | |
self.root = Tk() | |
self.root.title('Edges') | |
self.root.resizable(width=False, height=False) | |
self.imagetk_original = self.convert_to_imagetk(image) | |
self.imagetk_modified = self.imagetk_original | |
self.label1 = Label(self.root, image=self.imagetk_original) | |
self.label1.pack(side=LEFT) | |
self.label2 = Label(self.root, image=self.imagetk_modified) | |
self.label2.pack(side=LEFT) | |
self.button1 = Button(text='Reset', width=10, | |
command=self.reset_image).pack() | |
self.button2 = Button(text='Convolution', width=10, | |
command=self.action).pack() | |
self.button_exit = Button(text='Exit', width=10, | |
command=self.root.destroy).pack() | |
self.root.mainloop() | |
def open_image(self, image_file_path): | |
image = Image.open(image_file_path) | |
image.thumbnail((self.w, self.h), Image.ANTIALIAS) | |
return image | |
def convert_to_imagetk(self, image): | |
return ImageTk.PhotoImage(image) | |
def reset_image(self): | |
image = self.open_image(self.image_file_path) | |
self.update_image(image) | |
def update_image(self, image): | |
self.imagetk_modified = self.convert_to_imagetk(image) | |
self.label2.config(image=self.imagetk_modified) | |
self.label2.pack() | |
self.root.mainloop() | |
def action(self): | |
start = time.time() | |
'''Abrimos la imagen en la cual vamos a buscar bordes | |
y la convertimos a escala de grises. | |
''' | |
f = self.open_image(self.image_file_path) | |
f = grayscale(f) | |
'''Experimente con varias matrices para observar | |
cuales daban mejor resultado y deje solo la que me | |
parecio mas exacta. | |
''' | |
# bordes verticales | |
#h = [[-1, -1, -1], [0, 0, 0], [1, 1, 1]] | |
# bordes horizontales | |
#h = [[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]] | |
# resaltar bordes | |
#h = [[0, 0, 0], [-1, 1, 0], [0, 0, 0]] | |
# detectar bordes | |
h = [[0, 1, 0], [1, -4, 1], [0, 1, 0]] | |
'''Aqui mandamos llamar el metodo convolucion, | |
del cual recibiremos la imagen con la mascara aplicada | |
y luego aplicamos unos filtros mas para hacer los | |
bordes mas notorios. | |
''' | |
image = self.convolution(h, f) | |
image = average_allneighbors(image) | |
image = black_and_white(image, 18) | |
end = time.time() | |
print end - start | |
self.update_image(image) | |
def convolution(self, h, f): | |
'''Abrimos una imagen para aplicar en ella los cambios | |
y no alterar la imagen de la cual se obtienen los datos. | |
''' | |
F = self.open_image(self.image_file_path) | |
width, height = get_image_size(F) | |
k = len(h[1]) | |
for x in range(width): | |
for y in range(height): | |
suma = 0 | |
for i in range(k): | |
z1 = i - k/2 | |
for j in range(k): | |
z2 = j - k/2 | |
try: | |
#suma += f.getpixel((x+i, y+j))[0]*h[i][j] | |
suma += f.getpixel((x+z1, y+z2))[0]*h[i][j] | |
except: | |
pass | |
suma = int(suma) | |
F.putpixel((x, y), (suma, suma, suma)) | |
return F | |
def main(): | |
if len(sys.argv) > 3: | |
image_file_path = sys.argv[1] | |
w = int(sys.argv[2]) | |
h = int(sys.argv[3]) | |
if os.path.isfile(image_file_path): | |
Edges(image_file_path, w, h) | |
else: | |
print 'Image file does not exist' | |
else: | |
print 'First parameter must be an image file name' | |
if __name__ == '__main__': | |
main() |
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
import Image | |
def get_image_size(image): | |
width, height = image.size | |
return (width, height) | |
def black_and_white(image, gray_base): | |
width, height = get_image_size(image) | |
for w in range(width): | |
for h in range(height): | |
r, g, b = image.getpixel((w, h)) | |
gray = (r+g+b)/3 | |
if gray < gray_base: | |
image.putpixel((w, h), (0, 0, 0)) | |
else: | |
image.putpixel((w, h), (255, 255, 255)) | |
return image | |
def grayscale(image): | |
width, height = get_image_size(image) | |
for w in range(width): | |
for h in range(height): | |
r, g, b = image.getpixel((w, h)) | |
gray = (r+g+b)/3 | |
image.putpixel((w, h), (gray, gray, gray)) | |
return image | |
def thresholds(image, level_min, level_max): | |
width, height = get_image_size(image) | |
for w in range(width): | |
for h in range(height): | |
r, g, b = image.getpixel((w, h)) | |
gray = (r+g+b)/3 | |
if gray < level_min: | |
image.putpixel((w, h), (0, 0, 0)) | |
if gray > level_max: | |
image.putpixel((w, h), (255, 255, 255)) | |
image.putpixel((w, h), (gray, gray, gray)) | |
return image | |
def average(image): | |
width, height = get_image_size(image) | |
image_copy = image | |
for w in range(width): | |
for h in range(height): | |
if w > 0 and w < width-1 and h > 0 and h < height-1: | |
r1, g1, b1 = image_copy.getpixel((w, h)) | |
r2, g2, b2 = image_copy.getpixel((w, h-1)) | |
r3, g3, b3 = image_copy.getpixel((w-1, h)) | |
r4, g4, b4 = image_copy.getpixel((w, h+1)) | |
r5, g5, b5 = image_copy.getpixel((w+1, h)) | |
r, g, b = ((r1+r2+r3+r4+r5)/5, | |
(g1+g2+g3+g4+g5)/5, | |
(b1+b2+b3+b4+b5)/5) | |
image.putpixel((w, h), (r, g, b)) | |
return image | |
def average_allneighbors(image): | |
width, height = get_image_size(image) | |
image_copy = image | |
for w in range(width): | |
for h in range(height): | |
if w > 0 and w < width-1 and h > 0 and h < height-1: | |
r1, g1, b1 = image_copy.getpixel((w, h)) | |
r2, g2, b2 = image_copy.getpixel((w, h-1)) | |
r3, g3, b3 = image_copy.getpixel((w, h+1)) | |
r4, g4, b4 = image_copy.getpixel((w-1, h)) | |
r5, g5, b5 = image_copy.getpixel((w-1, h-1)) | |
r6, g6, b6 = image_copy.getpixel((w-1, h+1)) | |
r7, g7, b7 = image_copy.getpixel((w+1, h)) | |
r8, g8, b8 = image_copy.getpixel((w+1, h-1)) | |
r9, g9, b9 = image_copy.getpixel((w+1, h+1)) | |
r, g, b = ((r1+r2+r3+r4+r5+r6+r7+r8+r9)/9, | |
(g1+g2+g3+g4+g5+g6+g7+g8+g9)/9, | |
(b1+b2+b3+b4+b5+b6+b7+b8+b9)/9) | |
image.putpixel((w, h), (r, g, b)) | |
return image | |
def negative(image): | |
width, height = get_image_size(image) | |
for w in range(width): | |
for h in range(height): | |
r, g, b = image.getpixel((w, h)) | |
gray = (r+g+b)/3 | |
image.putpixel((w, h), (255-r, 255-g, 255-b)) | |
return image | |
def sepia(image, sepia_intensity=25): | |
width, height = get_image_size(image) | |
for w in range(width): | |
for h in range(height): | |
r, g, b = image.getpixel((w, h)) | |
gray = (r+g+b)/3 | |
r = gray + (sepia_intensity * 2) | |
g = gray + sepia_intensity | |
b = gray - sepia_intensity | |
if r > 255: | |
r = 255 | |
if g > 255: | |
g = 255 | |
if b < 0: | |
b = 0 | |
image.putpixel((w, h), (r, g, b)) | |
return image |
Referencias:
Matrices de convolución
Matrices para detección de bordes
Muy bien. 5 pts.
ResponderEliminar