196 votes

Comment est-ce que je peux améliorer ma patte de détection ?

Après ma précédente question sur trouver des orteils à l'intérieur de chaque patte, j'ai commencé le chargement d'autres mesures, afin de voir comment cela fonctionnerait. Malheureusement, j'ai vite couru dans un problème avec l'une des étapes précédentes: reconnaître les pattes.

Vous voyez, ma preuve de concept a pris le maximum de pression de chaque capteur dans le temps et serait de commencer à chercher pour la somme de chaque ligne, jusqu'à ce qu'il trouve à qui != 0.0. Puis il fait de même pour les colonnes et dès qu'il trouve plus de 2 lignes qui sont de zéro à nouveau. Il stocke l'minimale et maximale de ligne et de colonne à des valeurs de l'indice.

alt text

Comme vous pouvez le voir dans la figure, cela fonctionne assez bien dans la plupart des cas. Cependant, il y a beaucoup d'inconvénients à cette approche (autre que d'être très primitive):

  • Les humains peuvent avoir des " creux de pieds qui signifie qu'il y a plusieurs lignes vides à l'intérieur de l'empreinte elle-même. Depuis j'ai eu peur de ce qui pourrait arriver avec les (gros) chiens aussi, j'ai attendu au moins 2 ou 3 lignes vides avant de couper la patte.

    Cela crée un problème si un autre contact dans une colonne différente avant d'atteindre plusieurs des lignes vides, élargissant ainsi la zone. Je me dis que je pourrais comparer les colonnes et voir si elles dépassent une certaine valeur, ils doivent être séparés par les pattes.

  • Le problème s'aggrave quand le chien est en très petites ou les promenades à un rythme plus rapide. Ce qui se passe, c'est que la patte avant de les orteils sont encore la prise de contact, tandis que la patte arrière de l'orteils commencent tout juste à prendre contact au sein de la même zone que la patte avant!

    Avec mon script simple, il ne sera pas en mesure de séparer ces deux-là, parce qu'il aurait pour déterminer les cadres de cette région appartiennent à la patte, tandis que actuellement je n'aurais plus qu'à regarder le maximum des valeurs sur tous les cadres.

Des exemples de cas où ça commence à aller mal:

alt textalt text

Alors maintenant, je suis à la recherche d'une meilleure façon de reconnaître et de séparer les pattes (après que je vais arriver à le problème de décider de la patte, elle l'est!).

Mise à jour:

J'ai été bricoler pour obtenir Joe (génial!) réponse en œuvre, mais je vais avoir des difficultés de l'extraction de l'effectif de la patte de données à partir de mes fichiers.

alt text

Le coded_paws me montre toutes les différentes pattes, lorsqu'il est appliqué à la pression maximale de l'image (voir ci-dessus). Cependant, la solution va au-dessus de chaque image (pour séparer le chevauchement des pattes) et définit les quatre Rectangle attributs, telles que les coordonnées ou la hauteur/largeur.

Je ne peux pas comprendre comment prendre ces attributs et de les stocker dans une variable que je peux appliquer à des données de mesure. Depuis que j'ai besoin de savoir pour chaque patte, ce que son emplacement est au cours de laquelle les images et ce couple à la patte c'est (avant/arrières, gauche/droite).

Alors, comment puis-je utiliser les Rectangles attributs pour extraire ces valeurs pour chaque patte?

J'ai les mesures que j'ai utilisé dans la question de l'installation dans mon public dossier Dropbox (exemple 1, exemple 2, exemple 3). Pour quiconque s'intéresse j'ai aussi fait un blog pour vous tenir au courant :-)

356voto

Joe Kington Points 68089

Si vous avez juste envie de (semi) régions limitrophes, il y a déjà une mise en œuvre simple en Python: SciPy's ndimage.la morphologie du module. C'est assez commun de l'image de la morphologie de l'opération.


