Logotipo de Zephyrnet

Detección de bordes de OpenCV en Python con cv2.Canny()

Fecha:

Introducción

La detección de bordes es algo que hacemos naturalmente, pero no es tan fácil cuando se trata de definir reglas para computadoras. Si bien se han ideado varios métodos, el método reinante fue desarrollado por John F. Canny en 1986, y se llama acertadamente el método Canny.

Es rápido, bastante robusto y funciona lo mejor que podría funcionar para el tipo de técnica que es. Al final de la guía, sabrá cómo realizar la detección de bordes en tiempo real en videos y producir algo similar a:

Detección de bordes astutos

¿Qué es el método Canny? Consta de cuatro operaciones distintas:

  • Suavizado gaussiano
  • Informática de gradientes
  • Supresión no máxima
  • Umbral de histéresis

Suavizado gaussiano se utiliza como el primer paso para "planificar" la imagen de entrada y suavizar el ruido, lo que hace que la salida final sea mucho más limpia.

Gradientes de imagen han estado en uso en aplicaciones anteriores para la detección de bordes. En particular, los filtros Sobel y Scharr se basan en gradientes de imagen. El filtro Sobel se reduce a dos núcleos (Gx y Gy), dónde Gx detecta cambios horizontales, mientras que Gy detecta cambios verticales:

G

x

=

[

-
1

0

+
1

-
2

0

+
2

-
1

0

+
1

]

G

y

=

[

-
1

-
2

-
1

0

0

0

+
1

+
2

+
1

]

Cuando los deslice sobre una imagen, cada uno de ellos "recogerá" (enfatizará) las líneas en su orientación respectiva. Los núcleos de Scharr funcionan de la misma manera, con diferentes valores:

G

x

=

[

+
3

0

-
3

+
10

0

-
10

+
3

0

-
3

]

G

y

=

[

+
3

+
10

+
3

0

0

0

-
3

-
10

-
3

]

Estos filtros, una vez convolucionados sobre la imagen, producirán mapas de características:

Crédito de la imagen: Davidwkennedy

Para estos mapas de características, puede calcular la magnitud del gradiente y orientación de gradiente – es decir, cuán intenso es el cambio (cuán probable es que algo sea un borde) y en qué dirección apunta el cambio. Dado que Gy denota el cambio vertical (gradiente Y) y Gx denota el cambio horizontal (gradiente X), puede calcular la magnitud simplemente aplicando el teorema de Pitágoras para obtener la hipotenusa del triángulo formado por la "izquierda" y direcciones "correctas":

$$
{G} ={raíz cuadrada {{{G} _{x}}^{2}+{{G} _{y}}^{2}}}
$$

Usando la magnitud y la orientación, puede producir una imagen con sus bordes resaltados:

Crédito de la imagen: Davidwkennedy

Sin embargo, ¡puedes ver cuánto ruido también se captó de la textura de los ladrillos! Los gradientes de imagen son muy sensibles al ruido. Esta es la razón por la cual los filtros de Sobel y Scharr se usaron como componente, pero no como el único enfoque en el método de Canny. El suavizado gaussiano también ayuda aquí.

Supresión no máxima

Un problema notable con el filtro Sobel es que los bordes no son muy claros. No es como si alguien tomara un lápiz y dibujara una línea para crear un lineart de la imagen. Los bordes no suelen ser tan claros en las imágenes, ya que la luz se difunde gradualmente. Sin embargo, podemos encontrar la línea común en los bordes y suprimir el resto de los píxeles a su alrededor, lo que produce una línea de separación limpia y delgada. ¡Esto se conoce como supresión no máxima! Los píxeles no máximos (más pequeños que con los que los estamos comparando en un campo local pequeño, como un núcleo de 3 × 3) se suprimen. El concepto es aplicable a más tareas que esta, pero vinculémoslo a este contexto por ahora.

Umbral de histéresis

Muchos elementos que no son bordes pueden y probablemente serán evaluados como bordes, debido a las condiciones de iluminación, los materiales en la imagen, etc. Debido a las diversas razones por las que ocurren estos errores de cálculo, es difícil hacer una evaluación automática de lo que es y es un borde. 't. Puede umbralizar gradientes e incluir solo los más fuertes, suponiendo que los bordes "reales" son más intensos que los bordes "falsos".

