Detección de picos en una matriz 2D

Estoy ayudando a una clínica veterinaria a medir la presión debajo de la pata de un perro. Utilizo 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 ha sido cargado por la pata a lo largo del tiempo. Aquí hay un ejemplo de una pata, donde usé Excel para dibujar las áreas que quiero 'detectar'. Estas son cajas de 2 por 2 alrededor del sensor con máximos locales, que juntas tienen la mayor suma.

texto alternativo

Así que probé un poco de experimentación 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.

texto alternativo

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 como una conveniencia, cualquier solución más avanzada es bienvenida, pero soy simplemente un científico del movimiento humano, así que no soy un programador real ni un matemático, así que manténgalo 'simple'.

Aquí hay una versión que se puede cargar con np.loadtxt


Resultados

Así que probé la solución de @jextee (vea los resultados a continuación). Como puede ver, funciona muy bien en las patas delanteras, pero funciona menos en 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 mira de arriba hacia abajo hacia el valor más bajo, sin tener en cuenta dónde se encuentra.

¿Alguien sabría cómo modificar el algoritmo de @jextee para que también pueda encontrar el cuarto dedo del pie?

texto alternativo

Como aún no he procesado ninguna otra prueba, no puedo proporcionar ninguna otra muestra. Pero los datos que di antes fueron 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 distribuyeron espacialmente sobre el plato.

texto alternativo

Actualizar:

He creado un blog para cualquier persona interesada. y He configurado un SkyDrive con todas las medidas sin procesar. 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 detección de la pata y clasificación de la pata, ¡Finalmente pude verificar la detección de dedos de cada pata! Resulta que no funciona tan bien en nada más que en patas como la 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: una uña se reconoce como un dedo del pie y el 'talón' es tan ancho que se reconoce dos veces.

texto alternativo

La pata es demasiado grande, por lo que tomar un tamaño de 2x2 sin superposición hace que algunos dedos se detecten dos veces. Al revés, en perros pequeños a menudo no se encuentra el quinto dedo del pie, lo cual sospecho que se debe a que el área de 5x2 es demasiado grande.

Después probando la solución actual en todas mis medidas Llegué a la asombrosa conclusión de que para casi todos mis perros pequeños no encontró un quinto dedo del pie y que en más del 5% de los impactos de los perros grandes, ¡encontraría más!

Así que 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 dejaría 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?

preguntado el 10 de septiembre de 10 a las 09:09

¿Supongo que las comas son lugares decimales en lugar de separadores de valores? -

Mientras estoy haciendo un estudio de viabilidad, todo vale. Así que estoy buscando tantas formas de definir la presión, incluidas las subregiones. También necesito ser capaz de discriminar entre los lados del "dedo gordo del pie" y del "dedo meñique" para estimar la orientación. Pero como esto no se ha hecho antes, no se sabe lo que podríamos encontrar :-) -

@Ron: uno de los objetivos de este estudio es ver para qué tamaño / peso de perros es adecuado el sistema, así que sí, mientras este perro pesaba unos 20 kg. Tengo algunos que son considerablemente más pequeños (y más grandes) y espero no poder hacer lo mismo con los realmente pequeños. -

@frank las patas se miden a lo largo del tiempo, de ahí la 3ª dimensión. Sin embargo, no se mueven de su lugar (en términos relativos), por lo que lo que más me interesa es dónde se encuentran los dedos de los pies en 2D. El aspecto 3D viene gratis después de eso:

Para mí, no está claro si el Nueva actualización ¿Ya está cubierto en las respuestas a continuación? -

22 Respuestas

Detecté los picos usando un filtro máximo local. Aquí está el resultado de su primer conjunto de datos de 4 patas: Resultado de la detección de picos

También lo ejecuté en el segundo conjunto de datos de 9 patas y funcionó también.

Así es como lo haces:

import numpy as np
from scipy.ndimage.filters import maximum_filter
from scipy.ndimage.morphology import generate_binary_structure, binary_erosion
import matplotlib.pyplot as pp

#for some reason I had to reshape. Numpy ignored the shape header.
paws_data = np.loadtxt("paws.txt").reshape(4,11,14)

#getting a list of images
paws = [p.squeeze() for p in np.vsplit(paws_data,4)]


def detect_peaks(image):
    """
    Takes an image and detect the peaks usingthe local maximum filter.
    Returns a boolean mask of the peaks (i.e. 1 when
    the pixel's value is the neighborhood maximum, 0 otherwise)
    """

    # define an 8-connected neighborhood
    neighborhood = generate_binary_structure(2,2)

    #apply the local maximum filter; all pixel of maximal value 
    #in their neighborhood are set to 1
    local_max = maximum_filter(image, footprint=neighborhood)==image
    #local_max is a mask that contains the peaks we are 
    #looking for, but also the background.
    #In order to isolate the peaks we must remove the background from the mask.

    #we create the mask of the background
    background = (image==0)

    #a little technicality: we must erode the background in order to 
    #successfully subtract it form local_max, otherwise a line will 
    #appear along the background border (artifact of the local maximum filter)
    eroded_background = binary_erosion(background, structure=neighborhood, border_value=1)

    #we obtain the final mask, containing only peaks, 
    #by removing the background from the local_max mask (xor operation)
    detected_peaks = local_max ^ eroded_background

    return detected_peaks