Fondamentalement, vous avez 5 étapes:

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. Le flou sur les données d'entrée un peu assurez-vous que les pattes ont continu d'une empreinte. (Il serait plus efficace d'utiliser un noyau plus grand ( structure kwarg aux divers scipy.ndimage.morphology des fonctions), mais ce n'est pas tout à fait correctement son travail pour une raison quelconque...)

  2. Seuil de la matrice de sorte que vous avez un booléen tableau de lieux où la pression est supérieure à un certain seuil valeur ( thresh = data > value)

  3. Remplir interne des trous, de sorte que vous avez plus propre régions (filled = sp.ndimage.morphology.binary_fill_holes(thresh))

  4. Trouver les séparer des régions contiguës (coded_paws, num_paws = sp.ndimage.label(filled)). Cette fonction retourne un tableau avec les régions codé par le nombre (chaque région est une zone contiguë d'un unique entier (de 1 jusqu'au nombre de pattes) avec des zéros partout ailleurs)).

  5. Isoler les régions contiguës à l'aide de data_slices = sp.ndimage.find_objects(coded_paws). Cela renvoie une liste de tuples de slice objets, de sorte que vous pourriez obtenir de la région de données de chaque patte avec [data[x] for x in data_slices]. Au lieu de cela, nous allons dessiner un rectangle sur la base de ces tranches, ce qui prend un peu plus de travail.


Les deux animations ci-dessous montrent votre "le Chevauchement des Pattes" et "Regroupées Pattes" données de l'exemple. Cette méthode semble fonctionner parfaitement. (Et pour ce qu'il vaut, c'est beaucoup plus fluide que les images GIF ci-dessous sur ma machine, de sorte que la patte de l'algorithme de détection est assez rapide...)

Overlapping PawsGrouped Paws


Voici un exemple complet (maintenant avec beaucoup plus d'explications détaillées). La grande majorité de ce qui est de la lecture de l'entrée et de faire une animation. Le réel de la patte de détection est à seulement 5 lignes de 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')

Mise à jour: pour autant Que l'identification de la patte est en contact avec le capteur à ce temps, la solution la plus simple est de simplement faire la même analyse, mais l'utilisation de toutes les données à la fois. (c'est à dire pile de l'entrée dans un tableau 3D, et de travailler avec elle, au lieu de la personne le temps des images). Parce que SciPy est ndimage fonctions sont conçues pour fonctionner avec la n-dimensions des tableaux, nous n'avons pas à modifier l'original de la patte de fonction de recherche.

# 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

4voto

TaslemGuy Points 374

Je ne suis pas expert dans la détection de l'image, et je ne connais pas Python, mais je vais lui donner une claque...

Pour détecter personne pattes, vous devez d'abord sélectionner uniquement le tout avec une pression supérieure à quelques petites seuil, très proche, pas de pression du tout. Chaque pixel/point qui est au-dessus de ce qui devrait être "marqué." Ensuite, chaque pixel adjacent à tous les "marqué" pixels devient marquée, et ce processus est répété plusieurs fois. Les Masses qui sont totalement connecté formé, si vous avez des objets distincts. Ensuite, chaque "objet" minimum et le maximum des valeurs x et y, de sorte que les cases peuvent être soigneusement rangés autour d'eux.

Pseudo-code:

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

Qui doit le faire.

0voto

Tom Wijsman Points 7139

Note: je dis pixel, mais cela pourrait être des régions à l'aide d'une moyenne des pixels. L'optimisation est une autre question...

Sonne comme vous avez besoin d'analyser une fonction (de la pression au cours du temps) pour chaque pixel et de déterminer où la fonction se transforme (en cas de changement > X dans l'autre sens, il est considéré comme un tour de compteur d'erreurs).

Si vous savez à quelles images il s'avère, vous saurez de l'image où la pression a été le plus dur et vous saurez d'où il était le moins fort entre les deux pattes. En théorie, vous savez que les deux cadres où les pattes pressé le plus dur et de calculer la moyenne de ces intervalles.

après que je vais arriver à le problème de décider de la patte, elle l'est!

C'est le même tour comme avant, en sachant que lors de chaque patte s'applique le plus de pression pour vous aider à décider.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X