La umbralización funciona de la misma manera que de costumbre: si el gradiente está por debajo de un umbral inferior, elimínelo (póngalo a cero) y si está por encima de un umbral superior determinado, manténgalo. Todo lo que se encuentra entre el límite inferior y el límite superior está en la "zona gris". Si cualquier borde entre los umbrales está conectado a un borde definitivo (los que están por encima del umbral): también se consideran bordes. Si no están conectados, es probable que sean arficats de un borde mal calculado.

¡Eso es un umbral de histéresis! En efecto, ayuda a limpiar la salida final y elimina los bordes falsos, según lo que clasifique como borde falso. Para encontrar buenos valores de umbral, generalmente experimentará con diferentes límites inferior y superior para los umbrales, o empleará un método automatizado como el método de Otsu o el método Triangle.

Carguemos una imagen y la escalamos en grises (Canny, al igual que Sobel/Scharr requiere que las imágenes estén en escala de grises):

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('finger.jpg', cv2.IMREAD_GRAYSCALE)
img_blur = cv2.GaussianBlur(img, (3,3), 0)

plt.imshow(img_blur, cmap='gray')

La imagen de primer plano de un dedo servirá como un buen campo de pruebas para la detección de bordes: no es fácil distinguir una huella dactilar de la imagen, pero podemos aproximarnos a una.

Detección de bordes en imágenes con cv2.Canny()

El algoritmo de Canny se puede aplicar usando OpenCV Canny() método:

cv2.Canny(input_img, lower_bound, upper_bound)

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

Encontrar el equilibrio correcto entre el límite inferior y el límite superior puede ser complicado. Si ambos son bajos, tendrás pocos bordes. Si el límite inferior es bajo y el superior es alto, tendrá ruido. Si ambos son altos y están cerca uno del otro, tendrás pocos bordes. El lugar correcto tiene suficiente espacio entre los límites y los tiene en la escala correcta. ¡Experimento!

La imagen de entrada se desenfocará con el método Canny, pero a menudo, se beneficiará de desenfocarla. antes también entra. El método aplica un desenfoque gaussiano de 5 × 5 a la entrada antes de pasar por el resto de las operaciones, pero incluso con este desenfoque, aún puede filtrarse algo de ruido, por lo que hemos desenfocado la imagen antes de introducirla en el algoritmo:


edge = cv2.Canny(img_blur, 20, 30)

fig, ax = plt.subplots(1, 2, figsize=(18, 6), dpi=150)
ax[0].imshow(img, cmap='gray')
ax[1].imshow(edge, cmap='gray')

Esto resulta en:

Los valores de 20 y 30 aquí no son arbitrarios: probé el método en varios parámetros y elegí un conjunto que parecía producir un resultado decente. ¿Podemos intentar automatizar esto?

Umbralización automatizada para cv2.Canny()?

¿Puede encontrar un conjunto óptimo de valores de umbral? Sí, pero no siempre funciona. Puede hacer su propio cálculo por un buen valor y luego ajustar el rango con un sigma alrededor de ese umbral:

lower_bound = (1-sigma)*threshold
upper_bound = (1+sigma)*threshold

Cuándo sigma, es decir, 0.33 – los límites serán 0.66*threshold y 1.33*threshold, permitiendo un rango de ~1/3 a su alrededor. Aunque, encontrar la threshold es lo que es más difícil. OpenCV nos proporciona el método de Otsu (funciona muy bien para imágenes bimodales) y el método Triangle. Probemos ambos, además de tomar una mediana simple de los valores de píxel como tercera opción:

otsu_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_OTSU)
triangle_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_TRIANGLE)
manual_thresh = np.median(img_blur)

def get_range(threshold, sigma=0.33):
    return (1-sigma) * threshold, (1+sigma) * threshold

otsu_thresh = get_range(otsu_thresh)
triangle_thresh = get_range(triangle_thresh)
manual_thresh = get_range(manual_thresh)