#applying the detection and plotting results
for i, paw in enumerate(paws):
    detected_peaks = detect_peaks(paw)
    pp.subplot(4,2,(2*i+1))
    pp.imshow(paw)
    pp.subplot(4,2,(2*i+2) )
    pp.imshow(detected_peaks)

pp.show()

Todo lo que necesitas hacer después es usar scipy.ndimage.measurements.label en la máscara para etiquetar todos los objetos distintos. Entonces podrás jugar con ellos individualmente.

Nota: 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 segundo plano. Otro factor importante es el tamaño de la barrio. Deberá ajustarlo si cambia el tamaño del pico (debe permanecer aproximadamente proporcional).

Respondido 26 Oct 18, 04:10

Hay una solución más simple que (eroded_background ^ local_peaks). Solo hazlo (primer plano y picos locales) - ryan soklaski

¡Gracias por el consejo sobre cómo detectar máximos / picos locales en matrices multidimensionales! - milind r

Solución

Archivo de datos: pata.txt. Código fuente:

from scipy import *
from operator import itemgetter

n = 5  # how many fingers are we looking for

d = loadtxt("paw.txt")
width, height = d.shape

# Create an array where every element is a sum of 2x2 squares.

fourSums = d[:-1,:-1] + d[1:,:-1] + d[1:,1:] + d[:-1,1:]

# Find positions of the fingers.

# Pair each sum with its position number (from 0 to width*height-1),

pairs = zip(arange(width*height), fourSums.flatten())

# Sort by descending sum value, filter overlapping squares

def drop_overlapping(pairs):
    no_overlaps = []
    def does_not_overlap(p1, p2):
        i1, i2 = p1[0], p2[0]
        r1, col1 = i1 / (width-1), i1 % (width-1)
        r2, col2 = i2 / (width-1), i2 % (width-1)
        return (max(abs(r1-r2),abs(col1-col2)) >= 2)
    for p in pairs:
        if all(map(lambda prev: does_not_overlap(p,prev), no_overlaps)):
            no_overlaps.append(p)
    return no_overlaps

pairs2 = drop_overlapping(sorted(pairs, key=itemgetter(1), reverse=True))

# Take the first n with the heighest values

positions = pairs2[:n]

# Print results

print d, "\n"

for i, val in positions:
    row = i / (width-1)
    column = i % (width-1)
    print "sum = %f @ %d,%d (%d)" % (val, row, column, i)
    print d[row:row+2,column:column+2], "\n"

Salida sin cuadrados superpuestos. Parece que se seleccionan las mismas áreas que en su ejemplo.

Algunos comentarios

La parte complicada es calcular las sumas de todos los cuadrados de 2x2. Supuse que los necesita todos, por lo que podría haber algunos solapamientos. Utilicé porciones para cortar las primeras / últimas columnas y filas de la matriz 2D original, y luego superponiéndolas todas juntas y calculando sumas.

Para entenderlo mejor, imagina una matriz de 3x3:

>>> a = arange(9).reshape(3,3) ; a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

Entonces puedes tomar sus rodajas:

>>> a[:-1,:-1]
array([[0, 1],
       [3, 4]])
>>> a[1:,:-1]
array([[3, 4],
       [6, 7]])
>>> a[:-1,1:]
array([[1, 2],
       [4, 5]])
>>> a[1:,1:]
array([[4, 5],
       [7, 8]])

Ahora imagina que los apilas uno encima del otro y sumas 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:

>>> sums = a[:-1,:-1] + a[1:,:-1] + a[:-1,1:] + a[1:,1:]; sums
array([[ 8, 12],
       [20, 24]])

Cuando tenga las sumas de más de 2x2 cuadrados, puede usar max para encontrar el máximo, o sort o el sorted para encontrar los picos.

Para recordar las posiciones de los picos, acoplo cada valor (la suma) con su posición ordinal en una matriz aplanada (ver zip). Luego vuelvo a calcular la posición de la fila / columna cuando imprimo los resultados.

Notas

Permití que los cuadrados de 2x2 se superpusieran. La versión editada filtra algunos de ellos de modo que solo aparecen cuadrados que no se superponen en los resultados.

Elegir dedos (una idea)

Otro problema es cómo elegir lo que probablemente sean dedos de todos los picos. Tengo una idea que puede funcionar o no. 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 debería estar dentro de ese círculo. Además, los dedos delanteros están más o menos separados por igual. Podemos intentar utilizar estas propiedades heurísticas para detectar los dedos.

Pseudo código:

