2010-11-03 63 views
184

在我上一个问题finding toes within each paw之后,我开始加载其他测量值,看看它会如何保持。不幸的是,我很快遇到了上述步骤之一的问题:识别爪子。如何提高我的爪子检测?

你看,我的概念验证基本上花了每个传感器的最大压力随着时间的推移,并开始寻找每一行的总和,直到它找到!= 0.0。然后它对列执行相同的操作,只要它发现多于2行的数据再次为零。它将最小和最大行数和列值存储到某个索引。

alt text

正如你在图中看到,这工作得很好在大多数情况下。然而,也有很多缺点,以这种方式(比是非常原始的除外):

  • 人类可以有“空心脚”,这意味着有足迹本身内的几个空行。因为我担心这可能会发生在(大)狗身上,所以我在切断爪子之前等待至少2或3个空行。

    如果另一个联系人在达到多个空行之前在另一列中创建联系人,则会产生问题,从而扩大该区域。我想我可以比较栏目,看看它们是否超过了一定的价值,它们必须是单独的爪子。

  • 当狗很小或步伐较快时,问题会变得更严重。会发生什么情况是前爪的脚趾仍在接触,而后爪的脚趾刚开始与前爪在同一区域内接触!

    使用我的简单脚本,它将无法分割这两个,因为它必须确定哪个区域的哪些帧属于哪个爪子,而目前我只需要查看所有的最大值帧。脚麻它开始的地方

例子:

alt text alt text

所以现在我正在寻找识别和分离爪更好的办法(之后我会去解决它的问题吧!)。

更新:

我一直在摆弄得到乔(真棒!)答案实现的,但我有从我的文件中提取的实际爪子数据的困难。

alt text

的coded_pa​​ws显示我所有不同的爪子,当施加到最大压力图像(见上文)。但是,解决方案遍历每个框架(以分离重叠的爪子)并设置四个Rectangle属性,例如坐标或高度/宽度。

我无法弄清楚如何获取这些属性并将它们存储在某些我可以应用于测量数据的变量中。因为我需要知道每个爪子,它的位置是在哪个框架中,并将它连接到哪个爪子(前/后,左/右)。

那么如何使用矩形属性为每个爪子提取这些值?

我在我的公共Dropbox文件夹(example 1example 2example 3)的问题设置中使用了测量。 For anyone interested I also set up a blog让您及时了解最新:-)

+0

好像你将不得不把从行/列算法远的你限制有用的信息。 – 2010-11-03 22:21:48

+12

哇!猫控制软件? – alxx 2010-11-10 08:34:34

+0

它实际上是狗的数据@alxx ;-)但是,它会被用来诊断它们! – 2010-11-10 08:37:04

回答

344

如果你只想要(半)连续的区域,Python中已经有一个简单的实现:SciPyndimage.morphology模块。这是相当常见的image morphology操作。


基本上,你有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. 模糊输入的数据位,以确保爪子有一个连续的足迹。所以,你有一个(这将是更有效地只使用一个更大的内核(该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 Paws Grouped 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数组中,并使用它来代替单个时间帧)。由于SciPy的ndimage函数旨在与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

+79

我甚至无法解释你真棒回答是多么的棒! – 2010-11-04 16:03:11

+0

@Ivo - 很高兴帮助!这是一个很酷的数据集,也是一个很好的问题! – 2010-11-04 16:17:09

+0

@Joe:很好的回答!你是如何制作GIF的?我可以'plt.savefig(...)'创建一个png集合,但是imagemagick的'convert * .png output.gif'会将我的机器带到膝盖上...... – unutbu 2010-11-04 20:15:01

3

我在图像检测方面的专家,我不知道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.

应该这样做。

0

注:我说像素,但这可能是使用平均像素的区域。优化是另一个问题...

听起来像你需要分析每个像素的函数(压力随时间变化),并确定where the function turns(当它在另一个方向上改变> X时,它被认为是对抗错误的转向)。

如果你知道它在哪个帧上转动,你就会知道压力最大的帧,并且你会知道两个爪子之间的最小距离。理论上讲,你会知道爪子最难按下的两个框架,并且可以计算出这些间隔的平均值。

之后我会去决定它是哪个爪子的问题!

这是与以前相同的行程,了解每个爪子适用的最大压力可以帮助您决定。