print(f"Otsu's Threshold: {otsu_thresh} nTriangle Threshold: {triangle_thresh} nManual Threshold: {manual_thresh}")

Esto resulta en:

Otsu's Threshold: (70.35, 139.65) 
Triangle Threshold: (17.419999999999998, 34.58) 
Manual Threshold: (105.18999999999998, 208.81)

¡Estos son bastante diferentes! A partir de los valores que hemos visto antes, podemos anticipar que el método Triángulo funciona mejor aquí. El umbral manual no está muy informado, ya que solo toma el valor de píxel mediano y termina teniendo un umbral base alto que se multiplica aún más en un amplio rango para esta imagen. El método de Otsu sufre menos por esto, pero sufre de todos modos.

Si ejecutamos el Canny() método con estos rangos de umbral:

edge_otsu = cv2.Canny(img_blur, *otsu_thresh)
edge_triangle = cv2.Canny(img_blur, *triangle_thresh)
edge_manual = cv2.Canny(img_blur, *manual_thresh)

fig, ax = plt.subplots(1, 3, figsize=(18, 6), dpi=150)
ax[0].imshow(edge_otsu, cmap='gray')
ax[1].imshow(edge_triangle, cmap='gray')
ax[2].imshow(edge_manual, cmap='gray')

Nota: La función espera varios argumentos y nuestros umbrales son una sola tupla. Podemos desestructurar la tupla en múltiples argumentos prefijándola con *. Esto también funciona en listas y conjuntos, y es una excelente manera de proporcionar múltiples argumentos después de obtenerlos mediante programación.

Esto resulta en:

¡El método Triángulo funcionó bastante bien aquí! Esto no es garantía de que funcionará bien en otros casos también.

Detección de bordes en tiempo real en videos con cv2.Canny()

¡Finalmente, apliquemos la detección de bordes Canny a un video en tiempo real! Mostraremos el video que se está procesando (cada cuadro como se hace) usando cv2.imshow() que muestra una ventana con el marco que nos gustaría mostrar. Sin embargo, también guardaremos el video en un archivo MP4 que luego se puede inspeccionar y compartir.

Para cargar un video usando OpenCV, usamos el VideoCapture() método. si pasamos 0 – ¡Grabará desde la cámara web actual, por lo que también puede ejecutar el código en su cámara web! Si pasa un nombre de archivo, cargará el archivo:

def edge_detection_video(filename):
    cap = cv2.VideoCapture(filename)
    
    fourcc = cv2.VideoWriter_fourcc(*'MP4V')
    out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (int(cap.get(3)), int(cap.get(4))), isColor=False)
    
    while cap.isOpened():
        (ret, frame) = cap.read()
        if ret == True:
            frame = cv2.GaussianBlur(frame, (3, 3), 0)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            edge = cv2.Canny(frame, 50, 100)
            out.write(edge)
            cv2.imshow('Edge detection', edge)
        else:
            break

        if cv2.waitKey(10) & 0xFF == ord('q'):
            break

    cap.release()
    out.release()
    cv2.destroyAllWindows()

edge_detection_video('secret_video.mp4')

El VideoWriter acepta varios parámetros: el nombre del archivo de salida, el FourCC (cuatro códigos de códec, que indican el códec utilizado para codificar el video), la velocidad de fotogramas y la resolución como una tupla. Para no adivinar o cambiar el tamaño del video, hemos utilizado el ancho y la altura del video original, obtenidos a través de la VideoCapture instancia que contiene datos sobre el video en sí, como el ancho, la altura, el número total de fotogramas, etc.

Mientras se abre la captura, intentamos leer el siguiente cuadro con cap.read(), que devuelve un código de resultado y el siguiente cuadro. El código de resultado es True or False, que denota la presencia del siguiente cuadro o la ausencia del mismo. Solo cuando haya un marco, intentaremos procesarlo más, de lo contrario, romperemos el ciclo. Para cada cuadro válido, lo ejecutamos a través de un desenfoque gaussiano, lo convertimos a escala de grises, ejecutamos cv2.Canny() en él y escríbalo usando el VideoWriter al disco, y mostrar usando cv2.imshow() para una vista en vivo.