select the top N finger candidates (not too many, 10 or 12)
consider all possible combinations of 5 out of N (use itertools.combinations)
for each combination of 5 fingers:
    for each finger out of 5:
        fit the best circle to the remaining 4
        => position of the center, radius
        check if the selected finger is inside of the circle
        check if the remaining four are evenly spread
        (for example, consider angles from the center of the circle)
        assign some cost (penalty) to this selection of 4 peaks + a rear finger
        (consider, probably weighted:
             circle fitting error,
             if the rear finger is inside,
             variance in the spreading of the front fingers,
             total intensity of 5 peaks)
choose a combination of 4 peaks + a rear peak with the lowest penalty

Este es un enfoque de fuerza bruta. Si N es relativamente pequeño, creo que es factible. Para N = 12, hay C_12 ^ 5 = 792 combinaciones, multiplicado por 5 formas de seleccionar un dedo trasero, por lo que 3960 casos para evaluar por cada pata.

Respondido el 11 de Septiembre de 10 a las 00:09

Tendrá que filtrar las patas manualmente, dada su lista de resultados ... elegir los cuatro resultados más altos le dará las cuatro posibilidades de construir un cuadrado de 2x2 que contenga el valor máximo de 6.8 - johannes charra

Los cuadros de 2x2 no se pueden superponer, ya que si quiero hacer estadísticas, no quiero usar la misma región, quiero comparar regiones :-) - Ivo Flipse

Edité la respuesta. Ahora no hay cuadrados superpuestos en los resultados. - Sastanin

Lo probé y parece funcionar para las patas delanteras, pero menos para las traseras. Supongo que tendremos que probar algo que sepa dónde buscar. Ivo Flipse

Expliqué mi idea de cómo se pueden detectar los dedos en pseudocódigo. Si le gusta, puedo intentar implementarlo mañana por la noche. - Sastanin

Esta es una problema de registro de imagen. La estrategia general es:

  • Tener un ejemplo conocido o algún tipo de antes en los datos.
  • Ajuste sus datos al ejemplo o ajuste el ejemplo a sus datos.
  • Ayuda si sus datos son aproximadamente alineados en primer lugar.

Aquí hay un enfoque aproximado y listo, "la cosa más tonta que podría funcionar":

  • Comience con las coordenadas de cinco dedos del pie aproximadamente en el lugar que espera.
  • Con cada uno, suba iterativamente a la cima de la colina. es decir, dada la posición actual, muévase al píxel vecino máximo, si su valor es mayor que el píxel actual. Deténgase cuando las coordenadas de su dedo del pie hayan dejado de moverse.

Para contrarrestar el problema de la orientación, podría tener aproximadamente 8 configuraciones iniciales para las direcciones básicas (norte, noreste, etc.). Ejecute cada uno individualmente y descarte cualquier resultado en el que dos o más dedos terminen en el mismo píxel. Pensaré en esto un poco más, pero este tipo de cosas todavía se están investigando en el procesamiento de imágenes, ¡no hay respuestas correctas!

Idea un poco más compleja: agrupación (ponderada) de K significa. No está tan mal.

  • Comience con cinco coordenadas del dedo del pie, pero ahora estos son "centros de grupos".

Luego iterar hasta la convergencia:

  • Asigne cada píxel al grupo más cercano (solo haga una lista para cada grupo).
  • Calcula el centro de masa de cada grupo. Para cada grupo, esto es: Suma (coordenada * valor de intensidad) / Suma (coordenada)
  • Mueva cada grupo al nuevo centro de masa.

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.

(Nuevamente, ha especificado la cantidad de clústeres por adelantado. Con la agrupación en clústeres debe especificar la densidad de una forma u otra: elija el número de clústeres, apropiado en este caso, o elija un radio de clúster y vea cuántos termina con. Un ejemplo de lo último es cambio medio.)

Lamento 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é.

Respondido el 11 de Septiembre de 10 a las 01:09

El problema es que las patas cambian su orientación y no tengo ninguna calibración / línea de base de una pata correcta para empezar. Además, me temo que muchos de los algoritmos de reconocimiento de imágenes están un poco fuera de mi alcance. - Ivo Flipse

El enfoque "tosco y listo" es bastante simple; tal vez no entendí bien la idea. Pondré un pseudocódigo para ilustrar. - pastelmaster

Tengo la sensación de que tu sugerencia ayudará a arreglar el reconocimiento de las patas traseras, simplemente no sé 'cómo' - Ivo Flipse

Agregué otra idea. Por cierto, si tiene una gran cantidad de buenos datos, sería genial ponerlos en línea en algún lugar. Podría ser útil para las personas que estudian el procesamiento de imágenes / aprendizaje automático y es posible que obtenga más código de él ... - pastelmaster

Estaba pensando en escribir mi procesamiento de datos en un simple blog de Wordpress, simplemente para ser útil para otros y tengo que escribirlo de todos modos. Me gustan todas tus sugerencias, pero temo que tendré que esperar a alguien sin una fecha límite ;-) - Ivo Flipse

Usando homología persistente para analizar su conjunto de datos, obtengo el siguiente resultado (haga clic para ampliar):

