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.
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.
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
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?
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.
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!
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!
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?
Ich habe die Peaks mit einem local maximum filter erkannt. Hier ist das Ergebnis Ihres ersten Datensatzes von 4 Pfoten:
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).
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.
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.
Ich durfte die 2x2-Quadrate überlappen. Die bearbeitete Version filtert einige von ihnen heraus, sodass in den Ergebnissen nur nicht überlappende Quadrate angezeigt werden.
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.
Dies ist ein Problem bei der Registrierung von Bildern . Die allgemeine Strategie ist:
Hier ist eine grobe und bereite Annäherung, "das dümmste Ding, das möglicherweise funktionieren könnte":
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.
Dann iterieren bis zur Konvergenz:
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.
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:
... und für diejenigen, die keinen Zugang zu einem NIM-Abonnement haben:
Unter Verwendung einer persistenten Homologie zur Analyse Ihres Datensatzes erhalte ich das folgende Ergebnis (zum Vergrößern klicken):
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:
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:
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 .
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.
Nur ein paar Ideen aus meinem Kopf:
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.
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:
Was Sie wollen, ist ein bisschen anders (2D und beinhaltet Druckwerte), aber ich denke immer noch, Sie könnten es versuchen.
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>
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).
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
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.
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.
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/
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.
Interessantes Problem. Die Lösung, die ich versuchen würde, ist folgende.
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).
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.
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.
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
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
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.
...
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.
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.