web-dev-qa-db-de.com

Peakerkennung in einem 2D-Array

Ich helfe einer Tierklinik, die den Druck unter einer Hundepfote misst. Ich verwende Python für meine Datenanalyse, und jetzt versuche ich, die Pfoten in (anatomische) Subregionen zu unterteilen.

Ich habe aus jeder Pfote ein 2D-Array erstellt, das aus den Maximalwerten 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 die zu zeichnenden Bereiche mit Excel gezeichnet habe. Dies sind 2 x 2 Boxen um den Sensor mit lokalen Maximas, die zusammen die größte Summe haben.

alt text

Also habe ich ein wenig experimentiert und beschloss, einfach nach den Maxima jeder Spalte und Zeile zu suchen (kann aufgrund der Form der Pfote nicht in eine Richtung schauen). Dies scheint den Ort der einzelnen Zehen ziemlich gut zu "erkennen", markiert aber auch benachbarte Sensoren. 

alt text

Was wäre also der beste Weg, um Python mitzuteilen, welches dieser Maximum ich will? 

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

Ich habe auch 2x2 als Annehmlichkeit genommen, jede weiter fortgeschrittene Lösung ist willkommen, aber ich bin einfach ein Wissenschaftler für menschliche Bewegung, also bin ich weder ein echter Programmierer noch ein Mathematiker. 

Hier ist eine Version, die mit np.loadtxt geladen werden kann


Ergebnisse

Also habe ich die Lösung von @ jextee ausprobiert (siehe Ergebnisse unten). Wie Sie sehen, funktioniert es sehr gut an den Vorderpfoten, aber weniger gut bei den Hinterbeinen. 

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

Könnte jemand wissen, wie man den Algorithmus von @ jextee tweak, damit er vielleicht auch den 4. Zeh findet?

alt text

Da ich noch keine anderen Versuche durchgeführt habe, kann ich keine anderen Proben liefern. Aber die Daten, die ich zuvor gegeben habe, 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 wurden.

alt text

Aktualisieren:

ICH HABE EINEN BLOG FÜR ALLE INTERESSIERTEN EINGERICHTET und Ich habe ein SkyDrive mit allen Rohdaten erstellt. Für alle, die mehr Daten anfordern: mehr Leistung für Sie! 


Neues Update:

Nach der Hilfe, die ich mit meinen Fragen zu Paw Detection und Paw Sorting bekam, konnte ich endlich die Zehenerkennung für jede Paw überprüfen! Es hat sich herausgestellt, dass es nur in Pfoten mit der Größe meines eigenen Beispiels so gut funktioniert. Natürlich im Nachhinein, es ist meine eigene Schuld, dass ich den 2x2 so willkürlich gewählt habe.

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

alt text

Die Pfote ist zu groß. Wenn Sie also eine Größe von 2x2 ohne Überlappung nehmen, werden einige Zehen doppelt erkannt. Umgekehrt findet man bei kleinen Hunden oft keinen fünften Zeh, der vermutlich durch einen zu großen Bereich von 2x2 verursacht wird.

Nach versuchte die aktuelle Lösung bei all meinen Messungen kam ich zu der erstaunlichen Schlussfolgerung, dass für fast alle meine kleinen Hunde kein fünfter Zeh gefunden wurde und dass bei über 50% der Auswirkungen für die großen Hunde dies der Fall war würde mehr finden!

Also muss ich es natürlich ändern. Meine eigene Vermutung bestand darin, die Größe der neighborhood in etwas kleiner und für große Hunde größer zu ändern. Aber generate_binary_structure ließ mich die Größe des Arrays nicht ändern. 

Ich hoffe also, dass jemand anderes einen besseren Vorschlag hat, um die Zehen zu lokalisieren, vielleicht die Skalierung der Zehenfläche mit der Pfotengröße?

782
Ivo Flipse

Ich habe die Peaks mit einem local maximum filter erkannt. Hier ist das Ergebnis Ihres ersten Datensatzes von 4 Pfoten: Peaks detection result