Resultado

Esta es la versión 2D del método de detección de picos descrito en este Pues contesta. La figura anterior simplemente muestra clases de homología persistente de dimensión 0 ordenadas por persistencia.

Elevé el conjunto de datos original en un factor de 2 usando scipy.misc.imresize (). Sin embargo, tenga en cuenta que 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:

Gráfico de función 3D

Ahora considere un nivel de agua a la altura 255 que desciende continuamente a niveles más bajos. En las islas máximas locales surgen (nacimiento). En los puntos de silla de montar se unen dos islas; consideramos que la isla inferior se fusiona 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:

Diagrama de persistencia

La persistencia de una isla es entonces la diferencia entre el nivel de nacimiento y el de muerte; la distancia vertical de un punto a la diagonal principal gris. La figura etiqueta las islas disminuyendo la persistencia.

La primera imagen muestra la ubicación de los nacimientos de las islas. Este método no sólo proporciona los máximos locales, sino que también cuantifica su "importancia" mediante la persistencia mencionada anteriormente. Luego, 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.

Se puede encontrar el código Python aquí.

contestado el 23 de mayo de 18 a las 18:05

Este problema ha sido estudiado con cierta profundidad por los físicos. Hay una buena implementación en RAÍZ. Mira el Espectro clases (especialmente TSpectrum2 para su caso) y la documentación correspondiente.

Referencias:

  1. M. Morhac et al .: Métodos de eliminación de antecedentes para espectros de rayos gamma de coincidencia multidimensional. Instrumentos y métodos nucleares en la investigación de la física A 401 (1997) 113-132.
  2. M. Morhac et al .: Deconvolución de oro unidimensional y bidimensional eficiente y su aplicación a la descomposición de espectros de rayos gamma. Instrumentos y métodos nucleares en la investigación de la física A 401 (1997) 385-408.
  3. M. Morhac et al .: Identificación de picos en espectros de rayos gamma de coincidencia multidimensional. Instrumentos y métodos nucleares en la investigación de la física A 443 (2000), 108-125.

... y para aquellos que no tienen acceso a una suscripción a NIM:

Respondido el 10 de Septiembre de 10 a las 23:09

