Frage Peak-Erkennung in einem 2D-Array


Ich helfe einer Tierklinik, Druck unter einer Hundetatze zu messen. Ich benutze Python für meine Datenanalyse und jetzt stecke ich fest, die Pfoten in (anatomische) Unterbereiche zu teilen.

Ich habe für jede Pfote ein 2D-Array erstellt, das aus den maximalen Werten für jeden Sensor besteht, der im Laufe der Zeit von der Pfote geladen wurde. Hier ist ein Beispiel für eine Pfote, bei der ich mit Excel die Bereiche gezeichnet habe, die ich "erkennen" möchte. Dies sind 2 mal 2 Boxen um den Sensor mit lokalen Maxima, die zusammen die größte Summe haben.

alt text

Also habe ich ein wenig experimentiert und beschlossen, einfach nach dem Maximum jeder Spalte und Reihe zu suchen (kann aufgrund der Form der Pfote nicht in eine Richtung schauen). Dies scheint die Position der einzelnen Zehen ziemlich gut zu "erkennen", aber es markiert auch benachbarte Sensoren.

alt text

Was wäre der beste Weg, Python zu sagen, welches dieser Maxima ich möchte?

Hinweis: Die 2x2 Quadrate können nicht überlappen, da sie separate Zehen sein müssen!

Auch ich habe 2x2 als Annehmlichkeit genommen, jede weiter fortgeschrittene Lösung ist willkommen, aber ich bin einfach ein menschlicher Bewegungswissenschaftler, also bin ich weder ein echter Programmierer noch ein Mathematiker, also bitte behalte es "einfach".

Hier ist ein Version, mit der geladen werden kann np.loadtxt


Ergebnisse

Also habe ich @ Jextee's Lösung ausprobiert (siehe untenstehende Ergebnisse). Wie Sie sehen können, wirkt es sehr auf die Vorderpfoten, aber es funktioniert weniger gut für die Hinterbeine.

Genauer gesagt kann es den kleinen Peak, der der vierte Zeh ist, nicht erkennen. Dies ist offensichtlich der Tatsache zu eigen, dass die Schleife von oben nach unten auf den niedrigsten Wert schaut, ohne zu berücksichtigen, wo dies ist.

Würde jemand wissen, wie @ Jextee Algorithmus zu optimieren, so dass es in der Lage sein könnte, die 4. Zehe zu finden?

alt text

Da ich noch keine weiteren Versuche bearbeitet habe, kann ich keine weiteren Proben liefern. Aber die Daten, die ich vorher gab, waren die Durchschnittswerte jeder Pfote. Diese Datei ist ein Array mit den maximalen Daten von 9 Pfoten in der Reihenfolge, in der sie mit der Platte in Kontakt kamen.

Dieses Bild zeigt, wie sie räumlich über die Platte verteilt waren.

alt text

Aktualisieren:

Ich habe einen Blog für alle Interessierten eingerichtet und Ich habe ein SkyDrive mit allen Rohmessungen eingerichtet. Also für jeden, der mehr Daten anfordert: mehr Power für Sie!


Neues Update:

Also nach der Hilfe habe ich meine Fragen zu bekommen Pfotenerkennung und Pfoten sortierenEndlich konnte ich die Zeheerkennung für jede Pfote kontrollieren! Es stellt sich heraus, dass es nicht so gut funktioniert, außer Pfoten, die so groß sind wie die in meinem eigenen Beispiel. Natürlich ist es im Nachhinein meine eigene Schuld, das 2x2 so beliebig zu wählen.

Hier ist ein schönes Beispiel, wo es schief geht: Ein Nagel wird als Zehe erkannt und die "Ferse" ist so breit, dass sie zweimal erkannt wird!

alt text

Die Pfote ist zu groß, so dass eine 2x2 Größe ohne Überlappung dazu führt, dass einige Zehen zweimal erkannt werden. Andersherum findet man bei kleinen Hunden häufig keine 5. Zehe, was vermutlich auf den zu großen 2x2-Bereich zurückzuführen ist.

Nach versuche die aktuelle Lösung bei all meinen Messungen Ich kam zu der erstaunlichen Schlussfolgerung, dass es für fast alle meine kleinen Hunde keinen 5. Zeh gefunden hat und dass es bei über 50% der Schläge für die großen Hunde mehr finden würde!

Also muss ich es klar ändern. Meine eigene Vermutung war die Größe der ändern neighborhood zu etwas kleiner für kleine Hunde und größer für große Hunde. Aber generate_binary_structure würde mich nicht die Größe des Arrays ändern lassen.

Daher hoffe ich, dass jemand anderes einen besseren Vorschlag hat, um die Zehen zu lokalisieren, vielleicht, dass der Zehenbereich mit der Pfotengröße skaliert ist?


