Universidad Internacional del Ecuador 13 de enero de 2026
[Link]
Detección de Bordes: Trabajo de Visión por Computador
Byron Velasco Cueva, Alex Perez Vilañez, Paúl Cortés Barahona
Maestrı́a en Inteligencia Artificial Aplicada, Universidad Internacional del Ecuador
byvelascocu@[Link], alperezvi@[Link], pacortesba@[Link]
Palabras Clave: Visión por Computador, Canny Edge Detection, OpenCV, Python, Procesamiento de Imágenes
Resumen
El procesamiento digital de imágenes es una rama fundamental de la visión por computador que permite extraer información
relevante a partir de imágenes digitales. Una de las tareas más importantes dentro de este campo es la detección de bordes, ya
que estos representan cambios bruscos de intensidad que suelen corresponder a los lı́mites de los objetos presentes en la escena.
En este proyecto se implementa el algoritmo Canny Edge Detection, uno de los métodos más robustos y utilizados, debido a su
buena respuesta ante el ruido y su capacidad para producir contornos bien definidos. La implementación utiliza OpenCV, NumPy y
Matplotlib. Además, se incorpora el uso de una semilla aleatoria (seed) para generar variaciones controladas en los parámetros y
una funcionalidad para detección en tiempo real vı́a webcam.
1. Introducción 3. Metodologı́a y Flujo del Programa
En el ámbito de la Inteligencia Artificial, la capacidad El software desarrollado sigue un flujo lineal de proce-
de interpretar el mundo visual es clave. La detección de samiento, enriquecido con interactividad en tiempo real:
bordes actúa como un paso de preprocesamiento vital para
tareas más complejas como la segmentación de objetos o 1. Entrada: Carga de imagen estática o captura de
el reconocimiento facial. frame de webcam.
Este trabajo presenta una implementación práctica del
algoritmo de Canny [1]. Se explora no solo la aplicación 2. Preprocesamiento: Conversión de la imagen a
estática sobre imágenes, sino también la dinámica median- escala de grises ([Link]), paso necesario ya
te el uso de video en tiempo real, permitiendo observar que Canny trabaja sobre intensidad de luz, no sobre
cómo la variación de los umbrales (thresholds) afecta la color.
detección de caracterı́sticas en entornos no controlados.
3. Configuración Dinámica: Se utiliza una semilla
(‘seed‘) para aleatorizar ligeramente los umbrales
2. Tecnologı́as Utilizadas bajo y alto del detector Canny, ası́ como para añadir
ruido gaussiano artificial si se desea probar la robus-
Para el desarrollo de esta práctica se han utilizado las
tez.
siguientes herramientas:
4. Procesamiento: Aplicación de [Link] con los
Python [2]: Lenguaje principal de desarrollo debido
parámetros calculados.
a su versatilidad en ciencia de datos.
OpenCV (cv2) [3]: Biblioteca estándar de la in- 5. Visualización: Despliegue de la imagen original y
dustria para carga de imágenes, conversión a escala la imagen procesada.
de grises y ejecución del algoritmo Canny.
NumPy [4]: Utilizado para la manipulación eficien- 4. Pruebas del Código
te de matrices y la generación de ruido aleatorio
controlado. 4.1 Prueba con imagen local
Matplotlib [5]: (Opcional en el código) Para visua- El sistema permite cargar imágenes desde el disco. En
lización de datos y gráficos. la Figura 1 se observa el resultado de aplicar el filtro a una
imagen de prueba (Perry el ornitorrinco). Se aprecia cómo
Webcam: Hardware utilizado para la fase de prue- el algoritmo destaca los contornos principales ignorando
bas en tiempo real. gran parte de la textura interna plana.
1
Referencias Bibliográficas
[1] John Canny. “A computational approach to edge de-
tection”. En: IEEE Transactions on Pattern Analysis
and Machine Intelligence 6 (1986), págs. 679-698.
[2] Guido Van Rossum y Fred L. Drake. Python 3 Refe-
rence Manual. Scotts Valley, CA: CreateSpace, 2009.
[3] Gary Bradski. “The OpenCV Library”. En: Dr. Dobb’s
Journal of Software Tools 25.11 (2000), págs. 120-123.
Figura 1. Comparativa: Imagen Original vs. Detección de Bordes Canny [4] Charles R. Harris et al. “Array programming with
(Imagen Local).
NumPy”. En: Nature 585.7825 (2020), págs. 357-362.
[5] John D. Hunter. “Matplotlib: A 2D graphics environ-
4.2 Prueba en tiempo real (Webcam) ment”. En: Computing in Science & Engineering 9.3
(2007), págs. 90-95.
Como funcionalidad extra (”Bonus”), se implementó
un bucle de captura de video. Esto permite ajustar la
semilla en tiempo real y ver el efecto inmediato en los A. Anexo: Código Fuente
bordes detectados.
A continuación, se presenta el código completo desa-
rrollado en Python.
1 import argparse
2 import time
3 from typing import Optional , Tuple
4 import cv2
5 import numpy as np
6
7 def ca nn y _w it h_ s ee d (
8 gray_img : np . ndarray ,
Figura 2. Detección de bordes en tiempo real utilizando la webcam. 9 seed : int = 0 ,
10 base_low : int = 80 ,
11 base_high : int = 160 ,
12 add_noise : bool = True ,
13 noise_sigma : float = 6.0 ,
5. Resultados y Conclusiones 14 ) -> Tuple [ np . ndarray , int , int ]:
15 """
Los experimentos realizados arrojan las siguientes con- 16 Aplica Canny modificando los umbrales segun
clusiones: una semilla
17 y opcionalmente agrega ruido .
18 """
Eficacia de Canny: El algoritmo demostró ser muy
19 rng = np . random . default_rng ( seed )
eficaz para limpiar ruido y mantener solo los bordes 20
estructurales importantes, siempre que los umbrales 21 # Variacion aleatoria de los umbrales
se ajusten correctamente. 22 low = int ( np . clip ( base_low + rng . integers
( -40 , 41) , 0 , 255) )
Importancia de los Umbrales: Un umbral bajo 23 high = int ( np . clip ( base_high + rng . integers
( -40 , 41) , 0 , 255) )
muy pequeño introduce demasiado ruido (falsos bor- 24
des), mientras que un umbral alto excesivo pierde 25 if high <= low :
detalles importantes del objeto. 26 high = min (255 , low + 50)
27
Interactividad: La implementación de la webcam 28 img_proc = gray_img
29
permite entender intuitivamente cómo la iluminación
30 # Agregar ruido gaussiano si se solicita
y el movimiento afectan la detección de bordes, algo 31 if add_noise :
difı́cil de apreciar solo con imágenes estáticas. 32 noise = rng . normal (0.0 , noise_sigma ,
size = gray_img . shape ) . astype ( np . float32 )
Reproducibilidad: El uso de la semilla aleatoria 33 img_proc = np . clip ( gray_img . astype ( np .
(‘seed‘) asegura que, aunque introduzcamos variacio- float32 ) + noise , 0 , 255) . astype ( np . uint8 )
34
nes de prueba, estas sean reproducibles, un principio 35 edges = cv2 . Canny ( img_proc , threshold1 = low ,
clave en la experimentación cientı́fica. threshold2 = high )
36 return edges , low , high
37
Disponibilidad de Datos El código completo utilizado 38 def process_image ( image_path : str , seed : int ,
para este reporte se encuentra disponible en la sección de base_low : int , base_high : int , add_noise :
Anexos. bool ) -> None :
2
39 img_bgr = cv2 . imread ( image_path ) 94 cap . release ()
40 if img_bgr is None : 95 cv2 . d e s t r o y A l l W i n d o w s ()
41 raise F i l e N o t F o u n d E r r o r ( f " No se puede 96
leer : { image_path } " ) 97 def main () :
42 98 parser = argparse . Argumen tParser ()
43 gray = cv2 . cvtColor ( img_bgr , cv2 . 99 parser . add_argument ( " -- image " , type = str ,
COLO R_BGR2GR AY ) default = None )
44 edges , low , high = ca n ny _w i th _s ee d ( 100 parser . add_argument ( " -- webcam " , action = "
45 gray , seed = seed , base_low = base_low , store_true " )
base_high = base_high , add_noise = add_noise 101 parser . add_argument ( " -- cam - index " , type = int ,
46 ) default =0)
47 102 parser . add_argument ( " -- seed " , type = int ,
48 cv2 . imshow ( " Original " , img_bgr ) default =0)
49 cv2 . imshow ( f " Canny ( seed ={ seed } , t1 ={ low } , 103 parser . add_argument ( " -- base - low " , type = int ,
t2 ={ high }) " , edges ) default =80)
50 print ( f " [ Imagen ] seed ={ seed } | t1 ={ low } | t2 104 parser . add_argument ( " -- base - high " , type = int ,
={ high } " ) default =160)
51 cv2 . waitKey (0) 105 parser . add_argument ( " --no - noise " , action = "
52 cv2 . d e s t r o y A l l W i n d o w s () store_true " )
53 106 parser . add_argument ( " -- width " , type = int ,
54 def pr ocess_we bcam ( default = None )
55 cam_index : int , seed : int , base_low : int , 107 parser . add_argument ( " -- height " , type = int ,
base_high : int , default = None )
56 add_noise : bool , width : Optional [ int ] , 108 parser . add_argument ( " -- fps " , type = float ,
height : Optional [ int ] , default = None )
57 fps_limit : Optional [ float ] 109
58 ) -> None : 110 args = parser . parse_args ()
59 cap = cv2 . VideoCapture ( cam_index , cv2 . 111 add_noise = not args . no_noise
CAP_DSHOW ) 112
60 if not cap . isOpened () : 113 if args . image :
61 raise RuntimeError ( f " Error al abrir 114 process_image ( args . image , args . seed ,
webcam { cam_index }. " ) args . base_low , args . base_high , add_noise )
62 115 if args . webcam :
63 if width : cap . set ( cv2 . CAP_PROP_FRAME_WIDTH , 116 pr ocess_we bcam ( args . cam_index , args . seed
float ( width ) ) , args . base_low , args . base_high , add_noise ,
64 if height : cap . set ( cv2 . CAP_PROP_FRAME_HEIGHT args . width , args . height , args . fps )
, float ( height ) ) 117
65 118 if __name__ == " __main__ " :
66 print ( " Controles : q = salir | n = seed +1 | p = 119 main ()
seed -1 | r = reset " )
67 last_time = time . time () Listing 1. Implementación completa de Canny Edge Detection
68
69 while True :
70 ret , frame = cap . read ()
71 if not ret : break
72
73 gray = cv2 . cvtColor ( frame , cv2 .
COLO R_BGR2GR AY )
74 edges , low , high = c an ny _w i th _s ee d (
75 gray , seed = seed , base_low = base_low ,
base_high = base_high , add_noise = add_noise
76 )
77
78 cv2 . imshow ( " Webcam Original " , frame )
79 cv2 . imshow ( f " Webcam Canny ( seed ={ seed }) "
, edges )
80
81 # Control de FPS
82 if fps_limit and fps_limit > 0:
83 dt = time . time () - last_time
84 target = 1.0 / fps_limit
85 if dt < target : time . sleep ( target -
dt )
86 last_time = time . time ()
87
88 key = cv2 . waitKey (1) & 0 xFF
89 if key == ord ( " q " ) : break
90 elif key == ord ( " n " ) : seed += 1
91 elif key == ord ( " p " ) : seed -= 1
92 elif key == ord ( " r " ) : seed = 0
93