Question Détection de crête dans un tableau 2D


J'aide une clinique vétérinaire à mesurer la pression sous une patte de chien. J'utilise Python pour mon analyse de données et maintenant je suis coincé en essayant de diviser les pattes en sous-régions (anatomiques).

J'ai fait un tableau 2D de chaque patte, qui se compose des valeurs maximales pour chaque capteur qui a été chargé par la patte au fil du temps. Voici un exemple d'une patte, où j'ai utilisé Excel pour dessiner les zones que je veux «détecter». Ce sont 2 par 2 boîtes autour du capteur avec des maxima locaux, qui ensemble ont la plus grande somme.

alt text

J'ai donc essayé d'expérimenter et j'ai décidé de simplement rechercher les maximums de chaque colonne et rangée (impossible de regarder dans une direction en raison de la forme de la patte). Cela semble «détecter» assez bien l'emplacement des orteils séparés, mais il marque également les capteurs voisins.

alt text

Alors, quelle serait la meilleure façon de dire à Python quels sont ces maximums que je veux?

Remarque: Les carrés 2x2 ne peuvent pas se chevaucher, car ils doivent être séparés des orteils!

J'ai aussi pris 2x2 pour plus de commodité, n'importe quelle solution plus avancée est la bienvenue, mais je suis simplement un scientifique du mouvement humain, donc je ne suis ni un vrai programmeur ni un mathématicien, alors s'il vous plaît gardez le 'simple'.

Voici un version qui peut être chargée avec np.loadtxt


Résultats

J'ai donc essayé la solution de @ jextee (voir les résultats ci-dessous). Comme vous pouvez le voir, cela fonctionne très bien sur les pattes avant, mais cela fonctionne moins bien pour les pattes arrière.

Plus précisément, il ne peut pas reconnaître le petit pic qui est le quatrième orteil. Ceci est évidemment inhérent au fait que la boucle regarde vers le bas vers la valeur la plus basse, sans tenir compte de l'endroit où c'est.

Est-ce que quelqu'un pourrait savoir comment modifier l'algorithme de @ jextee, afin qu'il puisse être capable de trouver le 4ème orteil aussi?

alt text

Comme je n'ai pas encore traité d'autres essais, je ne peux pas fournir d'autres échantillons. Mais les données que j'ai données auparavant étaient les moyennes de chaque patte. Ce fichier est un tableau avec les données maximales de 9 pattes dans l'ordre où elles ont été en contact avec la plaque.

Cette image montre comment ils ont été répartis dans l'espace sur la plaque.

alt text

Mettre à jour:

J'ai mis en place un blog pour toute personne intéressée et J'ai installé un SkyDrive avec toutes les mesures brutes. Donc, à quiconque demande plus de données: plus de puissance pour vous!


Nouvelle mise à jour:

Donc, après l'aide que j'ai eu avec mes questions concernant détection de la patte et tri des pattes, J'étais enfin capable de vérifier la détection des orteils pour chaque patte! Il s'avère que ça ne marche pas aussi bien que des pattes de la taille de mon exemple. Bien sûr, avec le recul, c'est ma faute si je choisis le 2x2 de manière aussi arbitraire.

Voici un bon exemple de ce qui ne va pas: un ongle est reconnu comme un orteil et le 'talon' est si large, il est reconnu deux fois!

alt text

La patte est trop grande, donc en prenant une taille de 2x2 sans chevauchement, certains orteils sont détectés deux fois. Dans l'autre sens, chez les petits chiens, il échoue souvent à trouver un cinquième orteil, que je soupçonne être causé par la zone 2x2 étant trop grande.

Après essayer la solution actuelle sur toutes mes mesures J'en suis arrivé à la conclusion stupéfiante que pour presque tous mes petits chiens il ne trouvait pas de 5ème orteil et que dans plus de 50% des impacts pour les gros chiens il trouverait plus!

Donc, clairement, je dois le changer. Ma propre estimation était de changer la taille de la neighborhood à quelque chose de plus petit pour les petits chiens et plus grand pour les grands chiens. Mais generate_binary_structure ne me laisserait pas changer la taille du tableau.