735
2017-09-11 03:38


Ursprung


Antworten:


Ich erkannte die Peaks mit a lokaler maximaler Filter. Hier ist das Ergebnis auf Ihrem ersten Datensatz von 4 Pfoten: Peaks detection result

Ich habe es auch auf dem zweiten Datensatz von 9 Pfoten und es hat auch funktioniert.

Hier ist, wie Sie es machen:

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

Alles, was Sie danach tun müssen, ist die Verwendung scipy.ndimage.measurements.label auf der Maske, um alle unterschiedlichen Objekte zu kennzeichnen. Dann wirst du in der Lage sein, mit ihnen einzeln zu spielen.

Hinweis dass die Methode gut funktioniert, weil der Hintergrund nicht laut ist. Wenn dies der Fall wäre, würden Sie eine Reihe anderer unerwünschter Spitzen im Hintergrund entdecken. Ein weiterer wichtiger Faktor ist die Größe der Gegend. Sie müssen es anpassen, wenn sich die Peakgröße ändert (die sollte ungefähr proportional bleiben).


257
2017-09-10 14:09



Lösung

Datendatei: pawtxt. Quellcode:

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"

Ausgabe ohne überlappende Quadrate. Es scheint, dass die gleichen Bereiche wie in Ihrem Beispiel ausgewählt sind.

Einige Kommentare

Der schwierige Teil besteht darin, Summen aller 2x2 Quadrate zu berechnen. Ich nahm an, dass Sie alle brauchen, also könnte es einige Überschneidungen geben. Ich habe Slices benutzt, um die ersten / letzten Spalten und Zeilen aus dem ursprünglichen 2D-Array zu schneiden und sie dann alle zusammen zu überlappen und Summen zu berechnen.

Um es besser zu verstehen, Abbildung eines 3x3-Arrays:

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

Dann kannst du seine Scheiben nehmen:

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

Stellen Sie sich nun vor, Sie stapeln sie übereinander und summieren Elemente an den gleichen Positionen. Diese Summen sind genau die gleichen Summen über die 2x2 Quadrate mit der oberen linken Ecke in der gleichen Position:

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

Wenn Sie die Summen über 2x2 Quadrate haben, können Sie verwenden max um das Maximum zu finden, oder sort, oder sorted um die Gipfel zu finden.

Zur Erinnerung an die Positionen der Peaks kopple ich jeden Wert (die Summe) mit seiner Ordinalposition in einem abgeflachten Array (siehe zip). Dann berechne ich die Zeilen- / Spaltenposition erneut, wenn ich die Ergebnisse drucke.

Anmerkungen

Ich ließ die 2x2 Quadrate überlappen. Die bearbeitete Version filtert einige von ihnen heraus, so dass nur nicht überlappende Quadrate in den Ergebnissen erscheinen.

Finger auswählen (eine Idee)

Ein anderes Problem ist, wie man wählt, was wahrscheinlich Finger von allen Spitzen ist. Ich habe eine Idee, die funktionieren kann oder auch nicht. Ich habe keine Zeit, es jetzt zu implementieren, also nur Pseudocode.

Ich bemerkte, dass, wenn die vorderen Finger fast auf einem perfekten Kreis bleiben, der hintere Finger innerhalb dieses Kreises sein sollte. Außerdem sind die vorderen Finger mehr oder weniger gleichmäßig beabstandet. Wir könnten versuchen, diese heuristischen Eigenschaften zu verwenden, um die Finger zu erkennen.

Pseudocode:

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

Dies ist ein Brute-Force-Ansatz. Wenn N relativ klein ist, dann denke ich, dass es machbar ist. Für N = 12 gibt es C_12 ^ 5 = 792 Kombinationen, mal 5 Möglichkeiten, einen hinteren Finger auszuwählen, also 3960 Fälle für jede Pfote zu bewerten.


40
2017-09-10 14:54



Das ist ein Bildregistrierungsproblem. Die allgemeine Strategie ist:

  • Habe ein bekanntes Beispiel oder irgendeine Art von vorher auf den Daten.
  • Passen Sie Ihre Daten an das Beispiel an oder passen Sie das Beispiel an Ihre Daten an.
  • Es hilft, wenn Ihre Daten sind grob in erster Linie ausgerichtet.

Hier ist ein ungefährer Ansatz"Das dümmste Ding, das möglicherweise funktionieren könnte":

  • Beginnen Sie mit fünf Zehenkoordinaten ungefähr an der Stelle, die Sie erwarten.
  • Mit jedem, iterativ auf den Gipfel des Hügels klettern. d. h. bei der aktuellen Position, bewegen Sie sich zum maximalen benachbarten Pixel, wenn sein Wert größer als das aktuelle Pixel ist. Stoppen Sie, wenn sich Ihre Zehenkoordinaten nicht mehr bewegen.

