Wie kann ich meine Pfotenerkennung verbessern?

Nach meiner vorherigen Frage auf die Suche nach Zehen in jeder Pfote , begann ich Laden von anderen Messungen zu sehen, wie es halten würde. Leider bin ich schnell in ein Problem mit einem der vorhergehenden Schritte gerannt: Erkennung der Pfoten.

Sie sehen, mein Proof of Concept grundsätzlich nahm den maximalen Druck jedes Sensors im Laufe der Zeit und würde auf der Suche nach der Summe jeder Zeile, bis es findet, dass! = 0.0. Dann tut es dasselbe für die Spalten und sobald es mehr als 2 Zeilen mit dem wieder Null gibt. Es speichert die minimalen und maximalen Zeilen- und Spaltenwerte zu einem Index.

Alt-Text

Wie Sie in der Figur sehen können, funktioniert das in den meisten Fällen ganz gut. Allerdings gibt es viele Nachteile zu diesem Ansatz (außer sehr primitiv):

  • Menschen können "hohle Füße" haben, was bedeutet, dass es mehrere leere Reihen innerhalb der Fußabdruck selbst gibt. Da ich befürchtete, dass dies bei (großen) Hunden auch passieren konnte, wartete ich für mindestens 2 oder 3 leere Reihen vor dem Abschneiden der Pfote.

    Dies schafft ein Problem, wenn ein anderer Kontakt in einer anderen Spalte gemacht wird, bevor er mehrere leere Zeilen erreicht, wodurch der Bereich erweitert wird. Ich glaube, ich könnte die Säulen vergleichen und sehen, ob sie einen bestimmten Wert überschreiten, sie müssen getrennte Pfoten sein.

  • Das Problem wird schlimmer, wenn der Hund sehr klein ist oder in einem höheren Tempo geht. Was passiert, ist, dass die Zehen der vorderen Pfote immer noch in Berührung kommen, während die Zehenspitzen der Zahne gerade anfangen, in der gleichen Gegend wie die Vorderpfote Kontakt zu machen!

    Mit meinem einfachen Skript wird es nicht in der Lage sein, diese beiden zu teilen, denn es müsste feststellen, welche Frames dieses Bereichs zu welcher Pfote gehören, während ich momentan nur die Maximalwerte über alle Frames sehen müsste.

Beispiele dafür, wo es los geht falsch:

Alt-TextAlt-Text

Also jetzt suche ich nach einer besseren Art, die Pfoten zu erkennen und zu trennen (worauf ich das Problem bekomme, zu entscheiden, welche Pfote es ist!).

Aktualisieren:

Ich habe angeheftet, Joe's (awesome!) Antwort umzusetzen, aber ich habe Schwierigkeiten, die tatsächlichen Pfoten-Daten aus meinen Dateien zu extrahieren.

Alt-Text

Die coded_paws zeigt mir alle verschiedenen Pfoten, wenn sie auf das maximale Druckbild angewendet werden (siehe oben). Allerdings geht die Lösung über jeden Rahmen (um überlappende Pfoten zu trennen) und setzt die vier Rectangle-Attribute wie Koordinaten oder Höhe / Breite.

Ich kann nicht herausfinden, wie man diese Attribute aufnimmt und sie in einer Variablen speichert, die ich auf die Messdaten anwenden kann. Da muss ich für jede Pfote wissen, was seine Lage ist, während der Frames und paaren diese, die Paw ist es (vorne / hinten, links / rechts).

Also, wie kann ich die Rectangles Attribute verwenden, um diese Werte für jede Pfote zu extrahieren?