Par conséquent, j'espère que quelqu'un d'autre a une meilleure suggestion pour localiser les orteils, peut-être avoir l'échelle de la zone des orteils avec la taille de la patte?


735
2017-09-11 03:38


origine


Réponses:


J'ai détecté les pics en utilisant un filtre maximum local. Voici le résultat sur votre premier ensemble de 4 pattes: Peaks detection result

Je l'ai également couru sur le deuxième ensemble de données de 9 pattes et ça a fonctionné aussi.

Voici comment vous le faites:

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()

Tout ce que vous devez faire après est l'utilisation scipy.ndimage.measurements.label sur le masque pour étiqueter tous les objets distincts. Ensuite, vous pourrez jouer avec eux individuellement.

Remarque que la méthode fonctionne bien car l'arrière-plan n'est pas bruyant. Si c'était le cas, vous détecteriez un tas d'autres pics indésirables en arrière-plan. Un autre facteur important est la taille de quartier. Vous devrez l'ajuster si la taille du pic change (le devrait rester à peu près proportionnel).


257
2017-09-10 14:09



Solution

Fichier de données: paw.txt. Code source:

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"

Sortie sans chevauchement des carrés. Il semble que les mêmes zones sont sélectionnées comme dans votre exemple.

Certains commentaires

La partie difficile consiste à calculer la somme de tous les carrés de 2 x 2. Je suppose que vous avez besoin de tous, donc il pourrait y avoir des chevauchements. J'ai utilisé des tranches pour couper les premières / dernières colonnes et lignes du tableau 2D original, puis les superposer et calculer les sommes.

Pour mieux le comprendre, imageant un tableau 3x3:

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

Ensuite, vous pouvez prendre ses tranches:

>>> 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]])

Imaginez maintenant que vous les empilez les uns au-dessus des autres et que vous additionnez des éléments aux mêmes positions. Ces sommes seront exactement les mêmes sommes sur les carrés 2x2 avec le coin supérieur gauche dans la même position:

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

Lorsque vous avez les sommes sur 2x2 carrés, vous pouvez utiliser max pour trouver le maximum, ou sort, ou sorted pour trouver les pics.

Pour mémoriser les positions des pics, je couple chaque valeur (la somme) avec sa position ordinale dans un tableau aplati (voir zip). Ensuite, je calcule à nouveau la position rangée / colonne lorsque j'imprime les résultats.

Remarques

J'ai laissé chevaucher les carrés 2x2. La version modifiée élimine certains d'entre eux de sorte que seuls les carrés sans chevauchement apparaissent dans les résultats.

Choisir les doigts (une idée)

Un autre problème est de savoir comment choisir les doigts susceptibles de sortir de tous les pics. J'ai une idée qui peut ou peut ne pas fonctionner. Je n'ai pas le temps de l'implémenter maintenant, donc juste du pseudo-code.

J'ai remarqué que si les doigts avant restent sur un cercle presque parfait, le doigt arrière devrait être à l'intérieur de ce cercle. En outre, les doigts avant sont plus ou moins équidistants. Nous pouvons essayer d'utiliser ces propriétés heuristiques pour détecter les doigts.

Pseudo code:

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

C'est une approche de force brute. Si N est relativement petit, alors je pense que c'est faisable. Pour N = 12, il y a C_12 ^ 5 = 792 combinaisons, multiplié par 5 façons de sélectionner un doigt arrière, soit 3960 cases à évaluer pour chaque patte.


40
2017-09-10 14:54



C'est un problème d'enregistrement d'image. La stratégie générale est:

  • Avoir un exemple connu, ou une sorte de avant sur les données.
  • Ajustez vos données à l'exemple ou adaptez l'exemple à vos données.
  • Cela aide si vos données sont grossièrement aligné en premier lieu.

Voici une approche approximative, "la chose la plus stupide qui pourrait fonctionner":

  • Commencez avec cinq coordonnées d'orteil dans à peu près l'endroit que vous attendez.
  • Avec chacun, grimper itérativement au sommet de la colline. c'est-à-dire donné la position actuelle, déplacez-vous au pixel maximum voisin, si sa valeur est supérieure au pixel courant. Arrêtez-vous lorsque les coordonnées de vos orteils ont cessé de bouger.