Um dem Orientierungsproblem entgegenzuwirken, könnten Sie etwa 8 Grundeinstellungen für die Grundrichtungen (Norden, Nordosten usw.) haben. Führen Sie jeden einzeln aus und werfen Sie alle Ergebnisse weg, bei denen zwei oder mehr Zehen am selben Pixel enden. Ich werde darüber noch etwas nachdenken, aber diese Art der Sache wird immer noch in der Bildverarbeitung erforscht - es gibt keine richtigen Antworten!

Etwas komplexere Idee: (gewichtete) K-Means Clustering. Ist doch nicht so schlimm.

  • Beginnen Sie mit fünf Zehenkoordinaten, aber jetzt sind dies "Clusterzentren".

Dann iteriere bis zur Konvergenz:

  • Weisen Sie jedes Pixel dem nächsten Cluster zu (erstellen Sie einfach eine Liste für jeden Cluster).
  • Berechnen Sie den Massenschwerpunkt jedes Clusters. Für jeden Cluster ist dies: Summe (Koordinate * Intensitätswert) / Summe (Koordinate)
  • Verschiebe jeden Cluster zum neuen Schwerpunkt.

Diese Methode wird mit ziemlicher Sicherheit viel bessere Ergebnisse liefern, und Sie erhalten die Masse jedes Clusters, die beim Identifizieren der Zehen helfen kann.

(Auch hier haben Sie die Anzahl der Cluster angegeben. Beim Clustering müssen Sie die Dichte auf die eine oder andere Weise angeben: Wählen Sie entweder die Anzahl der Cluster, die in diesem Fall geeignet ist, oder wählen Sie einen Clusterradius und sehen Sie, wie viele Sie beenden up mit. Ein Beispiel für Letzteres ist Mittelschicht.)

Entschuldigung wegen fehlender Implementierungsdetails oder anderer Details. Ich würde das programmieren, aber ich habe eine Frist. Wenn bis nächste Woche nichts mehr funktioniert hat, lassen Sie es mich wissen und ich werde es versuchen.


26
2017-09-10 22:49



Dieses Problem wurde von Physikern eingehend untersucht. Es gibt eine gute Implementierung in WURZEL. Schaue auf die TSpektrum Klassen (besonders TSpektrum2 für Ihren Fall) und die Dokumentation für sie.

Verweise:

  1. M. Morhac et al .: Hintergrundeliminationsverfahren für multidimensionale Koinzidenz-Gammastrahlenspektren. Nukleare Instrumente und Methoden in der Physikforschung A 401 (1997) 113-132.
  2. M. Morhac et al .: Effiziente ein- und zweidimensionale Goldentfaltung und ihre Anwendung auf die Zersetzung von Gammastrahlenspektren. Nukleare Instrumente und Methoden in der Physikforschung A 401 (1997) 385-408.
  3. M. Morhac et al .: Identifizierung von Peaks in multidimensionalen Koinzidenz-Gammastrahlenspektren. Nukleare Instrumente und Methoden in der Forschungsphysik A 443 (2000), 108-125.

... und für diejenigen, die keinen Zugang zu einem Abonnement für NIM haben:


12
2017-09-10 12:38



Nur ein paar Ideen von ganz oben:

  • Nimm den Gradient (Ableitung) des Scans, schau, ob das die falschen Aufrufe beseitigt
  • Nimm das Maximum der lokalen Maxima

Vielleicht möchten Sie auch einen Blick darauf werfen OpenCVEs hat eine ziemlich gute Python API und könnte einige nützliche Funktionen haben.


9
2017-09-10 13:05



Hier ist eine Idee: Sie berechnen den (diskreten) Laplace-Operator des Bildes. Ich würde erwarten, dass es (negativ und) groß bei Maxima ist, in einer Weise, die dramatischer ist als in den ursprünglichen Bildern. So könnten Maxima leichter zu finden sein.

Hier ist eine andere Idee: Wenn Sie die typische Größe der Hochdruckflecken kennen, können Sie zuerst Ihr Bild glätten, indem Sie es mit einem Gaußschen der gleichen Größe falten. Dadurch können Sie einfachere Bilder verarbeiten.


9
2017-09-11 01:07



Danke für die Rohdaten. Ich bin im Zug und das ist soweit ich gekommen bin (mein Stopp steht vor der Tür). Ich massiere deine TXT-Datei mit Regexps und habe sie in eine HTML-Seite mit Javascript für die Visualisierung geplumpst. Ich teile es hier mit, weil einige, wie ich, es vielleicht besser hacken als Python.

Ich denke, ein guter Ansatz wird skalier- und rotationsinvariant sein, und mein nächster Schritt wird die Untersuchung von Mischungen von Gaussianern sein. (jedes Pfotenfeld ist das Zentrum eines Gaußschen).

    <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



