5.18. Histogramas y estadísticas¶
Junto a las operaciones que modifican los píxeles de una imagen, la clase Image incluye una familia de métodos que los miden: resumen la distribución de los valores de los píxeles, devuelven el brillo medio y mediano, encuentran el corte óptimo entre los píxeles oscuros y los claros, e informan de la dispersión de los canales de color. Las mediciones alimentan las aplicaciones de dos maneras: como entradas para el código que decide qué umbral usar, qué ganancia establecer y cómo se ve el perfil tonal de la escena; y como señales de diagnóstico –»¿hay suficiente luz en la escena?»– sobre las que una aplicación puede actuar sin tomar ninguna decisión sobre un píxel concreto.
El punto de partida de casi todas las mediciones es el histograma.
5.18.1. El histograma¶
Un histograma de una imagen es un recuento de cuántos píxeles tienen cada posible valor de brillo. Para una imagen en escala de grises, se trata de una lista de recuentos indexada por los valores de 0 a 255. Para una imagen en color, son tres listas de ese tipo: una por canal.
get_histogram() calcula uno:
h = img.get_histogram()
El objeto devuelto es un resultado histogram que expone las listas de intervalos por canal y algunas consultas de alto nivel sobre ellas. Los recuentos de los intervalos están normalizados de modo que sumen 1.0: el histograma describe el perfil de la distribución en lugar del recuento absoluto de píxeles, lo que hace que las mediciones sean comparables entre imágenes de distintos tamaños.
Para las imágenes en escala de grises, el histograma tiene un canal de intervalos, disponible como h.bins() (o de forma equivalente h[0]). Para las imágenes RGB565, el histograma se calcula en el espacio de color LAB presentado en la página sobre umbralización binaria, con tres canales de intervalos disponibles como h.l_bins(), h.a_bins(), h.b_bins() (o h[0], h[1], h[2]). LAB es la misma elección que utilizan los métodos de umbral y seguimiento; los histogramas coinciden con los umbrales sobre en qué espacio se está midiendo el color.
5.18.2. Los intervalos y el número de intervalos¶
El histograma predeterminado tiene un intervalo por cada posible valor de píxel: 256 intervalos para un canal de 8 bits. A veces esa resolución es más fina de lo que la aplicación necesita. Un clasificador que solo se interesa por el perfil aproximado de la distribución podría aprovechar mejor un número menor de intervalos –32 o incluso 8 intervalos–, lo que se ejecuta más rápido y produce un resultado más limpio frente al ruido. La palabra clave bins (y las palabras por canal l_bins, a_bins, b_bins para color) establece el número:
h = img.get_histogram(bins=32)
La delimitación por ROI y por umbral funciona igual que en todos los demás métodos de medición. Pasa un roi para confinar el histograma a un rectángulo de píxeles; pasa una lista thresholds para incluir solo los píxeles que coincidan con esos rangos. La forma con umbral es la que convierte «calcula el histograma únicamente de los píxeles coincidentes» en una operación de una sola llamada, un patrón habitual cuando una aplicación quiere caracterizar la textura de una región ya detectada sin tener que recorrer los píxeles ella misma.
Un histograma en escala de grises con tres mediciones de resumen superpuestas: el umbral de Otsu (el corte que mejor separa los grupos oscuro y claro), la media y la mediana. Cada medición dice algo distinto sobre la misma distribución.¶
5.18.3. Estadísticas¶
Un histograma es una descripción de la frecuencia de cada valor; las estadísticas son los resúmenes numéricos que se derivan de él. El objeto statistics devuelto por get_statistics() incluye el conjunto estándar:
mean– la media aritmética de los valores de los píxeles.median– el valor por debajo del cual se encuentra la mitad de los píxeles.mode– el valor individual más común.stdev– la desviación estándar, una medida de la dispersión en torno a la media.minymax– los valores de píxel más claro y más oscuro presentes.lqyuq– los cortes del cuartil inferior y superior.
Para una imagen RGB565, las formas por canal (l_mean, a_median, b_mode, etc.) ofrecen las mismas mediciones canal por canal.
La mayoría de esos números aparecen en contextos específicos. mean y stdev juntos dan una estimación del ruido: una escena que debería ser uniforme tiene una stdev pequeña, mientras que un sensor ruidoso le da a esa misma escena una stdev mayor. min y max dan el contraste de la imagen: cuanto más cercanos estén, más plana es la escena; cuanto más separados, más rango dinámico tiene el algoritmo con el que trabajar. median es el centro robusto cuando la distribución tiene valores atípicos (unos pocos píxeles muy brillantes no arrastran la mediana como arrastran la media). mode es el valor individual más común, útil para encontrar el nivel de fondo de una imagen cuyo fondo cubre la mayor parte de los píxeles.
get_statistics() ejecuta el cálculo del histograma internamente y luego lo resume; pasar los mismos argumentos thresholds y roi que un histograma calculado previamente produce las estadísticas para el mismo conjunto de píxeles.
5.18.4. Percentiles y consultas de CDF¶
El objeto histogram expone un método get_percentile() que convierte una fracción en un valor de píxel: el valor por debajo del cual se encuentra la fracción solicitada de píxeles. h.get_percentile(0.5) es la mediana; h.get_percentile(0.05) y h.get_percentile(0.95) juntos dan un mínimo/máximo robusto que ignora como valores atípicos el 5% inferior y el 5% superior.
Esa es la forma que utiliza una aplicación cuando quiere caracterizar el rango de los valores de los píxeles sin dejar que un puñado de píxeles claros u oscuros aislados sesgue la respuesta. El mínimo/máximo robusto a partir de los percentiles 5 y 95 es también la entrada natural para una pasada de estiramiento de contraste: el remapeo por píxel que cubre Correcciones tonales.
5.18.5. El método de Otsu¶
Los histogramas responden a otra pregunta que merece la pena destacar por sí sola: dada una imagen cuyos píxeles se dividen en un grupo «oscuro» y uno «claro», ¿cuál es el corte entre ellos? La página sobre umbralización ya nombró el mecanismo por su resultado –un único umbral global que la aplicación puede pasar a binary()–, pero aplazó el cómo. El cómo es el método de Otsu, y reside en el histograma.
La intuición: una imagen con un primer plano y un fondo claros tiene dos grupos en su histograma de brillo, con un valle entre ambos. El lugar correcto para umbralizar es el fondo del valle: el valor donde los dos grupos están mejor separados. El método de Otsu busca cada posible corte y elige aquel en el que las varianzas dentro de cada grupo son mínimas (lo que equivale a decir que la varianza entre grupos es máxima), y el resultado es la división binaria óptima para la distribución de esa imagen en particular.
El objeto histogram expone Otsu a través de get_threshold:
h = img.get_histogram()
t = h.get_threshold()
El objeto threshold devuelto tiene los atributos value (para escala de grises) o l_value / a_value / b_value (para color) que llevan el corte elegido. Pasar el resultado directamente de vuelta a binary() proporciona un umbral global autoajustado cuyo corte lo elige la propia imagen:
img.binary([(t.value, 255)])
Ese patrón no resuelve el problema de iluminación desigual que resuelve el umbral adaptativo basado en filtros; lo que resuelve es la pregunta «¿en qué valor debo cortar?» cuando la umbralización global ya es el enfoque correcto. Para una escena cuya distinción entre primer plano y fondo está bien definida, el valor que elige Otsu suele estar a unas pocas unidades de lo que un humano elegiría a ojo.
5.18.6. Cálculo sobre una imagen de diferencia¶
Un detalle útil sobre get_histogram() y get_statistics(): ambos aceptan una palabra clave difference que toma otra Image y calcula el histograma (o las estadísticas) de la diferencia píxel a píxel entre la imagen de origen y esa imagen, sin asignar una imagen de diferencia aparte. Esa es la forma económica de preguntar «¿cuánto ha cambiado la escena desde el fotograma de referencia?» sin pagar el coste de una llamada explícita a difference() para producir una imagen cuyo único propósito es ser medida. Para un script de detección de movimiento que se ejecuta de forma continua, el ahorro se acumula.