Ich habe die Messungen, die ich bei der Frage in meinem öffentlichen Dropbox-Ordner verwendet habe ( Beispiel 1 , Beispiel 2 , Beispiel 3 ). Für alle Interessierten habe ich auch einen Blog eingerichtet , um dich auf dem Laufenden zu halten 🙂

  • Rekonstruiertes Bild nach Laplace-Pyramide Nicht das gleiche wie das Originalbild
  • Jeder Weg, um schöne antialiased runde Ecken für Bilder in Python zu machen?
  • Warum luma-Parameter unterscheidet sich in opencv und matlab?
  • Wie man Einschusslöcher auf dem Ziel mit Python zu erkennen
  • Wie kann ich die logische Bedienung und die logische Indexierung mit VIPS in Python durchführen?
  • OpenCV und Python: Verbundene Komponentenanalyse
  • 3 Solutions collect form web for “Wie kann ich meine Pfotenerkennung verbessern?”

    Wenn du gerade willst (halb) zusammenhängende Regionen, gibt es schon eine einfache Implementierung in Python: SciPys ndimage.morphology Modul. Dies ist ein ziemlich allgemeiner Bildmorphologiebetrieb .


    Grundsätzlich hast du 5 Schritte:

    def find_paws(data, smooth_radius=5, threshold=0.0001): data = sp.ndimage.uniform_filter(data, smooth_radius) thresh = data > threshold filled = sp.ndimage.morphology.binary_fill_holes(thresh) coded_paws, num_paws = sp.ndimage.label(filled) data_slices = sp.ndimage.find_objects(coded_paws) return object_slices 
    1. Verschwimmen Sie die Eingabedaten ein wenig, um sicherzustellen, dass die Pfoten einen durchgehenden Fußabdruck haben. (Es wäre effizienter, nur einen größeren Kernel zu benutzen (die structure kwarg zu den verschiedenen scipy.ndimage.morphology Funktionen), aber das ist nicht ganz richtig funktionieren aus irgendeinem Grund …)

    2. Schwellen Sie das Array so, dass Sie ein boolesches Array von Orten haben, an denen der Druck über einen Schwellenwert liegt (dh thresh = data > value )

    3. Füllen Sie alle inneren Löcher, so dass Sie sauberere Regionen haben ( filled = sp.ndimage.morphology.binary_fill_holes(thresh) )

    4. Finde die einzelnen zusammenhängenden Regionen ( coded_paws, num_paws = sp.ndimage.label(filled) ). Dies gibt ein Array mit den von der Nummer codierten Regionen zurück (jede Region ist ein zusammenhängender Bereich einer eindeutigen Ganzzahl (1 bis zur Anzahl der Pfoten) mit Nullen überall)).

    5. Isoliere die zusammenhängenden Regionen mit data_slices = sp.ndimage.find_objects(coded_paws) . Dies gibt eine Liste von Tupeln von slice Objekten zurück, so dass man die Region der Daten für jede Pfote mit [data[x] for x in data_slices] . Stattdessen zeichnen wir ein Rechteck, das auf diesen Scheiben basiert, was etwas mehr Arbeit bringt.


    Die beiden Animationen unten zeigen die Beispieldaten "Überlappende Pfoten" und "Gruppierte Pfoten". Diese Methode scheint perfekt zu funktionieren. (Und für was auch immer es sich lohnt, läuft das viel reibungsloser als die GIF Bilder unten auf meiner Maschine, also ist der Paw Detection Algorithmus ziemlich schnell …)

    Überlappende PfotenGruppierte Pfoten


    Hier ist ein volles Beispiel (jetzt mit viel ausführlicheren Erklärungen). Die überwiegende Mehrheit davon liest die Eingabe und macht eine Animation. Die tatsächliche Pfote-Erkennung ist nur 5 Zeilen Code.

     import numpy as np import scipy as sp import scipy.ndimage import matplotlib.pyplot as plt from matplotlib.patches import Rectangle def animate(input_filename): """Detects paws and animates the position and raw data of each frame in the input file""" # With matplotlib, it's much, much faster to just update the properties # of a display object than it is to create a new one, so we'll just update # the data and position of the same objects throughout this animation... infile = paw_file(input_filename) # Since we're making an animation with matplotlib, we need # ion() instead of show()... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) fig.suptitle(input_filename) # Make an image based on the first frame that we'll update later # (The first frame is never actually displayed) im = ax.imshow(infile.next()[1]) # Make 4 rectangles that we can later move to the position of each paw rects = [Rectangle((0,0), 1,1, fc='none', ec='red') for i in range(4)] [ax.add_patch(rect) for rect in rects] title = ax.set_title('Time 0.0 ms') # Process and display each frame for time, frame in infile: paw_slices = find_paws(frame) # Hide any rectangles that might be visible [rect.set_visible(False) for rect in rects] # Set the position and size of a rectangle for each paw and display it for slice, rect in zip(paw_slices, rects): dy, dx = slice rect.set_xy((dx.start, dy.start)) rect.set_width(dx.stop - dx.start + 1) rect.set_height(dy.stop - dy.start + 1) rect.set_visible(True) # Update the image data and title of the plot title.set_text('Time %0.2f ms' % time) im.set_data(frame) im.set_clim([frame.min(), frame.max()]) fig.canvas.draw() def find_paws(data, smooth_radius=5, threshold=0.0001): """Detects and isolates contiguous regions in the input array""" # Blur the input data a bit so the paws have a continous footprint data = sp.ndimage.uniform_filter(data, smooth_radius) # Threshold the blurred data (this needs to be a bit > 0 due to the blur) thresh = data > threshold # Fill any interior holes in the paws to get cleaner regions... filled = sp.ndimage.morphology.binary_fill_holes(thresh) # Label each contiguous paw coded_paws, num_paws = sp.ndimage.label(filled) # Isolate the extent of each paw data_slices = sp.ndimage.find_objects(coded_paws) return data_slices def paw_file(filename): """Returns a iterator that yields the time and data in each frame The infile is an ascii file of timesteps formatted similar to this: Frame 0 (0.00 ms) 0.0 0.0 0.0 0.0 0.0 0.0 Frame 1 (0.53 ms) 0.0 0.0 0.0 0.0 0.0 0.0 ... """ with open(filename) as infile: while True: try: time, data = read_frame(infile) yield time, data except StopIteration: break def read_frame(infile): """Reads a frame from the infile.""" frame_header = infile.next().strip().split() time = float(frame_header[-2][1:]) data = [] while True: line = infile.next().strip().split() if line == []: break data.append(line) return time, np.array(data, dtype=np.float) if __name__ == '__main__': animate('Overlapping paws.bin') animate('Grouped up paws.bin') animate('Normal measurement.bin') 

    Update: Soweit die Identifizierung, welche Pfote in Kontakt mit dem Sensor ist zu welchen Zeiten, ist die einfachste Lösung, nur die gleiche Analyse, aber verwenden Sie alle Daten auf einmal. (Dh die Inputs in ein 3D-Array zu stapeln und anstatt der einzelnen Zeitrahmen zu arbeiten). Weil SciPys ndimage-Funktionen mit n-dimensionalen Arrays arbeiten sollen, müssen wir die ursprüngliche Paw-Finding-Funktion nicht ändern überhaupt.

     # This uses functions (and imports) in the previous code example!! def paw_regions(infile): # Read in and stack all data together into a 3D array data, time = [], [] for t, frame in paw_file(infile): time.append(t) data.append(frame) data = np.dstack(data) time = np.asarray(time) # Find and label the paw impacts data_slices, coded_paws = find_paws(data, smooth_radius=4) # Sort by time of initial paw impact... This way we can determine which # paws are which relative to the first paw with a simple modulo 4. # (Assuming a 4-legged dog, where all 4 paws contacted the sensor) data_slices.sort(key=lambda dat_slice: dat_slice[2].start) # Plot up a simple analysis fig = plt.figure() ax1 = fig.add_subplot(2,1,1) annotate_paw_prints(time, data, data_slices, ax=ax1) ax2 = fig.add_subplot(2,1,2) plot_paw_impacts(time, data_slices, ax=ax2) fig.suptitle(infile) def plot_paw_impacts(time, data_slices, ax=None): if ax is None: ax = plt.gca() # Group impacts by paw... for i, dat_slice in enumerate(data_slices): dx, dy, dt = dat_slice paw = i%4 + 1 # Draw a bar over the time interval where each paw is in contact ax.barh(bottom=paw, width=time[dt].ptp(), height=0.2, left=time[dt].min(), align='center', color='red') ax.set_yticks(range(1, 5)) ax.set_yticklabels(['Paw 1', 'Paw 2', 'Paw 3', 'Paw 4']) ax.set_xlabel('Time (ms) Since Beginning of Experiment') ax.yaxis.grid(True) ax.set_title('Periods of Paw Contact') def annotate_paw_prints(time, data, data_slices, ax=None): if ax is None: ax = plt.gca() # Display all paw impacts (sum over time) ax.imshow(data.sum(axis=2).T) # Annotate each impact with which paw it is # (Relative to the first paw to hit the sensor) x, y = [], [] for i, region in enumerate(data_slices): dx, dy, dz = region # Get x,y center of slice... x0 = 0.5 * (dx.start + dx.stop) y0 = 0.5 * (dy.start + dy.stop) x.append(x0); y.append(y0) # Annotate the paw impacts ax.annotate('Paw %i' % (i%4 +1), (x0, y0), color='red', ha='center', va='bottom') # Plot line connecting paw impacts ax.plot(x,y, '-wo') ax.axis('image') ax.set_title('Order of Steps') 

    Alt-Text


    Alt-Text


    Alt-Text

    Ich bin kein Experte in Bild-Erkennung, und ich weiß nicht, Python, aber ich werde es ein whack …

    Um einzelne Pfoten zu erkennen, sollten Sie zuerst nur alles mit einem Druck wählen, der größer ist als eine kleine Schwelle, ganz nah an keinem Druck. Jeder Pixel / Punkt, der darüber liegt, sollte "markiert" werden. Dann wird jedes Pixel, das an alle "markierten" Pixel angrenzt, markiert, und dieser Vorgang wird ein paar Mal wiederholt. Massen, die völlig verbunden sind, würden gebildet werden, so dass Sie unterschiedliche Objekte haben. Dann hat jedes "Objekt" einen minimalen und maximalen x- und y-Wert, so dass Bounding-Boxen ordentlich um sie herum gepackt werden können.

    Pseudocode:

    (MARK) ALL PIXELS ABOVE (0.5)

    (MARK) ALL PIXELS (ADJACENT) TO (MARK) PIXELS

    REPEAT (STEP 2) (5) TIMES

    SEPARATE EACH TOTALLY CONNECTED MASS INTO A SINGLE OBJECT

    MARK THE EDGES OF EACH OBJECT, AND CUT APART TO FORM SLICES.

    Das soll es tun

    Anmerkung: Ich sage Pixel, aber das könnte Regionen sein, die einen Durchschnitt der Pixel verwenden. Optimierung ist ein weiteres Thema …

    Klingt wie Sie müssen eine Funktion (Druck über die Zeit) für jedes Pixel zu analysieren und zu bestimmen, wo die Funktion dreht (wenn es ändert sich> X in die andere Richtung ist es als eine Wendung, um Fehler zu beenden).

    Wenn Sie wissen, an welchen Frames es sich dreht, werden Sie den Rahmen kennen, wo der Druck am härtesten war und Sie wissen, wo es am wenigsten hart zwischen den beiden Pfoten war. In der Theorie würden Sie dann die beiden Frames kennen, wo die Pfoten am härtesten gedrückt wurden und einen Durchschnitt dieser Intervalle berechnen können.

    Danach werde ich das Problem zu entscheiden, welche Pfoten es ist!

    Dies ist die gleiche Tour wie vorher, wissend, wenn jede Pfote den meisten Druck anwendet, hilft Ihnen zu entscheiden.

    Python ist die beste Programmiersprache der Welt.