5.14. Suavizado gaussiano y bordes

Dos tareas dominan el uso de las ventanas de vecindad en la visión artificial clásica: suavizar limpiamente la variación de píxel a píxel y encontrar los bordes donde la imagen cambia bruscamente. El filtro gaussiano es la herramienta estándar para lo primero, los detectores basados en el laplaciano la herramienta estándar para lo segundo – y los dos se combinan, porque todo detector de bordes funciona mejor sobre una entrada ligeramente suavizada.

5.14.1. El filtro gaussiano

gaussian() es el primo ponderado al centro de mean(). Ambos calculan un promedio sobre la vecindad de cada píxel, pero los pesos del gaussiano no son uniformes: los píxeles más cercanos al centro de la vecindad cuentan más, los píxeles del borde de la vecindad cuentan menos, con los pesos siguiendo la familiar curva de campana que da nombre al filtro.

La ponderación en forma de campana es lo que hace que un filtro gaussiano sea más suave que un promedio de caja. El filtrado de media puede producir artefactos visibles en los bordes de los objetos – un corte abrupto en la ponderación introduce pequeños patrones de oscilación en las transiciones bruscas. Los pesos que caen suavemente del gaussiano evitan esa oscilación y producen un resultado que se parece más a lo que un «desenfoque» debería ser. El coste es más cómputo por píxel que el filtro de media, pero no de forma dramática – el coste por píxel sigue siendo mucho menor que el del filtro bilateral.

img.gaussian(1)    # 3x3 Gaussian -- a clean light blur
img.gaussian(2)    # 5x5 Gaussian -- stronger smoothing

El suavizado gaussiano es la primera etapa estándar de casi toda cadena de detección de bordes. Todos los detectores de bordes de abajo amplifican el contenido de alta frecuencia, incluyendo el ruido del sensor que el algoritmo en realidad no quiere detectar. Ejecutar un gaussiano ligero primero suprime ese ruido sin suavizar mucho los bordes reales, dejando que el detector de bordes encuentre bordes reales en lugar de moteado.

5.14.2. Máscara de enfoque (unsharp masking)

Una copia desenfocada con gaussiano de una imagen es la materia prima que la técnica de máscara de enfoque (unsharp mask) utiliza para el realce clásico. Establecer unsharp=True en el filtro lo cambia de «producir la imagen desenfocada» a «restar la imagen desenfocada del original y volver a sumar la diferencia al original» – el efecto es que los bordes de alta frecuencia se amplifican respecto a los interiores suaves.

img.gaussian(1, unsharp=True)

Los parámetros opcionales mul y add escalan la intensidad del resultado de la máscara de enfoque; los valores por defecto (mul=1.0, add=0.0) son un realce moderado que no exagera el ruido del sensor.

5.14.3. El filtro laplaciano

laplacian() ejecuta una aproximación discreta de la segunda derivada espacial de la imagen. La salida es grande donde los valores de los píxeles cambian rápidamente, cercana a cero donde son constantes o cambian linealmente. La lectura natural del resultado es una respuesta de borde: los píxeles donde la imagen cambia rápidamente se iluminan, los píxeles de los interiores suaves permanecen oscuros.

img.laplacian(1)   # 3x3 Laplacian -- edge response

Están disponibles los mismos parámetros que en gaussian. sharpen=True produce una imagen realzada (el laplaciano sumado de nuevo al original en lugar de devolverlo por sí solo). mul y add escalan la respuesta.

Un uso práctico más allá de la detección de bordes es la medición de enfoque. La respuesta del laplaciano promediada sobre una región da una medida aproximada de cuánto contenido de alta frecuencia lleva la región; en un fotograma bien enfocado ese promedio es alto, en un fotograma borroso desciende. Comparar la respuesta del laplaciano entre fotogramas es la forma económica de preguntar «¿está enfocada la lente?» sin necesitar una métrica de contraste más costosa.

5.14.4. El método find_edges

find_edges() ejecuta una cadena completa de detección de bordes en lugar de solo un filtro de respuesta de borde. Funciona sobre imágenes en escala de grises, y el resultado es una imagen binaria cuyos píxeles distintos de cero marcan las posiciones donde la entrada tiene el tipo de cambio de brillo que debería contar como un borde.

El método toma un parámetro edge_type que elige entre dos algoritmos:

EDGE_SIMPLE ejecuta un filtro paso-alto, aplica un umbral y devuelve el resultado. Rápido, pero la salida incluye todo cambio de brillo por encima del umbral, incluyendo ruido y textura que probablemente a la aplicación no le importen. Razonable para imágenes limpias y para casos donde el ruido va a limpiarse mediante una pasada morfológica posterior.

EDGE_CANNY ejecuta el detector de bordes Canny – el clásico algoritmo de varias etapas. Calcula el gradiente de brillo, suprime toda respuesta no máxima a lo largo de la dirección del gradiente (de modo que cada borde tiene un píxel de ancho), y aplica un umbral de histéresis (de modo que un borde que es fuerte en un lugar se traza incluso donde se desvanece entre medias). El resultado es un conjunto de píxeles de borde limpio, fino y conectado, del tipo que quiere todo algoritmo clásico basado en bordes.

El parámetro threshold es una tupla de dos elementos (low, high). Para EDGE_CANNY, el valor alto es el corte por encima del cual un píxel es definitivamente un borde, y el valor bajo es el corte por encima del cual un píxel es un borde solo si está conectado a uno definitivo. Para EDGE_SIMPLE, solo importa el valor alto; es el único corte por encima del cual un píxel cuenta como un borde. El valor por defecto de (100, 200) es un punto de partida que conviene ajustar para la escena concreta.

img.gaussian(1)                                   # pre-smooth
img.find_edges(image.EDGE_CANNY, threshold=(50, 100))

El detector Canny es la mejor opción para casi toda aplicación donde importan los bordes. El más rápido EDGE_SIMPLE vale la pena recordarlo para los casos donde el coste de Canny es un problema y el rechazo de ruido de su histéresis no se necesita realmente.

5.14.5. Umbral adaptativo sobre el gaussiano

Al igual que los filtros estadísticos, gaussian() acepta el par de palabras clave threshold=True / offset=N para el umbral adaptativo. El comportamiento es el mismo que con mean(): la estadística gaussiana en cada posición se convierte en el corte local, y el píxel de origen se compara con la estadística más el desplazamiento para producir un resultado binario.

La variante gaussiana suele ser la opción más limpia para el umbral adaptativo cuando la entrada está razonablemente libre de ruido. El promedio ponderado da un corte más suave que el que produce el filtro de media, con menos artefactos en las transiciones bruscas de iluminación.