Para echar un vistazo al artículo, parece describir el mismo procesamiento de datos que lo que estoy intentando aquí, sin embargo, me temo que superó en gran medida mis habilidades de programación :( - Ivo Flipse

@Ivo: Nunca he intentado implementarlo yo mismo. Solo uso ROOT. No obstante, hay enlaces de Python, pero tenga en cuenta que ROOT es un paquete bastante pesado. - dmckee --- ex-gatito moderador

@Ivo Flipse: Estoy de acuerdo con dmckee. Tiene muchas pistas prometedoras en otras respuestas. Si todos fallan y tienes ganas de invertir algo de tiempo, puedes profundizar en ROOT y (probablemente) hará lo que necesites. Nunca he conocido a nadie que haya intentado aprender ROOT a través de los enlaces de Python (en lugar de su C ++ natural), así que les deseo suerte. - Física Michael

Aquí tienes una idea: calculas el laplaciano (discreto) de la imagen. Esperaría que fuera (negativo y) grande en el máximo, de una manera 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 sencillas de procesar.

Respondido el 11 de Septiembre de 10 a las 10:09

Solo un par de ideas en la parte superior de mi cabeza:

  • tome el gradiente (derivado) del escaneo, vea si eso elimina las llamadas falsas
  • tomar el máximo de los máximos locales

Es posible que también desee echar un vistazo a OpenCV, tiene una API de Python bastante decente y puede tener algunas funciones que le resulten útiles.

Respondido el 10 de Septiembre de 10 a las 13:09

Con gradiente, ¿quiere decir que debería calcular la inclinación de las pendientes, una vez que esté por encima de cierto valor, sé que hay 'un pico'? Probé esto, pero algunos de los dedos solo tienen picos muy bajos (1.2 N / cm) en comparación con algunos de los otros (8 N / cm). Entonces, ¿cómo debo manejar los picos con un gradiente muy bajo? - Ivo Flipse

Lo que me funcionó en el pasado si no podía usar el gradiente directamente era mirar el gradiente y los máximos, por ejemplo, si el gradiente es un extremo local y estoy en un máximo local, entonces estoy en un punto de interesar. - CrisC

Estoy seguro de que ya tiene suficiente para continuar, pero no puedo evitar sugerir el uso del método de agrupación de k-means. k-means es un algoritmo de agrupación en clústeres no supervisado que tomará sus datos (en cualquier número de dimensiones; yo hago esto en 3D) y los organizará en k clústeres con límites distintos. Es bueno aquí porque sabes exactamente cuántos dedos de los pies (deberían) tener estos caninos.

Además, está implementado en Scipy, lo cual es realmente bueno (http://docs.scipy.org/doc/scipy/reference/cluster.vq.html).

A continuación, se muestra un ejemplo de lo que puede hacer para resolver clústeres 3D espacialmente: enter image description here

Lo que quieres hacer es un poco diferente (2D e incluye valores de presión), pero sigo pensando que podrías intentarlo.

Respondido 13 ago 13, 22:08

gracias por los datos sin procesar. Estoy en el tren y esto es todo lo que he llegado (mi parada se acerca). Masajeé su archivo txt con expresiones regulares y lo coloqué en una página html con algo de 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 de rotación y escala, y mi próximo paso será investigar mezclas de gaussianos. (cada almohadilla de la pata es el centro de un gaussiano).

    <html>
<head>
    <script type="text/javascript" src="http://vis.stanford.edu/protovis/protovis-r3.2.js"></script> 
    <script type="text/javascript">
    var heatmap = [[[0,0,0,0,0,0,0,4,4,0,0,0,0],
[0,0,0,0,0,7,14,22,18,7,0,0,0],
[0,0,0,0,11,40,65,43,18,7,0,0,0],
[0,0,0,0,14,61,72,32,7,4,11,14,4],
[0,7,14,11,7,22,25,11,4,14,65,72,14],
[4,29,79,54,14,7,4,11,18,29,79,83,18],
[0,18,54,32,18,43,36,29,61,76,25,18,4],
[0,4,7,7,25,90,79,36,79,90,22,0,0],
[0,0,0,0,11,47,40,14,29,36,7,0,0],
[0,0,0,0,4,7,7,4,4,4,0,0,0]
],[
[0,0,0,4,4,0,0,0,0,0,0,0,0],
[0,0,11,18,18,7,0,0,0,0,0,0,0],
[0,4,29,47,29,7,0,4,4,0,0,0,0],
[0,0,11,29,29,7,7,22,25,7,0,0,0],
[0,0,0,4,4,4,14,61,83,22,0,0,0],
[4,7,4,4,4,4,14,32,25,7,0,0,0],
[4,11,7,14,25,25,47,79,32,4,0,0,0],
[0,4,4,22,58,40,29,86,36,4,0,0,0],
[0,0,0,7,18,14,7,18,7,0,0,0,0],
[0,0,0,0,4,4,0,0,0,0,0,0,0],
],[
[0,0,0,4,11,11,7,4,0,0,0,0,0],
[0,0,0,4,22,36,32,22,11,4,0,0,0],
[4,11,7,4,11,29,54,50,22,4,0,0,0],
[11,58,43,11,4,11,25,22,11,11,18,7,0],
[11,50,43,18,11,4,4,7,18,61,86,29,4],
[0,11,18,54,58,25,32,50,32,47,54,14,0],
[0,0,14,72,76,40,86,101,32,11,7,4,0],
[0,0,4,22,22,18,47,65,18,0,0,0,0],
[0,0,0,0,4,4,7,11,4,0,0,0,0],
],[
[0,0,0,0,4,4,4,0,0,0,0,0,0],
[0,0,0,4,14,14,18,7,0,0,0,0,0],
[0,0,0,4,14,40,54,22,4,0,0,0,0],
[0,7,11,4,11,32,36,11,0,0,0,0,0],
[4,29,36,11,4,7,7,4,4,0,0,0,0],
[4,25,32,18,7,4,4,4,14,7,0,0,0],
[0,7,36,58,29,14,22,14,18,11,0,0,0],
[0,11,50,68,32,40,61,18,4,4,0,0,0],
[0,4,11,18,18,43,32,7,0,0,0,0,0],
[0,0,0,0,4,7,4,0,0,0,0,0,0],
],[
[0,0,0,0,0,0,4,7,4,0,0,0,0],
[0,0,0,0,4,18,25,32,25,7,0,0,0],
[0,0,0,4,18,65,68,29,11,0,0,0,0],
[0,4,4,4,18,65,54,18,4,7,14,11,0],
[4,22,36,14,4,14,11,7,7,29,79,47,7],
[7,54,76,36,18,14,11,36,40,32,72,36,4],
[4,11,18,18,61,79,36,54,97,40,14,7,0],
[0,0,0,11,58,101,40,47,108,50,7,0,0],
[0,0,0,4,11,25,7,11,22,11,0,0,0],
[0,0,0,0,0,4,0,0,0,0,0,0,0],
],[
[0,0,4,7,4,0,0,0,0,0,0,0,0],
[0,0,11,22,14,4,0,4,0,0,0,0,0],
[0,0,7,18,14,4,4,14,18,4,0,0,0],
[0,4,0,4,4,0,4,32,54,18,0,0,0],
[4,11,7,4,7,7,18,29,22,4,0,0,0],
[7,18,7,22,40,25,50,76,25,4,0,0,0],
[0,4,4,22,61,32,25,54,18,0,0,0,0],
[0,0,0,4,11,7,4,11,4,0,0,0,0],
],[
[0,0,0,0,7,14,11,4,0,0,0,0,0],
[0,0,0,4,18,43,50,32,14,4,0,0,0],
[0,4,11,4,7,29,61,65,43,11,0,0,0],
[4,18,54,25,7,11,32,40,25,7,11,4,0],
[4,36,86,40,11,7,7,7,7,25,58,25,4],
[0,7,18,25,65,40,18,25,22,22,47,18,0],
[0,0,4,32,79,47,43,86,54,11,7,4,0],
[0,0,0,14,32,14,25,61,40,7,0,0,0],
[0,0,0,0,4,4,4,11,7,0,0,0,0],
],[
[0,0,0,0,4,7,11,4,0,0,0,0,0],
[0,4,4,0,4,11,18,11,0,0,0,0,0],
[4,11,11,4,0,4,4,4,0,0,0,0,0],
[4,18,14,7,4,0,0,4,7,7,0,0,0],
[0,7,18,29,14,11,11,7,18,18,4,0,0],
[0,11,43,50,29,43,40,11,4,4,0,0,0],
[0,4,18,25,22,54,40,7,0,0,0,0,0],
[0,0,4,4,4,11,7,0,0,0,0,0,0],
],[
[0,0,0,0,0,7,7,7,7,0,0,0,0],
[0,0,0,0,7,32,32,18,4,0,0,0,0],
[0,0,0,0,11,54,40,14,4,4,22,11,0],
[0,7,14,11,4,14,11,4,4,25,94,50,7],
[4,25,65,43,11,7,4,7,22,25,54,36,7],
[0,7,25,22,29,58,32,25,72,61,14,7,0],
[0,0,4,4,40,115,68,29,83,72,11,0,0],
[0,0,0,0,11,29,18,7,18,14,4,0,0],
[0,0,0,0,0,4,0,0,0,0,0,0,0],
]
];
</script>
</head>
<body>
    <script type="text/javascript+protovis">    
    for (var a=0; a < heatmap.length; a++) {
    var w = heatmap[a][0].length,
    h = heatmap[a].length;
var vis = new pv.Panel()
    .width(w * 6)
    .height(h * 6)
    .strokeStyle("#aaa")
    .lineWidth(4)
    .antialias(true);
vis.add(pv.Image)
    .imageWidth(w)
    .imageHeight(h)
    .image(pv.Scale.linear()
        .domain(0, 99, 100)
        .range("#000", "#fff", '#ff0a0a')
        .by(function(i, j) heatmap[a][j][i]));
vis.render();
}
</script>
  </body>
</html>

texto alternativo

Respondido el 11 de Septiembre de 10 a las 02:09

Creo que esta es una prueba de concepto de que las técnicas gaussianas recomendadas podrían funcionar, ahora si alguien pudiera probarlo con Python ;-) - Ivo Flipse

Solución del físico:
Definir 5 marcas de patas identificadas por sus posiciones. X_i e iniciarlos con posiciones aleatorias. Definir alguna función de energía combinando algún premio por la ubicación de marcadores en las posiciones de las patas con algún castigo por superposición de marcadores; digamos:

E(X_i;S)=-Sum_i(S(X_i))+alfa*Sum_ij (|X_i-Xj|<=2*sqrt(2)?1:0)

(S(X_i) es la fuerza media en el cuadrado de 2x2 alrededor X_i, alfa es un parámetro que debe alcanzarse experimentalmente)

Ahora es el momento de hacer algo de magia 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 provocó este movimiento.
3. Obtenga un número aleatorio uniforme del 0-1 y llámelo r.
4. Si dE<0 or exp(-beta*dE)>r, acepta el movimiento y pasa al 1; si no es así, deshaga el movimiento y vaya a 1.
Esto debe repetirse hasta que los marcadores converjan en patas. Beta controla el escaneo para optimizar el intercambio, por lo que también debe optimizarse experimentalmente; también se puede aumentar constantemente con el tiempo de simulación (recocido simulado).

Respondido el 10 de Septiembre de 10 a las 20:09

¿Te importaría mostrar cómo funcionaría esto en mi ejemplo? Como realmente no me gustan las matemáticas de alto nivel, ya tengo dificultades para desentrañar la fórmula que propusiste :( - Ivo Flipse

Esta es la matemática de la escuela secundaria, probablemente mi notación esté ofuscada. Tengo un plan para comprobarlo, así que estad atentos. - mbq

Soy un físico de partículas. Durante mucho tiempo, la herramienta de software de referencia en nuestra disciplina se llamó PAW, y tenía una entidad relacionada con los gráficos llamada "marcador". Puedes imaginar lo confuso que encontré esta respuesta en el primer par de veces ... - dmckee --- ex-gatito moderador

Solo quiero decirles que hay una buena opción para encontrar locales maxima en imágenes con python:

from skimage.feature import peak_local_max

o para skimage 0.8.0:

from skimage.feature.peak import peak_local_max

http://scikit-image.org/docs/0.8.0/api/skimage.feature.peak.html

Respondido 17 Abr '20, 18:04

Aquí hay otro enfoque que utilicé al hacer algo similar para un telescopio grande:

1) Busque el píxel más alto. Una vez que tenga eso, busque el mejor ajuste para 2x2 (tal vez maximizando la suma 2x2), o haga un ajuste gaussiano 2d dentro de la subregión de, por ejemplo, 4x4 centrado en el píxel más alto.

Luego establezca esos 2x2 píxeles que ha encontrado en cero (o tal vez 3x3) alrededor del centro del pico

vuelva 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

Respondido el 10 de Septiembre de 10 a las 19:09

¿Le importaría compartir un ejemplo de código que haga esto? Puedo seguir lo que intentas hacer, pero no tengo idea de cómo codificarlo yo mismo. Ivo Flipse

De hecho, vengo de trabajar con Matlab, así que sí, eso ya ayudaría. Pero si usa funciones realmente ajenas, puede ser difícil para mí replicarlo con Python - Ivo Flipse

Probablemente valga la pena probar con redes neuronales si puede crear algunos datos de entrenamiento ... pero esto necesita muchas muestras anotadas a mano.

Respondido el 10 de Septiembre de 10 a las 19:09

Si vale la pena, no me importaría anotar una muestra grande a mano. Mi problema sería: ¿cómo implemento esto, ya que no sé nada sobre programación de redes neuronales? Ivo Flipse

un bosquejo aproximado ...

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 algo de código) aquí: http://en.wikipedia.org/wiki/Connected_Component_Labeling

