Как я могу улучшить обнаружение лапы?



после моего предыдущего вопроса о нахождение пальцев в каждой лапе, Я начал загружать другие измерения, чтобы увидеть, как он будет держать. К сожалению, я быстро столкнулся с проблемой с одним из предыдущих шагов: распознавание лап.



видите ли, мое доказательство концепции в основном принимало максимальное давление каждого датчика с течением времени и начинало искать сумму каждой строки, пока она не найдет на этом != 0,0. Затем он делает то же самое для столбцов и как только он находит более 2 строк, которые снова равны нулю. Он хранит минимальные и максимальные значения строк и столбцов для некоторого индекса.



alt text



Как вы можете видеть на рисунке, это работает достаточно хорошо в большинстве случаев. Однако у этого подхода есть много недостатков (кроме того, что он очень примитивен):





  • люди могут иметь "полые ноги", что означает, что в самом следе есть несколько пустых строк. Так как я боялся, что это может произойти с (большие) собаки тоже, я ждал по крайней мере 2 или 3 пустых ряда, прежде чем отрезать лапу.



    Это создает проблему, если другой контакт сделан в другом столбце, прежде чем он достигнет нескольких пустых строк, тем самым расширяя область. Я полагаю, что могу сравнить столбцы и посмотреть, превышают ли они определенное значение, они должны быть отдельными лапами.




  • проблема усугубляется, когда собака очень маленькая или ходит в более высоком темпе. Что происходит, так это то, что пальцы передней лапы все еще контактируют, в то время как пальцы задней лапы только начинают контактировать в той же области, что и передняя лапа!



    с моим простым скриптом он не сможет разделить эти два, потому что ему нужно будет определить, какие кадры этой области принадлежат какой лапе, в то время как в настоящее время мне нужно будет только посмотреть на максимальные значения по всем кадрам.




примеры того, где он начинает идти неправильно:



alt textalt text



так что теперь я ищу лучший способ распознавания и разделения лап (после чего я перейду к проблеме решения, какая лапа это!).



обновление:



я возился, чтобы получить Джо (потрясающе!) ответ реализован, но у меня возникли трудности с извлечением фактических данных paw из моих файлов.



alt text



coded_paws показывает мне все различные лапы, при нанесении на изображение максимального давления (см. выше). Однако решение проходит по каждому кадру (для разделения перекрывающихся лап) и устанавливает четыре атрибута прямоугольника, такие как координаты или высота/ширина.



Я не могу понять, как взять эти атрибуты и сохранить их в некоторой переменной, которую я могу применить к данным измерений. Поскольку мне нужно знать для каждой лапы, каково ее местоположение, во время которого кадры и пара это к какой лапе это (передняя/задняя, влево-вправо.)



Итак, как я могу использовать атрибуты прямоугольников для извлечения этих значений для каждой лапы?



У меня есть измерения, которые я использовал в настройке вопроса в моей общедоступной папке Dropbox (Пример 1,Пример 2,Пример 3). для всех заинтересованных я также создал блог чтобы держать вас в курсе :-)

531   3  

3 ответов:

если вы просто хотите (полу) смежных областей, уже есть простая реализация в Python:составляющей ' s ndimage.морфология модуль. Это довольно распространенный морфология изображения операции.


в основном, у вас есть 5 шагов:

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. размыть входные данные немного, чтобы убедиться, что лапы имеют непрерывный след. (Было бы более эффективно просто использовать более крупное ядро (the structure kwarg к различным scipy.ndimage.morphology функции), но это не совсем правильно работает почему-то...)

  2. пороговый массив, так что у вас есть логический массив мест, где давление превышает некоторое пороговое значение (т. е. thresh = data > value)

  3. заполните все внутренние отверстия, чтобы у вас были более чистые области (filled = sp.ndimage.morphology.binary_fill_holes(thresh))

  4. найти отдельные смежные области (coded_paws, num_paws = sp.ndimage.label(filled)). Это возвращает массив с области, закодированные числом (каждая область является смежной областью уникального целого числа (от 1 до числа лап) с нулями везде)).

  5. изолировать смежные области с помощью data_slices = sp.ndimage.find_objects(coded_paws). Это возвращает список кортежей slice объекты, так что вы можете получить область данных для каждой лапы с [data[x] for x in data_slices]. Вместо этого мы нарисуем прямоугольник на основе этих срезов, что займет немного больше работы.


две анимации ниже показаны ваши" перекрывающиеся лапы "и" сгруппированные лапы " примерные данные. Этот метод, кажется, работает отлично. (И для чего бы это ни стоило, это работает гораздо более гладко, чем изображения GIF ниже на моей машине, поэтому алгоритм обнаружения лапы довольно быстр...)

Overlapping PawsGrouped Paws


вот полный пример (теперь с гораздо более подробными объяснениями). Подавляющее большинство из них-это чтение ввода и создание анимации. Фактическое обнаружение лапы всего 5 строк кода.

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

обновление: что касается определения того, какая лапа находится в контакте с датчиком в какое время, самым простым решением является просто сделать тот же анализ, но использовать все данные сразу. (т. е. стек вход в 3D-массив, и работать с ним, а не отдельные временные рамки.) Поскольку функции ndimage SciPy предназначены для работы с n-мерными массивами, нам не нужно изменять исходную функцию поиска лапы при все.

# 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

Я не эксперт в обнаружении изображений, и я не знаю Python, но я дам ему удар...

чтобы обнаружить отдельные лапы, вы должны сначала выбрать только все с давлением больше, чем какой-то небольшой порог, очень близко к отсутствию давления вообще. Каждый пиксель / точка, которая находится выше этого, должна быть " отмечена. Затем каждый пиксель, смежный со всеми "отмеченными" пикселями, становится отмеченным, и этот процесс повторяется несколько раз. Массы, которые полностью связаны, будут сформированы, поэтому у вас есть различные объекты. Затем каждый "объект" имеет минимальное и максимальное значение x и y, поэтому ограничительные рамки могут быть аккуратно упакованы вокруг них.

псевдокод:

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

на этом все.

Примечание: Я говорю пиксель, но это могут быть регионы, использующие среднее значение пикселей. Оптимизация-это еще один вопрос...

Похоже, вам нужно проанализировать функцию (давление с течением времени) для каждого пикселя и определить где функция получается (когда он изменяется > X в другом направлении, это считается поворотом для противодействия ошибкам).

Если вы знаете, на каких кадрах он поворачивается, вы будете знать кадр, где давление было самым жестким, и вы будет знать, где это было наименее трудно между двумя лапами. Теоретически, вы тогда знали бы два кадра, где лапы нажимали наиболее сильно, и могли бы вычислить среднее значение этих интервалов.

после чего я перейду к проблеме решения, какая лапа это!

Это тот же тур, как и раньше, зная, когда каждая лапа применяет наибольшее давление помогает вам решить.

Comments

    Ничего не найдено.