Finalmente, lanzamos la captura y el escritor de video, ya que ambos trabajan con archivos en el disco y destruyen todas las ventanas existentes.

Cuando ejecuta el método con un secret_video.mp4 entrada: verá una ventana emergente y una vez que haya terminado, un archivo en su directorio de trabajo:

Conclusión

En esta guía, hemos analizado cómo funciona la detección de bordes de Canny y sus partes constituyentes: suavizado gaussiano, filtros Sobel y gradientes de imagen, supresión no máxima y umbral de histéresis. Finalmente, hemos explorado métodos para la búsqueda automatizada de rango de umbral para la detección de bordes Canny con cv2.Canny()y empleó la técnica en un video, proporcionando detección de bordes en tiempo real y guardando los resultados en un archivo de video.

Yendo más lejos: aprendizaje profundo práctico para la visión por computadora

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos revisar nuestro Curso: “Aprendizaje Profundo Práctico para Visión por Computador con Python”.

¿Otro curso de visión artificial?

No haremos clasificación de dígitos MNIST o moda MNIST. Cumplieron su parte hace mucho tiempo. Demasiados recursos de aprendizaje se centran en conjuntos de datos básicos y arquitecturas básicas antes de permitir que las arquitecturas avanzadas de caja negra carguen con la carga del rendimiento.

queremos centrarnos en desmitificación, sentido práctico, comprensión, intuición y proyectos reales. Querer aprender cómo ¿Tu puedes hacer la diferencia? Lo llevaremos en un viaje desde la forma en que nuestros cerebros procesan imágenes hasta escribir un clasificador de aprendizaje profundo de grado de investigación para el cáncer de mama y redes de aprendizaje profundo que "alucinan", enseñándole los principios y la teoría a través del trabajo práctico, equipándolo con el conocimientos y herramientas para convertirse en un experto en la aplicación del aprendizaje profundo para resolver la visión artificial.

¿Qué hay adentro?

  • Los primeros principios de la visión y cómo se puede enseñar a las computadoras a “ver”
  • Diferentes tareas y aplicaciones de la visión artificial
  • Las herramientas del oficio que facilitarán tu trabajo
  • Encontrar, crear y utilizar conjuntos de datos para la visión por computadora
  • La teoría y aplicación de las Redes Neuronales Convolucionales
  • Manejo de cambio de dominio, co-ocurrencia y otros sesgos en conjuntos de datos
  • Transfiera el aprendizaje y utilice el tiempo de capacitación y los recursos computacionales de otros para su beneficio
  • Construyendo y entrenando un clasificador de cáncer de mama de última generación
  • Cómo aplicar una buena dosis de escepticismo a las ideas principales y comprender las implicaciones de las técnicas ampliamente adoptadas
  • Visualización del "espacio conceptual" de ConvNet usando t-SNE y PCA
  • Casos prácticos de cómo las empresas utilizan técnicas de visión artificial para lograr mejores resultados
  • Evaluación adecuada del modelo, visualización del espacio latente e identificación de la atención del modelo.
  • Realizar investigaciones de dominio, procesar sus propios conjuntos de datos y establecer pruebas modelo
  • Arquitecturas de vanguardia, la progresión de las ideas, qué las hace únicas y cómo implementarlas
  • KerasCV: una biblioteca WIP para crear canalizaciones y modelos de última generación
  • Cómo analizar y leer documentos e implementarlos usted mismo
  • Selección de modelos en función de su aplicación
  • Creación de una canalización de aprendizaje automático de extremo a extremo
  • Panorama e intuición sobre la detección de objetos con Faster R-CNN, RetinaNets, SSD y YOLO
  • Instancia y segmentación semántica
  • Reconocimiento de objetos en tiempo real con YOLOv5
  • Capacitación de detectores de objetos YOLOv5
  • Trabajando con Transformers usando KerasNLP (biblioteca WIP de fuerza industrial)
  • Integrando Transformers con ConvNets para generar subtítulos de imágenes
  • sueño profundo
  • Optimización del modelo de Deep Learning para visión artificial
punto_img

Información más reciente

café vc

café vc

punto_img