tendrá que tomar una decisión sobre si utilizar 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 pata" como una región conectada, debería ser bastante fácil iterar a través de 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 identificarla 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, los sesgos y las traslaciones. 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: traslación invariantes momentos normalizados: escalado y traslación invariantes momentos hu: traslación, escala y rotación invariantes

se puede encontrar más información sobre momentos buscando "momentos de imagen" en wiki.

Respondido el 10 de Septiembre de 10 a las 20:09

Quizás puedas usar algo como los modelos de mezcla gaussianos. 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/

Respondido el 10 de Septiembre de 10 a las 20:09

Parece que puedes hacer un poco de trampa usando el algoritmo de jetxee. Él está encontrando bien los primeros tres dedos del pie, y deberías poder adivinar dónde se basa el cuarto.

Respondido el 10 de Septiembre de 10 a las 18:09

Interesante problema. La solución que probaría es la siguiente.

  1. Aplique un filtro de paso bajo, como una convolución con una máscara gaussiana 2D. Esto le dará un montón de valores (probablemente, pero no necesariamente de punto flotante).

  2. Realice una supresión no máxima 2D 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 varios 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 (cambio medio, redes neuronales, etc.) probablemente funcionarán hasta cierto punto, pero son demasiado complicadas y probablemente no sean ideales.