Physiker Lösung:
Definieren Sie 5 Pfotenmarkierungen, die durch ihre Positionen identifiziert werden X_i und initiiere sie mit zufälligen Positionen. Definieren Sie eine Energiefunktion, die einen Preis für die Position von Markern in Pfotenpositionen mit einer gewissen Strafe für die Überlappung von Markern kombiniert; sagen wir:

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

(S(X_i) ist die mittlere Kraft in 2x2 Quadrat herum X_i, alfa ist ein experimentell maximierter Parameter)

Jetzt ist es an der Zeit, eine Metropolis-Hastings-Magie zu machen:
  1. Wählen Sie einen zufälligen Marker und bewegen Sie ihn um ein Pixel in zufälliger Richtung.
  2. Berechnen Sie dE, die Differenz der Energie, die diese Bewegung verursachte.
  3. Erhalte eine einheitliche Zufallszahl von 0-1 und nenne sie r.
  4. Wenn dE<0 oder exp(-beta*dE)>rAkzeptiere den Zug und gehe zu 1; Wenn nicht, rückgängig machen und zu 1 gehen.
Dies sollte wiederholt werden, bis die Marker zu den Pfoten konvergieren. Beta steuert das Scannen, um den Kompromiss zu optimieren, daher sollte es auch experimentell optimiert werden; es kann auch mit der Zeit der Simulation (Simulated Annealing) ständig erhöht werden.


6
2017-09-10 18:21



Mit persistenter Homologie zur Analyse Ihres Datensatzes erhalte ich folgendes Ergebnis (zum Vergrößern anklicken):

  Result

Dies ist die 2D-Version des hier beschriebenen Peak-Detection-Verfahrens SO Antwort. Die obige Abbildung zeigt einfach 0-dimensionale persistente Homologieklassen, sortiert nach Persistenz.

Ich habe den ursprünglichen Datensatz mit scipy.misc.imresize () um den Faktor 2 hochskaliert. Beachten Sie jedoch, dass ich die vier Pfoten als einen Datensatz betrachtet habe; Aufteilen in vier würde das Problem erleichtern.

Methodik. Die Idee dahinter ist ganz einfach: Betrachten Sie den Funktionsgraphen der Funktion, der jedem Pixel seinen Pegel zuweist. Es sieht aus wie das:

3D function graph

Betrachten wir nun einen Wasserstand in der Höhe 255, der kontinuierlich auf niedrigere Niveaus abfällt. Auf lokalen Maxima Inseln Pop Up (Geburt). An Sattelpunkten verschmelzen zwei Inseln; Wir betrachten die untere Insel als die höhere Insel (Tod). Das sogenannte Persistenzdiagramm (der 0-dimensionalen Homologieklassen, unsere Inseln) zeigt die Todeswerte aller Inseln:

Persistence diagram

Das Beharrlichkeit von einer Insel ist dann der Unterschied zwischen der Geburts- und Todesstufe; der vertikale Abstand eines Punktes zur grauen Hauptdiagonalen. Die Abbildung kennzeichnet die Inseln durch Verringerung der Persistenz.

Das erste Bild zeigt die Geburtsorte der Inseln. Diese Methode gibt nicht nur die lokalen Maxima an, sondern quantifiziert auch ihre "Signifikanz" durch die oben erwähnte Persistenz. Man würde dann alle Inseln mit einer zu geringen Persistenz ausfiltern. In Ihrem Beispiel ist jedoch jede Insel (d. H. Jedes lokale Maximum) ein Gipfel, nach dem Sie suchen.

Python-Code kann gefunden werden Hier.


6
2017-09-10 18:33



Hier ist ein weiterer Ansatz, den ich verwendet habe, um etwas ähnliches für ein großes Teleskop zu tun:

1) Suche nach dem höchsten Pixel. Sobald Sie das haben, suchen Sie nach der besten Anpassung für 2x2 (vielleicht die 2x2 Summe maximierend), oder tun Sie eine 2d Gaussian Anpassung innerhalb der Subregion von sagen 4x4 zentriert auf dem höchsten Pixel.

Stellen Sie dann diese 2x2 Pixel, die Sie gefunden haben, auf Null (oder vielleicht 3x3) um die Peakmitte ein

gehe zurück zu 1) und wiederhole, bis die höchste Spitze unter eine Lärmschwelle fällt, oder du hast alle Zehen, die du brauchst


5
2017-09-10 19:05



Es ist wahrscheinlich wert, mit neuronalen Netzen zu versuchen, wenn Sie in der Lage sind, einige Trainingsdaten zu erstellen ... aber das erfordert viele von Hand kommentierte Proben.


5
2017-09-10 19:19