5.20. Regresión y similitud¶
Otras dos mediciones de la clase Image resumen la imagen como algo distinto de una distribución de valores de píxeles. La regresión lineal de píxeles umbralizados le da a una aplicación una línea sobre la que actuar: la entrada clásica para un robot seguidor de líneas. La medición de similitud le da a una aplicación un único número que describe cuánto se parecen dos imágenes: la entrada natural para una prueba de regresión con imagen de referencia o para un detector de cambios drásticos.
5.20.1. Regresión lineal¶
Cuando los píxeles del primer plano de una imagen forman una línea a lo largo del fotograma –la cinta de una pista que un robot está siguiendo, la línea de un horizonte, el borde de una carretera o de un pasillo–, la aplicación normalmente no quiere cada píxel individual del primer plano. Quiere la línea de mejor ajuste a través de todos ellos, parametrizada para poder decidir cómo está orientada la línea y por dónde cruza el fotograma.
get_regression() hace ese ajuste. Toma la misma forma de tupla de umbral que usan binary() y find_blobs(), identifica cada píxel que coincide con el umbral y devuelve un único resultado line que describe la línea de mejor ajuste a través de esos píxeles:
line = img.get_regression([(0, 60)])
if line:
img.draw_line((line.x1(), line.y1(),
line.x2(), line.y2()),
color=(255, 0, 0))
El ajuste es la regresión lineal de Theil-Sen: un método robusto que tolera los valores atípicos mejor que el ajuste por mínimos cuadrados, más conocido. Un pequeño puñado de píxeles lejos de la línea verdadera no sesga el resultado como lo harían con mínimos cuadrados, lo que se ajusta a la realidad de primer plano ruidoso de una salida de umbral real.
El resultado line lleva los puntos extremos recortados al rectángulo de la imagen (x1, y1, x2, y2), la longitud y la magnitud de la línea (length, magnitude) y la descripción geométrica de la línea en forma polar (theta, rho): el ángulo de la línea respecto a la horizontal y su distancia perpendicular al origen. La forma polar es lo que un bucle de control suele querer: theta le dice al robot hacia qué lado se inclina la línea, rho le dice por dónde cruza la línea la imagen, y un bucle de realimentación sobre ambos mantiene al robot centrado en la línea.
Un puñado de argumentos de palabra clave ajustan la robustez y el coste. x_stride e y_stride omiten píxeles durante el ajuste: pasos más grandes abaratan la regresión a costa de ajustar menos píxeles. area_threshold y pixels_threshold rechazan las líneas que no tienen suficientes píxeles coincidentes detrás de ellas. target_size reescala la entrada a un tamaño menor antes del ajuste: la regresión se ejecuta más rápido sobre un sustituto de 80 por 60 de la imagen sin mucha pérdida de precisión en la dirección de la línea.
Si no se pudo ajustar ninguna línea aceptable –si el umbral no coincidió con ningún píxel, o coincidió con un patrón que no parece una línea–, el método devuelve None. El código real de seguimiento de líneas protege cada llamada a get_regression() con una comprobación de None antes de acceder a los atributos de la línea.
5.20.2. Similitud de imágenes¶
Un tipo de medición distinto: en lugar de preguntar «¿qué contiene la imagen?», pregunta «¿cuánto se parecen estas dos imágenes?». La operación a la que recurrir es get_similarity(), que calcula el Índice de Similitud Estructural (SSIM) entre la imagen de origen y una imagen de referencia.
s = img.get_similarity(reference)
print(s.mean, s.stdev)
SSIM es la métrica estándar de similitud de imágenes utilizada en todo el procesamiento de imágenes porque se comporta como se comporta la intuición humana sobre la similitud: un pequeño desplazamiento o un pequeño cambio de brillo reduce ligeramente la puntuación, mientras que un cambio estructural grande (un objeto que falta, una escena distinta) la reduce drásticamente. La puntuación va de -1 a +1: +1 significa que las dos imágenes son idénticas, 0 significa que no están relacionadas, y -1 significa que son estructuralmente opuestas. Un objeto similarity devuelto expone el SSIM medio de toda la imagen, además de la desviación estándar, el mínimo y el máximo de las puntuaciones por mosaico.
Para el tipo de comparación en el que un número pequeño es mejor que uno grande –una prueba de regresión que debería informar de cero cuando «no ha cambiado nada» y aumentar a medida que se acumulan los cambios–, el indicador dssim=True devuelve la disimilitud estructural: el SSIM medio restado de 1, de modo que el valor de retorno es 0.0 para imágenes idénticas y aumenta a medida que difieren.
5.20.3. Casos de uso del SSIM¶
Las dos aplicaciones comunes:
Pruebas de regresión con imagen de referencia. Un marco de pruebas captura un fotograma de referencia en condiciones conocidas como buenas y lo almacena como imagen de referencia. Las ejecuciones de prueba posteriores capturan en las mismas condiciones y comparan con la imagen de referencia mediante SSIM. Una puntuación por encima de cierto umbral (0.95 o 0.98 según la tolerancia) es un aprobado; por debajo es un fallo. El marco de pruebas no necesita saber qué cambió: la puntuación SSIM es la señal.
Detección de cambios drásticos. Una aplicación que quiere una versión más burda de la diferencia entre fotogramas –una que ignore los pequeños cambios de brillo pero reaccione a los grandes cambios estructurales– puede usar SSIM frente a un fotograma de referencia en lugar de la diferencia píxel a píxel difference() seguida de un umbral. SSIM es menos sensible a la deriva de iluminación que la diferencia píxel a píxel, lo que lo convierte en la mejor opción cuando el objetivo es detectar que «la escena se ve sustancialmente distinta» en lugar de que «algún píxel individual cambió».
Ambas aplicaciones usan la misma llamada –img.get_similarity(reference)– y se activan en función de un umbral de la puntuación devuelta. La diferencia es simplemente si el umbral es alto (prueba de regresión, buscando una coincidencia casi idéntica) o bajo (detección de cambios, buscando cualquier cambio estructural grande).
5.20.4. La forma de transformar y comparar¶
Una sutileza útil: get_similarity() acepta los mismos parámetros x, y, x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint y transform que draw_image(). La imagen de referencia se posiciona, escala y transforma con esos parámetros antes de que se ejecute la comparación SSIM.
Eso significa que una aplicación puede preguntar «¿cuán similar es esta escena a un fotograma de referencia tras un desplazamiento / rotación / escalado conocido» sin preparar una imagen de referencia ya transformada. Es la forma económica de construir un rastreador que busca en un espacio de parámetros e informa de qué transformación de la referencia coincide mejor con el fotograma actual.