Respondido el 10 de Septiembre de 10 a las 23:09

No tengo experiencia con matrices de convolución y filtros gaussianos, ¿le gustaría mostrar cómo funcionaría en mi ejemplo? - Ivo Flipse

Bueno, aquí hay un código simple y no terriblemente eficiente, pero para este tamaño de conjunto de datos está bien.

import numpy as np
grid = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0.4,0.4,0.4,0,0,0],
              [0,0,0,0,0.4,1.4,1.4,1.8,0.7,0,0,0,0,0],
              [0,0,0,0,0.4,1.4,4,5.4,2.2,0.4,0,0,0,0],
              [0,0,0.7,1.1,0.4,1.1,3.2,3.6,1.1,0,0,0,0,0],
              [0,0.4,2.9,3.6,1.1,0.4,0.7,0.7,0.4,0.4,0,0,0,0],
              [0,0.4,2.5,3.2,1.8,0.7,0.4,0.4,0.4,1.4,0.7,0,0,0],
              [0,0,0.7,3.6,5.8,2.9,1.4,2.2,1.4,1.8,1.1,0,0,0],
              [0,0,1.1,5,6.8,3.2,4,6.1,1.8,0.4,0.4,0,0,0],
              [0,0,0.4,1.1,1.8,1.8,4.3,3.2,0.7,0,0,0,0,0],
              [0,0,0,0,0,0.4,0.7,0.4,0,0,0,0,0,0]])

arr = []
for i in xrange(grid.shape[0] - 1):
    for j in xrange(grid.shape[1] - 1):
        tot = grid[i][j] + grid[i+1][j] + grid[i][j+1] + grid[i+1][j+1]
        arr.append([(i,j),tot])

best = []

arr.sort(key = lambda x: x[1])

for i in xrange(5):
    best.append(arr.pop())
    badpos = set([(best[-1][0][0]+x,best[-1][0][1]+y)
                  for x in [-1,0,1] for y in [-1,0,1] if x != 0 or y != 0])
    for j in xrange(len(arr)-1,-1,-1):
        if arr[j][0] in badpos:
            arr.pop(j)


for item in best:
    print grid[item[0][0]:item[0][0]+2,item[0][1]:item[0][1]+2]

