Después de mi pregunta anterior sobre cómo encontrar los dedos de los pies dentro de cada pata , comencé a cargar otras medidas para ver cómo se mantendría. Desafortunadamente, me encontré rápidamente con un problema con uno de los pasos anteriores: reconocer las patas.
Verá, mi prueba de concepto básicamente tomó la presión máxima de cada sensor a lo largo del tiempo y comenzaría a buscar la suma de cada fila, ¡hasta que encuentre eso! = 0.0. Luego hace lo mismo para las columnas y tan pronto como encuentra más de 2 filas con eso son cero nuevamente. Almacena los valores mínimos y máximos de fila y columna en algún índice.
Como puede ver en la figura, esto funciona bastante bien en la mayoría de los casos. Sin embargo, hay muchas desventajas en este enfoque (además de ser muy primitivo):
Los humanos pueden tener 'pies huecos', lo que significa que hay varias filas vacías dentro de la propia huella. Como temía que esto también pudiera suceder con perros (grandes), esperé al menos 2 o 3 filas vacías antes de cortar la pata.
Esto crea un problema si otro contacto se hizo en una columna diferente antes de que alcance varias filas vacías, expandiendo así el área. Me imagino que podría comparar las columnas y ver si exceden un cierto valor, deben ser patas separadas.
El problema empeora cuando el perro es muy pequeño o camina a un ritmo más alto. ¡Lo que sucede es que los dedos de la pata delantera todavía están haciendo contacto, mientras que los dedos de la pata trasera comienzan a hacer contacto dentro de la misma área que la pata delantera!
Con mi script simple, no será capaz de dividir estos dos, porque tendría que determinar qué cuadros de esa área pertenecen a cada pata, mientras que actualmente solo tendría que mirar los valores máximos en todos los cuadros.
Ejemplos de dónde empieza a salir mal:
Así que ahora estoy buscando una mejor manera de reconocer y separar las patas (¡después de lo cual abordaré el problema de decidir qué pata es!).
Actualizar:
He estado jugando para implementar la respuesta de Joe (¡increíble!), Pero tengo dificultades para extraer los datos reales de la pata de mis archivos.
Coded_paws me muestra todas las patas diferentes, cuando se aplica a la imagen de presión máxima (ver arriba). Sin embargo, la solución pasa por cada cuadro (para separar las patas superpuestas) y establece los cuatro atributos de Rectángulo, como las coordenadas o la altura / anchura.
No puedo entender cómo tomar estos atributos y almacenarlos en alguna variable que pueda aplicar a los datos de medición. Como necesito saber para cada pata, cuál es su ubicación durante qué cuadros y acoplar esto a qué pata es (frontal / posterior, izquierda / derecha).
Entonces, ¿cómo puedo usar los atributos Rectángulos para extraer estos valores para cada pata?
Tengo las medidas que utilicé en la configuración de preguntas en mi carpeta pública de Dropbox ( ejemplo 1 , ejemplo 2 , ejemplo 3 ). Para cualquier persona interesada, también configuré un blog para mantenerlo actualizado :-)
fuente
Respuestas:
Si solo quiere regiones (semi) contiguas, ya hay una implementación fácil en Python: el módulo ndimage.morphology de SciPy . Esta es una operación de morfología de imagen bastante común .
Básicamente, tienes 5 pasos:
Desenfoque un poco los datos de entrada para asegurarse de que las patas tengan una huella continua. (Sería más eficiente usar un kernel más grande (el
structure
kwarg para las diversasscipy.ndimage.morphology
funciones) pero esto no funciona correctamente por alguna razón ...)Umbralice la matriz para que tenga una matriz booleana de lugares donde la presión supere algún valor umbral (es decir
thresh = data > value
)Rellene los agujeros internos, de modo que tenga regiones más limpias (
filled = sp.ndimage.morphology.binary_fill_holes(thresh)
)Encuentra las regiones contiguas separadas (
coded_paws, num_paws = sp.ndimage.label(filled)
). Esto devuelve una matriz con las regiones codificadas por número (cada región es un área contigua de un número entero único (1 hasta el número de patas) con ceros en todas partes).Aislar las regiones contiguas con
data_slices = sp.ndimage.find_objects(coded_paws)
. Esto devuelve una lista de tuplas deslice
objetos, para que pueda obtener la región de los datos para cada pata[data[x] for x in data_slices]
. En su lugar, dibujaremos un rectángulo basado en estos cortes, lo que requiere un poco más de trabajo.Las dos animaciones a continuación muestran los datos de ejemplo de "Patas superpuestas" y "Patas agrupadas". Este método parece estar funcionando perfectamente. (Y para lo que sea que valga, esto funciona mucho más suavemente que las imágenes GIF a continuación en mi máquina, por lo que el algoritmo de detección de la pata es bastante rápido ...)
Aquí hay un ejemplo completo (ahora con explicaciones mucho más detalladas). La gran mayoría de esto es leer la entrada y hacer una animación. La detección real de la pata es de solo 5 líneas de código.
Actualización: en cuanto a identificar qué pata está en contacto con el sensor en qué momentos, la solución más simple es simplemente hacer el mismo análisis, pero usar todos los datos a la vez. (es decir, apilar la entrada en una matriz 3D y trabajar con ella, en lugar de los marcos de tiempo individuales). Debido a que las funciones ndimage de SciPy están destinadas a trabajar con matrices n-dimensionales, no tenemos que modificar la función original de búsqueda de patas en absoluto.
fuente
convert *.png output.gif
. Ciertamente, imagemagick hizo que mi máquina se pusiera de rodillas antes, aunque funcionó bien para este ejemplo. En el pasado, he usado este script: svn.effbot.python-hosting.com/pil/Scripts/gifmaker.py para escribir directamente un gif animado desde python sin guardar los cuadros individuales. ¡Espero que ayude! Publicaré un ejemplo en la pregunta que @unutbu mencionó.bbox_inches='tight'
en elplt.savefig
, el otro era impaciencia :)No soy un experto en detección de imágenes, y no conozco Python, pero le daré un golpe ...
Para detectar patas individuales, primero debe seleccionar todo con una presión mayor que un umbral pequeño, muy cerca de ninguna presión. Cada píxel / punto que esté por encima de este debe estar "marcado". Luego, cada píxel adyacente a todos los píxeles "marcados" se marca, y este proceso se repite varias veces. Se formarían masas totalmente conectadas, por lo que tiene objetos distintos. Luego, cada "objeto" tiene un valor mínimo y máximo de x e y, por lo que los cuadros delimitadores se pueden empaquetar perfectamente alrededor de ellos.
Pseudocódigo:
(MARK) ALL PIXELS ABOVE (0.5)
(MARK) ALL PIXELS (ADJACENT) TO (MARK) PIXELS
REPEAT (STEP 2) (5) TIMES
SEPARATE EACH TOTALLY CONNECTED MASS INTO A SINGLE OBJECT
MARK THE EDGES OF EACH OBJECT, AND CUT APART TO FORM SLICES.
Eso debería ser suficiente.
fuente
Nota: Digo píxel, pero esto podría ser regiones que usan un promedio de los píxeles. La optimización es otro problema ...
Parece que necesita analizar una función (presión en el tiempo) para cada píxel y determinar dónde gira la función (cuando cambia> X en la otra dirección se considera un giro para contrarrestar los errores).
Si sabe en qué cuadros gira, sabrá el cuadro donde la presión fue más fuerte y sabrá dónde fue la menos dura entre las dos patas. En teoría, entonces conocerías los dos cuadros en los que las patas presionaron más y podrás calcular un promedio de esos intervalos.
Este es el mismo recorrido que antes, saber cuándo cada pata aplica la mayor presión lo ayuda a decidir.
fuente