Estoy ayudando a una clínica veterinaria que mide la presión debajo de una pata de perro. Uso Python para mi análisis de datos y ahora estoy atascado tratando de dividir las patas en subregiones (anatómicas).
Hice una matriz 2D de cada pata, que consta de los valores máximos para cada sensor que la pata ha cargado con el tiempo. Aquí hay un ejemplo de una pata, donde usé Excel para dibujar las áreas que quiero 'detectar'. Estos son cuadros de 2 por 2 alrededor del sensor con máximos locales, que juntos tienen la suma más grande.
Así que intenté experimentar un poco y decidí simplemente buscar los máximos de cada columna y fila (no puedo mirar en una dirección debido a la forma de la pata). Esto parece 'detectar' la ubicación de los dedos separados bastante bien, pero también marca los sensores vecinos.
Entonces, ¿cuál sería la mejor manera de decirle a Python cuáles de estos máximos son los que quiero?
Nota: ¡Los cuadrados de 2x2 no pueden superponerse, ya que tienen que ser dedos separados!
También tomé 2x2 por conveniencia, cualquier solución más avanzada es bienvenida, pero simplemente soy un científico del movimiento humano, así que no soy un verdadero programador o matemático, así que por favor manténgalo 'simple'.
Aquí hay una versión que se puede cargar connp.loadtxt
Resultados
Así que probé la solución de @ jextee (ver los resultados a continuación). Como puede ver, funciona muy bien en las patas delanteras, pero funciona menos bien para las patas traseras.
Más específicamente, no puede reconocer el pequeño pico que es el cuarto dedo del pie. Obviamente, esto es inherente al hecho de que el bucle se ve de arriba abajo hacia el valor más bajo, sin tener en cuenta dónde está.
¿Alguien sabría cómo ajustar el algoritmo de @ jextee, para que también pueda encontrar el cuarto dedo del pie?
Como todavía no he procesado ninguna otra prueba, no puedo suministrar ninguna otra muestra. Pero los datos que di antes eran los promedios de cada pata. Este archivo es una matriz con los datos máximos de 9 patas en el orden en que hicieron contacto con la placa.
Esta imagen muestra cómo se espaciaron espacialmente sobre la placa.
Actualizar:
He configurado un blog para cualquier persona interesada y he configurado un SkyDrive con todas las medidas en bruto. Entonces, para cualquiera que solicite más datos: ¡más poder para usted!
Nueva actualización:
Entonces, después de la ayuda que recibí con mis preguntas sobre la detección y la clasificación de las patas , ¡finalmente pude verificar la detección de cada pata! Resulta que no funciona tan bien en otra cosa que no sean las patas del tamaño de mi propio ejemplo. Por supuesto, en retrospectiva, es mi culpa por elegir el 2x2 de manera tan arbitraria.
Aquí hay un buen ejemplo de dónde sale mal: un clavo se reconoce como un dedo del pie y el 'talón' es tan ancho que se reconoce dos veces.
La pata es demasiado grande, por lo que tomar un tamaño de 2x2 sin superposición hace que se detecten algunos dedos dos veces. A la inversa, en perros pequeños a menudo no puede encontrar un quinto dedo del pie, lo que sospecho es que el área de 2x2 es demasiado grande.
Después de probar la solución actual en todas mis mediciones , llegué a la sorprendente conclusión de que para casi todos mis perros pequeños no encontró un quinto dedo del pie y que en más del 50% de los impactos para los perros grandes ¡encontraría más!
Claramente, necesito cambiarlo. Mi propia suposición fue cambiar el tamaño del neighborhood
a algo más pequeño para perros pequeños y más grande para perros grandes. Pero generate_binary_structure
no me dejaba cambiar el tamaño de la matriz.
Por lo tanto, espero que alguien más tenga una mejor sugerencia para ubicar los dedos de los pies, ¿tal vez tener la escala del área de los dedos con el tamaño de la pata?
fuente
Respuestas:
Detecté los picos usando un filtro máximo local . Aquí está el resultado en su primer conjunto de datos de 4 patas:
También lo ejecuté en el segundo conjunto de datos de 9 patas y funcionó también .
Así es como lo haces:
Todo lo que necesita hacer después es usar
scipy.ndimage.measurements.label
la máscara para etiquetar todos los objetos distintos. Entonces podrás jugar con ellos individualmente.Tenga en cuenta que el método funciona bien porque el fondo no es ruidoso. Si lo fuera, detectaría un montón de otros picos no deseados en el fondo. Otro factor importante es el tamaño del barrio . Tendrá que ajustarlo si cambia el tamaño máximo (el debería permanecer más o menos proporcional).
fuente
Solución
Archivo de datos: paw.txt . Código fuente:
Salida sin cuadrados superpuestos. Parece que se seleccionan las mismas áreas que en su ejemplo.
Algunos comentarios
La parte difícil es calcular sumas de todos los cuadrados de 2x2. Supuse que los necesita a todos, por lo que podría haber cierta superposición. Usé cortes para cortar las primeras / últimas columnas y filas de la matriz 2D original, y luego las superpuse todas juntas y calculé sumas.
Para entenderlo mejor, imaginando una matriz 3x3:
Entonces puedes tomar sus rebanadas:
Ahora imagine que los apila uno encima del otro y suma elementos en las mismas posiciones. Estas sumas serán exactamente las mismas sumas sobre los cuadrados de 2x2 con la esquina superior izquierda en la misma posición:
Cuando tienes las sumas sobre cuadrados 2x2, puedes usar
max
para encontrar el máximo, osort
, osorted
para encontrar los picos.Para recordar las posiciones de los picos, acople cada valor (la suma) con su posición ordinal en una matriz aplanada (ver
zip
). Luego calculo nuevamente la posición de la fila / columna cuando imprimo los resultados.Notas
Permití que los cuadrados de 2x2 se superpongan. La versión editada filtra algunos de ellos de modo que solo aparezcan cuadrados no superpuestos en los resultados.
Elegir dedos (una idea)
Otro problema es cómo elegir qué es probable que sean los dedos de todos los picos. Tengo una idea que puede o no funcionar. No tengo tiempo para implementarlo en este momento, así que solo pseudocódigo.
Noté que si los dedos delanteros permanecen en un círculo casi perfecto, el dedo trasero debe estar dentro de ese círculo. Además, los dedos delanteros están más o menos equidistantes. Podemos intentar usar estas propiedades heurísticas para detectar los dedos.
Pseudocódigo:
Este es un enfoque de fuerza bruta. Si N es relativamente pequeño, entonces creo que es factible. Para N = 12, hay combinaciones de C_12 ^ 5 = 792, multiplicadas por 5 formas de seleccionar un dedo posterior, por lo que se deben evaluar 3960 casos para cada pata.
fuente
Este es un problema de registro de imagen . La estrategia general es:
Aquí hay un enfoque aproximado y listo , "lo más tonto que podría funcionar":
Para contrarrestar el problema de orientación, podría tener aproximadamente 8 configuraciones iniciales para las direcciones básicas (Norte, Nordeste, etc.). Ejecute cada uno individualmente y deseche los resultados donde dos o más dedos terminen en el mismo píxel. Pensaré en esto un poco más, pero este tipo de cosas aún se está investigando en el procesamiento de imágenes: ¡no hay respuestas correctas!
Idea ligeramente más compleja: (ponderado) K-significa agrupamiento. No está tan mal.
Luego iterar hasta la convergencia:
Es casi seguro que este método dará resultados mucho mejores y obtendrá la masa de cada grupo que puede ayudar a identificar los dedos de los pies.
(Una vez más, ha especificado la cantidad de grupos por adelantado. Con la agrupación debe especificar la densidad de una forma u otra: elija la cantidad de grupos, apropiada en este caso, o elija un radio de grupo y vea cuántos termina arriba con. Un ejemplo de esto último es el cambio medio .)
Perdón por la falta de detalles de implementación u otros detalles. Codificaría esto, pero tengo una fecha límite. Si nada más ha funcionado para la próxima semana, avíseme y lo intentaré.
fuente
Utilizando la homología persistente para analizar su conjunto de datos, obtengo el siguiente resultado (haga clic para ampliar):
Esta es la versión 2D del método de detección de picos descrito en esta respuesta SO . La figura anterior simplemente muestra clases de homología persistente de 0 dimensiones ordenadas por persistencia.
Escalé el conjunto de datos original por un factor de 2 usando scipy.misc.imresize (). Sin embargo, tenga en cuenta que sí consideré las cuatro patas como un conjunto de datos; dividirlo en cuatro facilitaría el problema.
Metodología. La idea detrás de esto es bastante simple: considere el gráfico de función de la función que asigna a cada píxel su nivel. Se parece a esto:
Ahora considere un nivel de agua a una altura 255 que desciende continuamente a niveles más bajos. En las islas máximas locales aparecen (nacimiento). En los puntos de silla se unen dos islas; consideramos que la isla inferior se fusionó con la isla superior (muerte). El llamado diagrama de persistencia (de las clases de homología de dimensión 0, nuestras islas) representa la muerte sobre los valores de nacimiento de todas las islas:
La persistencia de una isla es entonces la diferencia entre el nivel de nacimiento y muerte; la distancia vertical de un punto a la diagonal principal gris. La figura etiqueta las islas al disminuir la persistencia.
La primera imagen muestra la ubicación de los nacimientos de las islas. Este método no solo proporciona los máximos locales sino que también cuantifica su "importancia" mediante la persistencia mencionada anteriormente. Entonces se filtrarían todas las islas con una persistencia demasiado baja. Sin embargo, en su ejemplo, cada isla (es decir, cada máximo local) es un pico que busca.
El código de Python se puede encontrar aquí .
fuente
Este problema ha sido estudiado con cierta profundidad por los físicos. Hay una buena implementación en ROOT . Mire las clases de TSpectrum (especialmente TSpectrum2 para su caso) y la documentación para ellas.
Referencias
... y para aquellos que no tienen acceso a una suscripción a NIM:
fuente
Aquí hay una idea: calcula el laplaciano (discreto) de la imagen. Esperaría que fuera (negativo y) grande en máximos, de una manera que sea más dramática que en las imágenes originales. Por lo tanto, los máximos podrían ser más fáciles de encontrar.
Aquí hay otra idea: si conoce el tamaño típico de los puntos de alta presión, primero puede suavizar su imagen convolucionándola con un gaussiano del mismo tamaño. Esto puede darle imágenes más simples para procesar.
fuente
Solo un par de ideas fuera de mi cabeza:
También es posible que desee echar un vistazo a OpenCV , tiene una API de Python bastante decente y podría tener algunas funciones que le serían útiles.
fuente
Estoy seguro de que ya tiene suficiente para continuar, pero no puedo evitar sugerirle que utilice el método de agrupación k-means. k-means es un algoritmo de agrupamiento no supervisado que tomará datos (en cualquier cantidad de dimensiones, lo haré en 3D) y los organizará en k grupos con límites distintos. Es agradable aquí porque sabes exactamente cuántos dedos de estos caninos (deberían) tener.
Además, se implementa en Scipy, lo cual es realmente agradable ( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ).
Aquí hay un ejemplo de lo que puede hacer para resolver espacialmente los clústeres 3D:
Lo que quieres hacer es un poco diferente (2D e incluye valores de presión), pero sigo pensando que podrías intentarlo.
fuente
Gracias por los datos en bruto. Estoy en el tren y esto es lo más lejos que he llegado (mi parada se acerca). Masajeé su archivo txt con expresiones regulares y lo puse en una página html con algunos javascript para visualización. Lo estoy compartiendo aquí porque algunos, como yo, podrían encontrarlo más fácilmente pirateable que Python.
Creo que un buen enfoque será invariante en escala y rotación, y mi próximo paso será investigar mezclas de gaussianos. (cada almohadilla de la pata es el centro de un gaussiano).
fuente
Solución del físico:
defina 5 marcadores de pata identificados por sus posiciones
X_i
e inícielos con posiciones aleatorias. Definir alguna función energética combinando algún premio por la ubicación de los marcadores en las posiciones de las patas con algún castigo por la superposición de marcadores; digamos:(
S(X_i)
es la fuerza media en un cuadrado de 2x2 alrededorX_i
,alfa
es un parámetro para alcanzar su punto máximo experimentalmente)Ahora es el momento de hacer magia con Metropolis-Hastings:
1. Seleccione un marcador aleatorio y muévalo un píxel en dirección aleatoria.
2. Calcule dE, la diferencia de energía que causó este movimiento.
3. Obtenga un número aleatorio uniforme de 0-1 y llámelo r.
4. Si
dE<0
oexp(-beta*dE)>r
, acepta el movimiento y ve a 1; si no, deshaga el movimiento y vaya a 1.Esto debe repetirse hasta que los marcadores converjan en patas. Beta controla el escaneo para optimizar la compensación, por lo que también debe optimizarse experimentalmente; También se puede aumentar constantemente con el tiempo de simulación (recocido simulado).
fuente
Aquí hay otro enfoque que utilicé al hacer algo similar para un telescopio grande:
1) Busca el píxel más alto. Una vez que tenga eso, busque el mejor ajuste para 2x2 (quizás maximizando la suma de 2x2), o haga un ajuste gaussiano 2d dentro de la subregión de, digamos, 4x4 centrado en el píxel más alto.
Luego configure esos píxeles de 2x2 que haya encontrado en cero (o tal vez 3x3) alrededor del centro del pico
regrese a 1) y repita hasta que el pico más alto caiga por debajo de un umbral de ruido, o tenga todos los dedos de los pies que necesita
fuente
Probablemente valga la pena probar con redes neuronales si puede crear algunos datos de entrenamiento ... pero esto necesita muchas muestras anotadas a mano.
fuente
un esbozo ...
probablemente desee utilizar un algoritmo de componentes conectados para aislar cada región de la pata. wiki tiene una descripción decente de esto (con algún código) aquí: http://en.wikipedia.org/wiki/Connected_Component_Labeling
Tendrás que tomar una decisión sobre si usar 4 u 8 conexiones. personalmente, para la mayoría de los problemas, prefiero la conectividad 6. de todos modos, una vez que haya separado cada "huella de la pata" como una región conectada, debería ser bastante fácil recorrer la región y encontrar los máximos. una vez que haya encontrado los máximos, podría ampliar iterativamente la región hasta alcanzar un umbral predeterminado para identificarlo como un "dedo del pie" dado.
Un problema sutil aquí es que tan pronto como comience a usar técnicas de visión por computadora para identificar algo como una pata derecha / izquierda / delantera / trasera y comience a mirar los dedos de los pies individuales, debe comenzar a tener en cuenta las rotaciones, sesgos y traducciones. Esto se logra mediante el análisis de los llamados "momentos". Hay algunos momentos diferentes a considerar en las aplicaciones de visión:
momentos centrales: invariante de traducción momentos normalizados: escala y traducción invariante momentos hu: invariante de traslación, escala y rotación
Se puede encontrar más información sobre los momentos buscando "momentos de imagen" en la wiki.
fuente
Quizás pueda usar algo como los modelos de mezcla gaussiana. Aquí hay un paquete de Python para hacer GMM (acabo de hacer una búsqueda en Google) http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/em/
fuente
Parece que puedes engañar un poco usando el algoritmo de jetxee. Está encontrando bien los primeros tres dedos del pie, y deberías poder adivinar dónde se basa el cuarto.
fuente
Interesante problema La solución que probaría es la siguiente.
Aplique un filtro de paso bajo, como la convolución con una máscara gaussiana 2D. Esto le dará un montón de valores (probablemente, pero no necesariamente de coma flotante).
Realice una supresión 2D no máxima utilizando el radio aproximado conocido de cada almohadilla de la pata (o dedo del pie).
Esto debería darle las posiciones máximas sin tener múltiples candidatos que estén muy juntos. Solo para aclarar, el radio de la máscara en el paso 1 también debe ser similar al radio utilizado en el paso 2. Este radio podría ser seleccionable, o el veterinario podría medirlo explícitamente de antemano (variará con la edad / raza / etc.).
Algunas de las soluciones sugeridas (desplazamiento medio, redes neuronales, etc.) probablemente funcionarán hasta cierto punto, pero son demasiado complicadas y probablemente no sean ideales.
fuente
Bueno, aquí hay un código simple y no terriblemente eficiente, pero para este tamaño de conjunto de datos está bien.
Básicamente, simplemente hago una matriz con la posición de la esquina superior izquierda y la suma de cada cuadrado de 2x2 y la ordeno por la suma. Luego tomo el cuadrado de 2x2 con la suma más alta fuera de la contienda, lo pongo en la
best
matriz y elimino todos los otros cuadrados de 2x2 que usaron cualquier parte de este cuadrado de 2x2 recién eliminado.Parece funcionar bien, excepto con la última pata (la que tiene la suma más pequeña en el extremo derecho en su primera imagen), resulta que hay otros dos cuadrados elegibles de 2x2 con una suma mayor (y tienen una suma igual a El uno al otro). Uno de ellos todavía selecciona un cuadrado de su cuadrado de 2x2, pero el otro está a la izquierda. Afortunadamente, por suerte, vemos que estamos eligiendo más del que desea, pero esto puede requerir que se usen otras ideas para obtener lo que realmente quiere todo el tiempo.
fuente
Solo quiero decirles que hay una buena opción para encontrar local
maxima
en imágenes con python:o para skimage
0.8.0
:http://scikit-image.org/docs/0.8.0/api/skimage.feature.peak.html
fuente
Quizás un enfoque ingenuo sea suficiente aquí: construya una lista de todos los cuadrados de 2x2 en su avión, ordénelos por su suma (en orden descendente).
Primero, seleccione el cuadrado de mayor valor en su "lista de patas". Luego, elija iterativamente 4 de los siguientes cuadrados mejores que no se crucen con ninguno de los cuadrados encontrados anteriormente.
fuente
Hay varias y extensas piezas de software disponibles de la comunidad de astronomía y cosmología: esta es un área importante de investigación tanto histórica como actual.
No se alarme si no es un astrónomo; algunos son fáciles de usar fuera del campo. Por ejemplo, podría usar astropy / photutils:
https://photutils.readthedocs.io/en/stable/detection.html#local-peak-detection
[Parece un poco grosero repetir su breve código de muestra aquí.]
A continuación se proporciona una lista incompleta y ligeramente sesgada de técnicas / paquetes / enlaces que pueden ser de interés: agregue más en los comentarios y actualizaré esta respuesta según sea necesario. Por supuesto, existe una compensación entre la precisión y los recursos informáticos. [Honestamente, hay demasiados para dar ejemplos de código en una sola respuesta como esta, así que no estoy seguro de si esta respuesta volará o no.]
Extractor de código fuente https://www.astromatic.net/software/sextractor
MultiNest https://github.com/farhanferoz/MultiNest [+ pyMultiNest]
Desafío de búsqueda de fuente ASKAP / EMU: https://arxiv.org/abs/1509.03931
También puede buscar desafíos de extracción de fuentes de Planck y / o WMAP.
...
fuente
¿Qué sucede si continúa paso a paso: primero localiza el máximo global, procesa si es necesario los puntos circundantes dado su valor, luego establece la región encontrada en cero y repite para el siguiente?
fuente
No estoy seguro de que esto responda la pregunta, pero parece que puedes buscar los n picos más altos que no tienen vecinos.
Aquí está la esencia. Tenga en cuenta que está en Ruby, pero la idea debe ser clara.
fuente