Ich habe es auch auf dem zweiten Datensatz von 9 Pfoten ausgeführt und es hat auch gut funktioniert .

So machen Sie es:

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 scipy.ndimage.measurements.label auf der Maske, um alle verschiedenen Objekte zu kennzeichnen. Dann können Sie mit ihnen einzeln spielen.

Beachten Sie, dass die Methode gut funktioniert, da der Hintergrund nicht laut ist. Andernfalls würden Sie eine Reihe anderer unerwünschter Spitzen im Hintergrund erkennen. Ein weiterer wichtiger Faktor ist die Größe von Nachbarschaft. Sie müssen es anpassen, wenn sich die Peakgröße ändert (die sollte ungefähr proportional bleiben).

278
Ivan

Lösung

Datendatei: Paw.txt . 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 dieselben Bereiche wie in Ihrem Beispiel ausgewählt werden.

Einige Kommentare

Der schwierige Teil besteht darin, die Summen aller 2x2-Quadrate zu berechnen. Ich nahm an, dass Sie alle brauchen, also kann es zu Überschneidungen kommen. Ich habe Scheiben verwendet, um die ersten/letzten Spalten und Zeilen aus dem ursprünglichen 2D-Array zu schneiden, sie dann alle zu überlappen und die Summen zu berechnen.

Um es besser zu verstehen, stellen Sie ein 3x3-Array dar:

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

Dann können Sie 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 denselben Positionen. Diese Summen entsprechen exakt den gleichen Beträgen über den 2x2-Quadraten, wobei sich die obere linke Ecke an derselben Position befindet:

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

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

Um sich die Positionen der Peaks zu merken, koppele 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 durfte die 2x2-Quadrate überlappen. Die bearbeitete Version filtert einige von ihnen heraus, sodass in den Ergebnissen nur nicht überlappende Quadrate angezeigt werden.

Finger wählen (eine Idee)

Ein weiteres Problem ist, wie man aus allen Peaks die Finger auswählen kann. Ich habe eine Idee, die möglicherweise funktioniert. Ich habe jetzt keine Zeit, es zu implementieren, also nur Pseudo-Code.

Ich habe bemerkt, dass der hintere Finger sich innerhalb dieses Kreises befinden sollte, wenn die vorderen Finger fast auf einem perfekten Kreis bleiben. Auch die vorderen Finger sind mehr oder weniger gleich beabstandet. Wir können versuchen, diese heuristischen Eigenschaften zur Erkennung der Finger zu verwenden.

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

Dies ist ein brutaler Ansatz. Wenn N relativ klein ist, denke ich, dass es machbar ist. Für N = 12 gibt es C_12 ^ 5 = 792 Kombinationen, mal 5 Möglichkeiten, einen Hinterfinger auszuwählen, also 3960 Fälle für jede Pfote auszuwerten.

43
sastanin

Dies ist ein Problem bei der Registrierung von Bildern . Die allgemeine Strategie ist:

  • Haben Sie ein bekanntes Beispiel oder eine Art vorheriger für die Daten.
  • Passen Sie Ihre Daten an das Beispiel an oder passen Sie das Beispiel an Ihre Daten an.
  • Es ist hilfreich, wenn Ihre Daten in erster Linie ungefähr ausgerichtet sind.

Hier ist eine grobe und bereite Annäherung, "das dümmste Ding, das möglicherweise funktionieren könnte":

  • Beginnen Sie mit fünf Zehenkoordinaten, ungefähr an dem Ort, den Sie erwarten.
  • Steigen Sie mit jedem Schritt iterativ auf die Spitze des Hügels. bei gegebener aktueller Position zum maximalen benachbarten Pixel gehen, falls 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 ungefähr 8 Grundeinstellungen für die Grundrichtungen (Nord, Nordost usw.) haben. Führen Sie jeden einzeln aus und werfen Sie alle Ergebnisse weg, bei denen zwei oder mehr Zehen am selben Pixel landen. Ich werde noch etwas darüber nachdenken, aber so etwas wird in der Bildverarbeitung immer noch erforscht - es gibt keine richtigen Antworten!