Pour contrer le problème d'orientation, vous pouvez avoir environ 8 réglages initiaux pour les directions de base (Nord, Nord-Est, etc.). Exécutez chacun individuellement et jetez tous les résultats où deux orteils ou plus se retrouvent au même pixel. Je vais y réfléchir un peu plus, mais ce genre de chose est encore à l'étude dans le traitement d'image - il n'y a pas de bonnes réponses!

Idée légèrement plus complexe: (pondérée) K-means clustering. C'est pas si mal.

  • Commencez avec cinq coordonnées d'orteil, mais maintenant ce sont des "centres de cluster".

Puis itérer jusqu'à convergence:

  • Affectez chaque pixel au cluster le plus proche (créez simplement une liste pour chaque cluster).
  • Calculer le centre de masse de chaque groupe. Pour chaque cluster, il s'agit de: Somme (coordonnée * valeur d'intensité) / Somme (coordonnée)
  • Déplacez chaque cluster vers le nouveau centre de masse.

Cette méthode donnera presque certainement de meilleurs résultats, et vous obtiendrez la masse de chaque groupe qui peut aider à identifier les orteils.

(Encore une fois, vous avez spécifié le nombre de clusters à l'avant.Avec le clustering, vous devez spécifier la densité d'une façon ou d'une autre: soit choisissez le nombre de clusters, approprié dans ce cas, soit choisissez un rayon et voyez combien vous terminez avec un exemple de ce dernier déplacement moyen.)

Désolé pour le manque de détails de mise en œuvre ou d'autres détails. Je coderais ceci mais j'ai une date limite. Si rien d'autre n'a fonctionné la semaine prochaine, faites le moi savoir et je vais essayer.


26
2017-09-10 22:49



Ce problème a été étudié en profondeur par les physiciens. Il y a une bonne mise en œuvre dans RACINE. Regarde le TSpectrum classes (en particulier TSpectrum2 pour votre cas) et la documentation pour eux.

Les références:

  1. M. Morhac et al .: Méthodes d'élimination de fond pour les spectres de rayons gamma à coïncidence multidimensionnelle. Instruments et méthodes nucléaires en physique Recherche A 401 (1997) 113-132.
  2. M. Morhac et al.: La déconvolution efficace de l'or en une et deux dimensions et son application à la décomposition des spectres gamma. Instruments et méthodes nucléaires en physique Recherche A 401 (1997) 385-408.
  3. M. Morhac et al.: Identification des pics dans les spectres gamma à coïncidence multidimensionnelle. Les instruments et méthodes nucléaires en physique de la recherche A 443 (2000), 108-125.

... et pour ceux qui n'ont pas accès à un abonnement à NIM:


12
2017-09-10 12:38



Juste quelques idées sur le dessus de ma tête:

  • prendre le gradient (dérivé) de l'analyse, voir si cela élimine les faux appels
  • prendre le maximum des maxima locaux

Vous pourriez également vouloir jeter un coup d'œil à OpenCV, il a une API Python assez décente et pourrait avoir certaines fonctions que vous trouverez utiles.


9
2017-09-10 13:05



Voici une idée: vous calculez le laplacien (discret) de l'image. Je m'attendrais à ce qu'il soit (négatif et) grand aux maxima, d'une manière qui est plus dramatique que dans les images originales. Ainsi, les maxima pourraient être plus faciles à trouver.

Voici une autre idée: si vous connaissez la taille typique des points à haute pression, vous pouvez d'abord lisser votre image en la convoluant avec un gaussien de la même taille. Cela peut vous donner des images plus simples à traiter.


9
2017-09-11 01:07



merci pour les données brutes. Je suis dans le train et c'est aussi loin que je l'ai eu (mon arrêt arrive). J'ai massé votre fichier txt avec des regexps et l'ai ploppé dans une page HTML avec un peu de javascript pour la visualisation. Je partage ça ici car certains, comme moi, pourraient le trouver plus facilement piratable que python.

Je pense qu'une bonne approche sera l'invariance d'échelle et de rotation, et ma prochaine étape consistera à étudier les mélanges de gaussiennes. (chaque patte étant le centre d'un gaussien).

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

