5.25. Búsqueda de manchas (blobs)¶
La umbralización convirtió el fotograma capturado en una máscara binaria: cada píxel supera la prueba de umbral o no la supera. Eso responde a qué colores que le importan a la aplicación aparecen en la escena, pero no a dónde – la máscara es solo un mar de unos y ceros. El siguiente paso es la detección de manchas (blobs): recorrer la máscara, encontrar regiones contiguas de píxeles que superan la prueba y devolver cada una como un objeto con una posición, un tamaño, una orientación y las demás propiedades sobre las que una aplicación puede actuar.
find_blobs() es el método de trabajo principal de ese paso, y es el punto de entrada más común al mundo de objetos de resultado del módulo image. Seguir una pelota de color, seguir una línea pintada en el suelo, contar cuántos puntos brillantes ve un sensor térmico, decidir si un LED azul está encendido o apagado – la misma llamada cubre todos estos casos. Las entradas cambian (los umbrales, la región buscada, los filtros aplicados al resultado), pero el patrón de llamada es el mismo.
5.25.1. La llamada básica¶
find_blobs recibe una lista de umbrales y devuelve una lista de objetos de resultado de manchas (blobs):
thresholds = [(30, 100, 15, 127, 15, 127)] # LAB threshold for red
blobs = img.find_blobs(thresholds)
for b in blobs:
img.draw_rectangle(b.rect, color=(255, 0, 0))
img.draw_cross(b.cx, b.cy, color=(255, 0, 0))
Cada tupla de umbral tiene la misma forma que los umbrales pasados a binary() – seis entradas (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) para una imagen RGB565 (los límites están en LAB), dos entradas (lo, hi) para una imagen en escala de grises. Se pueden suministrar hasta 32 umbrales en una sola llamada, que es lo que hace a find_blobs() tan flexible: las balizas rojas, verdes y azules pueden seguirse simultáneamente, cada una aportando sus propias manchas (blobs) a la lista devuelta, y la propiedad code de cada mancha (blob) identifica con qué umbral coincidió.
Las llamadas a draw_rectangle() y draw_cross() anteriores anotan el fotograma capturado para la vista previa del IDE. El resultado de la mancha (blob) ya contiene b.rect (el cuadro delimitador como una tupla de 4 elementos) y b.cx / b.cy (el centroide entero), por lo que dibujar la detección de vuelta en el fotograma son dos llamadas a métodos.
5.25.2. Qué contiene el resultado¶
Cada Blob es una tupla de atributos que empaqueta todo lo que el detector midió sobre la región. Las propiedades se dividen en cuatro grupos.
El grupo de cuadro delimitador y centroide – x, y, w, h, rect, cx, cy, cxf, cyf – describe la posición de la mancha (blob). rect es la tupla de 4 elementos (x, y, w, h) que esperan los métodos de dibujo; cx y cy son el centroide en coordenadas enteras de píxel; cxf y cyf son el centroide en coordenadas de punto flotante subpíxel, útiles cuando una calibración previa necesita posiciones fraccionarias.
Los descriptores de forma – pixels, area, density, perimeter, roundness, elongation, compactness, rotation – describen el aspecto de la mancha (blob). pixels es el recuento de píxeles que superan la prueba; area es el área del cuadro delimitador alineado con los ejes (w * h); density es la relación entre ambos, que se aproxima a 1.0 para un rectángulo sólido y baja hacia 0.0 para un trazo diagonal fino. roundness y compactness puntúan ambos cuán redonda es la mancha (blob), desde distintos puntos de vista geométricos (roundness a partir de los momentos de segundo orden, compactness a partir de la relación perímetro-área); elongation es 1.0 - roundness por comodidad. rotation es la orientación del eje mayor en radianes, que es más precisa en manchas (blobs) alargadas y se vuelve ruidosa en las casi redondas (un eje ambiguo no tiene una dirección bien definida).
Los metadatos de umbral y fusión – code, count – identifican qué umbral coincidió y cuántas manchas (blobs) de origen se fusionaron en la devuelta. code es un mapa de bits de 32 bits con un bit activado por cada umbral coincidente (un único umbral da code == 1; una mancha (blob) multicolor fusionada puede tener varios bits activados); count es 1 a menos que merge=True haya combinado varias detecciones en una.
El grupo de esquinas – corners, min_corners – proporciona la geometría rotada de la mancha (blob). corners es la tupla de 4 elementos de extremos (x, y) extraídos del contorno de la mancha (blob), ordenados en sentido horario desde la parte superior izquierda; min_corners es la tupla de 4 esquinas del rectángulo rotado de área mínima que encierra la mancha (blob). El rectángulo de área mínima es el ajuste ceñido; el rect alineado con los ejes es el ajuste holgado alineado con la cuadrícula de píxeles. Ambos son útiles según si una etapa posterior necesita un cuadro orientado o uno simple.
Una mancha (blob) lleva el cuadro delimitador alineado con los ejes (rect, x, y, w, h), el centroide (cx, cy o subpíxel cxf, cyf), el rectángulo rotado de área mínima (min_corners más rotation) y las líneas opcionales del eje mayor / menor calculadas por las funciones auxiliares a nivel de módulo que se muestran a continuación.¶
5.25.3. Filtrado de la búsqueda¶
Un fotograma capturado normalmente contiene píxeles que coinciden con el umbral por razones distintas al objeto que le importa a la aplicación: reflejos especulares, objetos de fondo lejanos, píxeles de ruido de imagen que casualmente caen en el rango LAB. Los argumentos por palabra clave de find_blobs() son la primera línea de defensa.
roi restringe la búsqueda a una región del fotograma, igual que cualquier otro método del módulo image. Una aplicación que sabe que el objeto solo puede aparecer en la mitad inferior del campo de visión pasa roi=(0, h//2, w, h//2) e ignora todo lo que esté por encima; el tiempo ahorrado se reinvierte en la tasa de fotogramas.
area_threshold y pixels_threshold filtran ambos las manchas (blobs) demasiado pequeñas como para tenerlas en cuenta. area_threshold descarta las manchas (blobs) cuyo cuadro delimitador tiene menos de esa cantidad de píxeles de área (útil para filtrar ruido disperso); pixels_threshold descarta las manchas (blobs) que tienen menos de esa cantidad de píxeles que superan la prueba (útil para filtrar manchas (blobs) grandes pero dispersas, como un patrón de punteado umbralizado con uno o dos píxeles que coinciden aquí y allá). Ambos valores por defecto son 10; subirlos a cientos para un objetivo en primer plano de unos pocos centímetros descarta hasta la última mota de ruido pequeño.
x_stride y y_stride establecen el paso en píxeles que da el escáner mientras busca una mancha (blob) para empezar a trazarla. El paso no es la resolución del trazado – el trazado siempre sigue el límite real de la mancha (blob) con detalle de un solo píxel – pero controla con qué rapidez el escaneo encuentra un píxel inicial. Cuando se sabe que las manchas (blobs) son grandes (un objetivo de color del tamaño de un puño a unos 30 cm de la cámara, fácilmente de cien píxeles de ancho), x_stride=4, y_stride=4 reduce el tiempo de escaneo en dieciséis sin pérdida práctica en la detección. Cuando las manchas (blobs) son pequeñas (una baliza LED lejana, de unos pocos píxeles de ancho), los pasos deben mantenerse en 1 para no pasar por encima de ellas por completo. invert invierte la prueba de umbral: la coincidencia se convierte en no-coincidencia y la rutina devuelve manchas (blobs) de píxeles que no superan la prueba en su lugar.
threshold_cb es una función de retorno (callback) de Python invocada en cada mancha (blob) después de la umbralización pero antes de construir la lista de resultados final. La función de retorno (callback) recibe la mancha (blob) y devuelve True para conservarla o False para descartarla. Este es el lugar para aplicar filtros arbitrarios a nivel de Python sobre propiedades que los argumentos por palabra clave no exponen directamente – una densidad mínima, un rango de rotación específico, una combinación personalizada de bits de código tras la fusión. Los argumentos por palabra clave son filtros en código nativo y se ejecutan rápido; la función de retorno (callback) se ejecuta en Python y es más lenta pero ilimitada en lo que puede expresar.
5.25.4. Fusión de manchas (blobs) superpuestas¶
merge=True posprocesa la lista de resultados para combinar las manchas (blobs) cuyos rectángulos delimitadores se superponen. El uso natural es detectar un objetivo cuyo color la cámara ve como múltiples regiones umbralizadas a causa de reflejos especulares, líneas de sombra o iluminación dispar a lo largo del objeto: una sola pelota roja podría volver como tres o cuatro manchas (blobs) rojas pequeñas que, tomadas en conjunto, trazan la pelota. Con merge=True, las tres manchas (blobs) se convierten en una grande, el rect cubre la unión, el code es el OR bit a bit de los códigos de las manchas (blobs) fusionadas (de modo que una fusión multicolor identifica qué colores contribuyeron) y count informa de cuántas manchas (blobs) de origen se combinaron.
margin agranda o encoge los rectángulos delimitadores antes de la prueba de superposición. Con margin=2, las manchas (blobs) cuyos rectángulos delimitadores quedan a menos de 2 píxeles entre sí todavía se fusionan; con margin=-2, solo se fusionan las manchas (blobs) cuyos rectángulos delimitadores se superponen al menos 2 píxeles. El ajuste natural: margen positivo para manejar manchas (blobs) que el umbral fragmentó en piezas adyacentes; margen negativo para mantener separados objetos distintos muy agrupados.
merge_cb se ejecuta en cada par candidato antes de que ocurra la fusión. La función de retorno (callback) recibe las dos manchas (blobs) y devuelve True para permitir la fusión o False para impedirla. Esta es la herramienta adecuada para verificar fusiones que la regla geométrica pasa por alto – por ejemplo, negarse a fusionar dos manchas (blobs) cuyos ángulos de rotation difieren en más de un umbral, o negarse a fusionar una mancha (blob) pequeña con una mucho más grande si la pequeña es solo moteado.
5.25.5. Histogramas de proyección¶
x_hist_bins_max y y_hist_bins_max adjuntan histogramas de proyección opcionales a cada mancha (blob). Un histograma de proyección es el recuento de píxeles que superan la prueba a lo largo de un eje: el histograma del eje X suma los píxeles que superan la prueba por columna dentro del cuadro delimitador de la mancha (blob), y el histograma del eje Y suma por fila. Ambos tienen por defecto el valor cero – los histogramas no se calculan a menos que se suministre un max distinto de cero, ya que de lo contrario añadirían trabajo a cada detección.
Cuando se calculan, los histogramas proporcionan una señal 1-D económica sobre la que una aplicación puede ejecutar análisis adicionales: detectar la posición de una franja vertical dentro de la mancha (blob), encontrar el punto de ruptura de un objetivo de dos colores, contar cuántos huecos aparecen a lo largo del eje largo. Se rellenan como las propiedades x_hist_bins y y_hist_bins de cada Blob.
5.25.6. Auxiliares geométricos adicionales¶
Un puñado de medidas geométricas adicionales existen como funciones a nivel de módulo que reciben una mancha (blob) y devuelven la medición solicitada:
image.get_solidity()devuelve la solidez de la mancha (blob) – los píxeles divididos por el área de la envolvente convexa. Una región sólida y rellena está cerca de1.0; una mancha (blob) con concavidades (una herradura, una mano con los dedos extendidos) baja muy por debajo.image.get_convexity()devuelve la convexidad – el perímetro de la envolvente convexa dividido por el perímetro de la mancha (blob). Una mancha (blob) perfectamente convexa es1.0; las manchas (blobs) dentadas o melladas son más bajas.image.get_major_axis_line()yimage.get_minor_axis_line()devuelven objetosLinea lo largo de los ejes mayor y menor de la mancha (blob), derivados del rectángulo rotado de área mínima.image.get_enclosing_circle()devuelve unCircleque encierra la mancha (blob) – útil cuando una etapa posterior quiere un círculo para dibujar o con el que comparar.image.get_enclosed_ellipse()devuelve la tupla de 5 elementos(cx, cy, rx, ry, rotation)para una elipse inscrita en el rectángulo de área mínima de la mancha (blob). Los valores se introducen directamente endraw_ellipse().
5.25.7. Autoaprendizaje de un umbral¶
Un detector de manchas (blobs) es solo tan bueno como los umbrales con los que se ejecuta, y la tarea de encontrar el umbral correcto para un color objetivo es un problema en sí mismo. Dos patrones comunes reducen esa tarea.
El primero es la selección interactiva en el IDE: capturar un fotograma, arrastrar un rectángulo alrededor de un ejemplo del color objetivo y dejar que el editor de umbrales del IDE informe de los límites LAB que ve. Esos límites se colocan en el script como los umbrales de find_blobs() y el detector queda listo.
El segundo es el autoaprendizaje programático: una rutina de calibración que se ejecuta en la cámara captura un fotograma, toma un histograma de un parche conocido donde está el objetivo (get_histogram() con roi=) y lee el rango de valores del parche en el histograma con get_percentile(). El percentil 5 establece el límite inferior de cada canal y el percentil 95 su límite superior, ignorando píxeles atípicos aislados en ambos extremos. En una imagen RGB565 una sola llamada de percentil informa de los tres canales LAB a la vez, por lo que las dos llamadas producen los seis números que espera find_blobs():
h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
lo.a_value, hi.a_value,
lo.b_value, hi.b_value)