Etwas komplexere Idee: (gewichtet) K-bedeutet Clustering. Ist doch nicht so schlimm.

  • Beginnen Sie mit fünf Zehenkoordinaten, aber jetzt handelt es sich um "Clusterzentren".

Dann iterieren 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)
  • Bewegen Sie jeden Cluster zum neuen Massenmittelpunkt.

Diese Methode wird fast sicher viel bessere Ergebnisse liefern und Sie erhalten die Masse jedes Clusters, die bei der Identifizierung der Zehen helfen kann.

(Wieder haben Sie die Anzahl der Cluster im Vorhinein festgelegt. 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 angemessen ist, oder wählen Sie einen Clusterradius aus, und sehen Sie, wie viele Sie beenden up mit. Ein Beispiel für letzteres ist mean-shift .)

Entschuldigung wegen fehlender Implementierungsdetails oder anderer Besonderheiten. Ich würde das kodieren, aber ich habe eine Frist. Wenn bis nächste Woche nichts anderes funktioniert hat, lass es mich wissen und ich werde es versuchen.

27
CakeMaster

Dieses Problem wurde von Physikern eingehend untersucht. Es gibt eine gute Implementierung in ROOT . Schauen Sie sich die TSpectrum -Klassen (speziell TSpectrum2 für Ihren Fall) und die Dokumentation dafür an.

Verweise:

  1. M.Morhac et al .: Hintergrundeliminierungsverfahren für mehrdimensionale Koinzidenz-Gammaspektren. Nukleare Instrumente und Methoden in der Physikforschung A 401 (1997) 113-132.
  2. M. Morhac et al .: Effiziente ein- und zweidimensionale Gold-Dekonvolution und ihre Anwendung bei der Zerlegung von Gammaspektren. Nukleare Instrumente und Methoden in der Physikforschung A 401 (1997) 385-408.
  3. M.Morhac et al .: Identifikation von Peaks in mehrdimensionalen Koinzidenz-Gammaspektren. Nukleare Instrumente und Methoden in der Forschungsphysik A 443 (2000), 108-125. 

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

12
dmckee

Unter Verwendung einer persistenten Homologie zur Analyse Ihres Datensatzes erhalte ich das folgende Ergebnis (zum Vergrößern klicken):

Result

Dies ist die 2D-Version der in dieser SO answer beschriebenen Peakerkennungsmethode. Die obige Abbildung zeigt einfach 0-dimensionale Klassen für persistente Homologie, 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. Eine Aufteilung in vier würde das Problem erleichtern.

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

3D function graph

Betrachten Sie nun einen Wasserstand in Höhe 255, der kontinuierlich auf niedrigere Niveaus abfällt. Bei lokalen Maxima-Inseln erscheint (Geburt). An Sattelpunkten vereinigen sich zwei Inseln; wir betrachten die untere insel als verschmolzen mit der oberen insel (tod). Das sogenannte Persistenzdiagramm (der 0-dimensionalen Homologieklassen, unserer Inseln) zeigt die Geburtenwerte aller Inseln:

Persistence diagram

Die Persistenz einer Insel ist dann der Unterschied zwischen dem Geburts- und dem Todesniveau; der vertikale Abstand eines Punktes zur grauen Hauptdiagonale. Die Abbildung kennzeichnet die Inseln, indem die Persistenz verringert wird.

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 herausfiltern. In Ihrem Beispiel ist jedoch jede Insel (d. H. Jedes lokale Maximum) ein Peak, nach dem Sie suchen.

Python-Code finden Sie hier .

11
S. Huber

Hier ist eine Idee: Sie berechnen den (diskreten) Laplace-Wert des Bildes. Ich würde erwarten, dass es bei Maxima (negativ und) groß ist, auf eine Weise, die dramatischer ist als in den Originalbildern. Maxima könnte daher leichter zu finden sein.