alt text


7
2017-09-10 19:24



La solution du physicien
Définir 5 pattes identifiées par leurs positions X_i et init les avec des positions aléatoires. Définir une fonction énergétique combinant un prix pour l'emplacement des marqueurs dans les positions des pattes avec une certaine punition pour le chevauchement des marqueurs; Disons:

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

(S(X_i) est la force moyenne dans le carré de 2x2 autour X_i, alfa est un paramètre à piquer expérimentalement)

Il est maintenant temps de faire de la magie de Metropolis-Hastings:
  1. Sélectionnez le marqueur aléatoire et déplacez-le d'un pixel dans le sens aléatoire.
  2. Calculez dE, la différence d'énergie provoquée par ce mouvement.
  3. Obtenez un nombre aléatoire uniforme de 0-1 et appelez-le r.
  4. Si dE<0 ou exp(-beta*dE)>r, acceptez le mouvement et allez à 1; sinon, annuler le mouvement et aller à 1.
Cela devrait être répété jusqu'à ce que les marqueurs convergent vers les pattes. La bêta contrôle la numérisation pour optimiser le compromis, elle devrait donc être optimisée aussi expérimentalement; il peut également être constamment augmenté avec le temps de simulation (recuit simulé).


6
2017-09-10 18:21



En utilisant une homologie persistante pour analyser votre ensemble de données, j'obtiens le résultat suivant (cliquez pour agrandir):

  Result

Ceci est la version 2D de la méthode de détection de pic décrite dans cette SO réponse. La figure ci-dessus montre simplement des classes d'homologie persistante 0-dimension triées par persistance.

J'ai amélioré le jeu de données d'origine par un facteur 2 en utilisant scipy.misc.imresize (). Cependant, notez que j'ai considéré les quatre pattes comme un ensemble de données; le diviser en quatre rendrait le problème plus facile.

Méthodologie. L'idée derrière cela très simple: Considérez le graphe de fonction de la fonction qui assigne à chaque pixel son niveau. Cela ressemble à ceci:

3D function graph

Considérons maintenant un niveau d'eau à la hauteur 255 qui descend continuellement aux niveaux inférieurs. Aux îles maxima locales apparaissent (naissance). Aux points de selle deux îles fusionnent; nous considérons que l'île inférieure a été fusionnée à l'île supérieure (la mort). Le diagramme dit de persistance (des classes d'homologie 0-ème dimension, nos îles) dépeint les valeurs de mort-sur-naissance de toutes les îles:

Persistence diagram

le persistance d'une île est alors la différence entre le niveau de naissance et de mort; la distance verticale d'un point à la diagonale principale grise. La figure marque les îles en diminuant la persistance.

La toute première photo montre les emplacements des naissances des îles. Cette méthode donne non seulement les maxima locaux, mais quantifie également leur «signification» par la persistance mentionnée ci-dessus. On filtrerait ensuite toutes les îles avec une persistance trop faible. Cependant, dans votre exemple, chaque île (c'est-à-dire chaque maximum local) est un pic que vous recherchez.

Le code Python peut être trouvé ici.


6
2017-09-10 18:33



Heres une autre approche que j'ai utilisée en faisant quelque chose de similaire pour un grand télescope:

1) Recherchez le pixel le plus élevé. Une fois que vous avez cela, cherchez autour de cela pour le meilleur ajustement pour 2x2 (peut-être maximiser la somme 2x2), ou faites un ajustement gaussien 2d à l'intérieur de la sous-région de 4x4 centré sur le pixel le plus élevé.

Ensuite, réglez ces 2x2 pixels que vous avez trouvés à zéro (ou peut-être 3x3) autour du centre du pic

retournez à 1) et répétez jusqu'à ce que le plus haut pic tombe en dessous d'un seuil de bruit, ou vous avez tous les orteils dont vous avez besoin


5
2017-09-10 19:05



Il est probablement utile d'essayer avec des réseaux de neurones si vous êtes capable de créer des données d'entraînement ... mais cela nécessite de nombreux exemples annotés à la main.


5
2017-09-10 19:19