Básicamente, hago una matriz con la posición de la parte 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 contención, lo coloco en el best matriz, y elimine todos los demás cuadrados de 2x2 que usaron cualquier parte de este, simplemente eliminó el cuadrado de 2x2.

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 2x2 elegibles con una suma mayor (y tienen una suma igual a El uno al otro). Uno de ellos sigue seleccionando 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 le gustaría, pero esto puede requerir que se utilicen otras ideas para obtener lo que realmente desea todo el tiempo.

Respondido el 10 de Septiembre de 10 a las 15:09

Creo que sus resultados son los mismos que los de la respuesta de @ Jextee. O al menos eso me parece que lo probé. - Ivo Flipse

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 mejores cuadrados que no se crucen con ninguno de los cuadrados encontrados anteriormente.

Respondido el 10 de Septiembre de 10 a las 14:09

De hecho, hice una lista con todas las sumas de 2x2, pero cuando las ordené, no tenía idea de cómo compararlas iterativamente. Mi problema fue que cuando lo clasifiqué, perdí la pista de las coordenadas. Quizás podría meterlos en un diccionario, con las coordenadas como clave. - Ivo Flipse

Sí, sería necesario algún tipo de diccionario. Habría asumido que su representación de la cuadrícula ya es una especie de diccionario. - johannes charra

Bueno, la imagen que ves arriba es una matriz numerosa. El resto se almacena actualmente en listas multidimensionales. Probablemente sería mejor dejar de hacer eso, aunque no estoy tan familiarizado con la iteración de diccionarios. Ivo Flipse

No estoy seguro de que esto responda a la pregunta, pero parece que puedes buscar los n picos más altos que no tengan vecinos.

Aquí está la esencia. Tenga en cuenta que está en Ruby, pero la idea debería ser clara.

require 'pp'

NUM_PEAKS = 5
NEIGHBOR_DISTANCE = 1

data = [[1,2,3,4,5],
        [2,6,4,4,6],
        [3,6,7,4,3],
       ]

def tuples(matrix)
  tuples = []
  matrix.each_with_index { |row, ri|
    row.each_with_index { |value, ci|
      tuples << [value, ri, ci]
    }
  }
  tuples
end

def neighbor?(t1, t2, distance = 1)
  [1,2].each { |axis|
    return false if (t1[axis] - t2[axis]).abs > distance
  }
  true
end

# convert the matrix into a sorted list of tuples (value, row, col), highest peaks first
sorted = tuples(data).sort_by { |tuple| tuple.first }.reverse

# the list of peaks that don't have neighbors
non_neighboring_peaks = []

sorted.each { |candidate|
  # always take the highest peak
  if non_neighboring_peaks.empty?
    non_neighboring_peaks << candidate
    puts "took the first peak: #{candidate}"
  else
    # check that this candidate doesn't have any accepted neighbors
    is_ok = true
    non_neighboring_peaks.each { |accepted|
      if neighbor?(candidate, accepted, NEIGHBOR_DISTANCE)
        is_ok = false
        break
      end
    }
    if is_ok
      non_neighboring_peaks << candidate
      puts "took #{candidate}"
    else
      puts "denied #{candidate}"
    end
  end
}

pp non_neighboring_peaks

contestado el 07 de mayo de 14 a las 16:05

Voy a intentar echar un vistazo y ver si puedo convertirlo a código Python :-) - Ivo Flipse

Incluya el código en la publicación en sí, en lugar de vincularlo a una esencia, si tiene una longitud razonable. - agf

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 actualmente.

No se alarme si no es 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 de mala educación repetir aquí su breve código de muestra].

A continuación se proporciona una lista incompleta y ligeramente sesgada de técnicas / paquetes / enlaces que podrían 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 funcionará o no].

Extractor de fuente https://www.astromatic.net/software/sextractor

multinido https://github.com/farhanferoz/MultiNest [+ pyMultiNest]

Desafío de búsqueda de fuentes de ASKAP / EMU: https://arxiv.org/abs/1509.03931

También puede buscar desafíos de extracción de origen de Planck y / o WMAP.

...

Respondido 08 Feb 19, 09:02

¿Qué pasa si avanza paso a paso? Primero ubica 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 la siguiente.

Respondido el 10 de Septiembre de 10 a las 14:09

Hmmm, ese ajuste a cero al menos lo eliminaría de cualquier cálculo adicional, eso sería útil. - Ivo Flipse

En lugar de establecerlo en cero, puede calcular una función gaussiana con parámetros seleccionados a mano y restar los valores encontrados de las lecturas de presión originales. Entonces, si el dedo del pie está presionando sus sensores, entonces al encontrar el punto de presión más alto, lo usa para disminuir el efecto de ese dedo en los sensores, eliminando así las celdas vecinas con valores de presión altos. en.wikipedia.org/wiki/File:Gaussian_2d.png - Danyar

¿Le importaría mostrar un ejemplo basado en mis datos de muestra @Daniyar? Como realmente no estoy familiarizado con este tipo de procesamiento de datos, Ivo Flipse

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.