Hier eine andere Idee: Wenn Sie die typische Größe der Hochdruckpunkte kennen, können Sie Ihr Bild zunächst glätten, indem Sie es mit einem Gaußschen Gusssian derselben Größe zusammenfalten. Dadurch erhalten Sie möglicherweise einfacher zu verarbeitende Bilder.

10
Eric O Lebigot

Nur ein paar Ideen aus meinem Kopf:

  • nehmen Sie den Gradienten (Ableitung) des Scans und prüfen Sie, ob dadurch falsche Anrufe ausgeschlossen werden
  • nehmen Sie das Maximum der lokalen Maxima

Vielleicht möchten Sie auch einen Blick auf OpenCV werfen, es verfügt über eine recht anständige Python-API und einige nützliche Funktionen.

9
ChrisC

Ich bin sicher, Sie haben jetzt genug, um weiterzumachen, aber ich kann nicht umhin, die Verwendung der K-Means-Clustering-Methode vorzuschlagen. k-means ist ein unüberwachter Clustering-Algorithmus, der Daten (in einer beliebigen Anzahl von Dimensionen - ich mache das in 3D -) in k-Cluster mit eindeutigen Grenzen bringen kann. Es ist schön hier, weil Sie genau wissen, wie viele Zehen diese Eckzähne haben (sollen).

Außerdem ist es in Scipy implementiert, was wirklich schön ist ( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ). 

Hier ein Beispiel, wie 3D-Cluster räumlich aufgelöst werden können: enter image description here

Was Sie wollen, ist ein bisschen anders (2D und beinhaltet Druckwerte), aber ich denke immer noch, Sie könnten es versuchen. 

8
astromax

danke für die Rohdaten. Ich bin im Zug und das ist so weit wie ich gekommen bin (meine Haltestelle steht vor der Tür). Ich habe Ihre TXT-Datei mit Regexps massiert und in eine HTML-Seite mit etwas Javascript zur Visualisierung eingefügt. Ich teile es hier, weil einige, wie ich, es leichter hacken können als Python.

Ich denke, ein guter Ansatz wird die Skalierung und Rotationsinvariante sein, und mein nächster Schritt wird die Untersuchung von Gaußmischungen sein. (jedes Pfotenpad 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
Ron

Physikerlösung:
Definiere 5 Pfotenmarkierungen, die anhand ihrer Positionen X_i identifiziert wurden, und initiiere sie mit zufälligen Positionen . Definiere eine Energiefunktion, die eine Kombination aus Belohnungen für die Position der Marker in den Pfotenpositionen und eine gewisse Bestrafung für das Überlappen der Marker enthält. 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 um X_i, alfa ist ein experimentell zu messender Parameter.)

Nun ist es an der Zeit, Metropolis-Hastings-Magie zu spielen:
1. Wählen Sie einen Zufallsmarker aus und verschieben Sie ihn um ein Pixel in zufälliger Richtung.
2. Berechnen Sie dE, den Energieunterschied, den diese Bewegung verursacht hat.
3. Holen Sie sich eine einheitliche Zufallszahl von 0-1 und nennen Sie es r.
4. Wenn dE<0 oder exp(-beta*dE)>r, akzeptieren Sie die Verschiebung und gehen Sie zu 1; wenn nicht, mache den Zug rückgängig und gehe zu 1.
Dies sollte wiederholt werden, bis die Markierungen zu Pfoten konvergieren. Beta kontrolliert das Scannen auf das Optimieren von Kompromissen, daher sollte es auch experimentell optimiert werden. es kann auch mit der Simulationszeit ständig erhöht werden (simuliertes Glühen).

6
mbq

Hier ist ein anderer Ansatz, den ich verwendet habe, als ich mit einem großen Teleskop etwas Ähnliches machte:

1) Suchen Sie nach dem höchsten Pixel. Sobald Sie das haben, suchen Sie danach nach der besten Anpassung für 2x2 (möglicherweise durch Maximieren der 2x2-Summe) oder nehmen Sie eine 2d-Gauß-Anpassung in den Subbereich von etwa 4x4 vor, der auf dem höchsten Pixel zentriert ist.

Setzen Sie dann die gefundenen 2x2 Pixel auf Null (oder vielleicht 3x3) um die Peakmitte

gehen Sie zurück zu 1) und wiederholen Sie den Vorgang, bis der höchste Gipfel eine Geräuschschwelle unterschreitet oder Sie alle Zehen haben, die Sie benötigen

5
Paulus

eine grobe Umrisslinie ...

sie möchten möglicherweise einen Algorithmus für verbundene Komponenten verwenden, um jeden Paw-Bereich zu isolieren. Wiki hat eine anständige Beschreibung (mit etwas Code) hier: http://en.wikipedia.org/wiki/Connected_Component_Labeling

sie müssen entscheiden, ob Sie 4 oder 8 Verbundenheit verwenden möchten. Ich persönlich bevorzuge für die meisten Probleme 6-Verbundenheit. Wenn Sie jeden Pfotenabdruck als zusammenhängende Region getrennt haben, sollte es trotzdem leicht sein, die Region zu durchlaufen und die Maxima zu finden. Sobald Sie das Maximum gefunden haben, können Sie die Region iterativ vergrößern, bis Sie eine vorgegebene Schwelle erreichen, um sie als "Zehe" zu identifizieren. 

ein subtiles Problem hier ist, dass, sobald Sie mit der Anwendung von Computer Vision-Techniken beginnen, um etwas als rechte/linke/vordere/hintere Paw zu erkennen und Sie die einzelnen Zehen betrachten, müssen Sie Rotationen, Neigungen und Übersetzungen berücksichtigen. Dies wird durch die Analyse sogenannter "Momente" erreicht. Es gibt einige verschiedene Momente, die bei Visionsanwendungen zu berücksichtigen sind: 

zentrale Momente: Translationsinvariante normalisierte Momente: Skalierung und Translationsinvariante hu-Momente: Translation, Skalierung und Rotationsinvariante 

weitere Informationen zu Momenten finden Sie, wenn Sie im Wiki nach "Bild-Momenten" suchen. 

5
joshua

Es ist wahrscheinlich einen Versuch wert, mit neuronalen Netzwerken zu versuchen, wenn Sie in der Lage sind, Trainingsdaten zu erstellen. Dies erfordert jedoch viele von Hand kommentierte Proben.

5
antirez

Vielleicht können Sie so etwas wie Gaußsche Mixture Models verwenden. Hier ist ein Python-Paket für GMMs (hat gerade eine Google-Suche durchgeführt) http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/em/

4

Es scheint, dass Sie mit dem Jetxee-Algorithmus etwas betrügen können. Er findet die ersten drei Zehen in Ordnung, und Sie sollten raten können, wo der vierte davon basiert.

3
geoff

Interessantes Problem. Die Lösung, die ich versuchen würde, ist folgende.

  1. Wenden Sie einen Tiefpassfilter an, z. B. Faltung mit einer 2D-Gaußmaske. Dadurch erhalten Sie eine Reihe von Werten (wahrscheinlich, aber nicht unbedingt Fließkommazahlen).

  2. Führen Sie eine nicht maximale Unterdrückung in 2D mit dem bekannten ungefähren Radius jedes Paw-Pads (oder der Zehe) durch.

Dies sollte Ihnen die maximalen Positionen geben, ohne dass mehrere Kandidaten nahe beieinander liegen. Zur Verdeutlichung sollte der Radius der Maske in Schritt 1 auch dem in Schritt 2 verwendeten Radius ähneln. Dieser Radius kann ausgewählt werden oder der Tierarzt könnte ihn vorher explizit messen (er variiert mit dem Alter/der Rasse/usw.).

Einige der vorgeschlagenen Lösungen (Mittelwertverschiebung, neuronale Netze usw.) funktionieren wahrscheinlich bis zu einem gewissen Grad, sind jedoch zu kompliziert und wahrscheinlich nicht ideal.

3
Bob Mottram

Nun, hier ist ein einfacher und nicht sehr effizienter Code, aber für diese Größe eines Datensatzes ist dies in Ordnung.

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]

Ich mache im Grunde nur ein Array mit der Position des oberen linken und der Summe jedes 2x2-Quadrats und sortiere es nach der Summe. Ich nehme dann das 2x2-Quadrat mit der höchsten Summe aus der Konkurrenz heraus, stelle es in das best-Array und entferne alle anderen 2x2-Quadrate, die einen beliebigen Teil dieses gerade benutzten 2x2-Quadrats entfernt haben.

Es scheint gut zu funktionieren, außer mit der letzten Pfote (die mit der kleinsten Summe ganz rechts in Ihrem ersten Bild). Es stellt sich heraus, dass es zwei weitere 2x2-Quadrate mit einer größeren Summe gibt (und sie haben die gleiche Summe zu) gegenseitig). Eines von ihnen wählt immer noch ein Quadrat aus Ihrem 2x2-Quadrat aus, das andere ist jedoch nach links. Glücklicherweise sehen wir durch das Glück, dass Sie mehr von dem wählen, den Sie möchten, aber dies erfordert möglicherweise andere Ideen, um das zu bekommen, was Sie eigentlich immer wollen.

3
Justin Peel

ich möchte Ihnen nur sagen, dass es eine nette Option gibt, um lokale Maxima in Bildern mit Python zu finden.

from skimage.feature import peak_local_max

oder für die 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

2
Thomio

Ich bin nicht sicher, ob dies die Frage beantwortet, aber es scheint, als könnten Sie einfach nach den n höchsten Gipfeln suchen, die keine Nachbarn haben.

Hier ist der Gist. Beachten Sie, dass es sich um Ruby handelt, aber die Idee sollte klar sein.

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
0
Rick Hull

In der Astronomie- und Kosmologie-Community gibt es mehrere umfangreiche Software-Produkte. Dies ist ein bedeutender Forschungsbereich, sowohl historisch als auch aktuell.

Seien Sie nicht beunruhigt, wenn Sie kein Astronom sind - einige sind außerhalb des Feldes einfach zu bedienen. Sie können beispielsweise Astropie/Fotutilien verwenden:

https://photutils.readthedocs.io/de/stable/detection.html#local-peak-detection

[Es scheint etwas unhöflich, ihren kurzen Beispielcode hier zu wiederholen.]

Eine unvollständige und leicht voreingenommene Liste von Techniken/Paketen/Links, die von Interesse sein könnten, ist unten aufgeführt. Fügen Sie weitere Kommentare hinzu und ich werde diese Antwort bei Bedarf aktualisieren. Natürlich gibt es einen Kompromiss zwischen Genauigkeit und Rechenressourcen. [Ehrlich gesagt, es gibt zu viele, um Codebeispiele in einer einzigen Antwort wie dieser zu geben. Ich bin mir nicht sicher, ob diese Antwort fliegen wird oder nicht.]

Quellenextraktor https://www.astromatic.net/software/sextractor

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

ASKAP/EMU-Quellensuchherausforderung: https://arxiv.org/abs/1509.03931

Sie können auch nach Quellcode-Extraktionsherausforderungen von Planck und/oder WMAP suchen.

...

0
jtlz2

Vielleicht genügt hier ein naiver Ansatz: Erstellen Sie eine Liste aller 2x2-Quadrate in Ihrem Flugzeug und ordnen Sie sie nach ihrer Summe (in absteigender Reihenfolge). 

Wählen Sie zuerst das Feld mit dem höchsten Wert in Ihrer "Pfotenliste" aus. Wählen Sie dann iterativ 4 der nächstbesten Quadrate aus, die sich mit keinem der zuvor gefundenen Quadrate überschneiden. 

0
Johannes Charra

Was passiert, wenn Sie Schritt für Schritt vorgehen: Sie suchen zuerst das globale Maximum, verarbeiten die umliegenden Punkte bei Bedarf mit ihrem Wert, setzen dann den gefundenen Bereich auf Null und wiederholen den Vorgang für den nächsten.

0
Cedric H.