如何整理我的爪子?

问题:

my previous question I got an excellent answer帮助我检测到一个爪子压在压力板上的位置,但现在我正在努力将这些结果与相应的爪子相联系:
 alt text
我手动注释爪子(RF =右前,RH =右后,LF =左前,LH =左后)。
正如你所看到的那样,显然有一个重复的模式,它几乎在每一个测量中都会出现。 Here’s a link to a presentation of 6 trials that were manually annotated.
我最初的想法是使用启发式方法进行排序,如:

  • 前爪和后爪之间的重量比约为〜60-40%
  • 后爪通常表面较小;
  • 爪子(通常)在左边和右边空间分开。

不过,我对我的启发式有些怀疑,因为一旦遇到我没想到的变化,我们就会失败。他们也无法应付可能有自己规则的跛脚狗的测量。
此外,Joe建议的注释有时会被弄乱,并没有考虑到爪子实际上是什么样的。
根据我收到的答案on my question about peak detection within the paw,我希望有更高级的解决方案来排序爪子。特别是因为压力分布及其进展对于每个单独的爪子是不同的,几乎像指纹。我希望有一种方法可以使用它来聚集我的爪子,而不是按照发生的顺序对它们进行排序。
 alt text
 所以我正在寻找一个更好的方法来排序结果与他们对应的爪子。
对于任何遇到挑战的人来说,I have pickled a dictionary all the sliced arrays that contain the pressure data of each paw(按测量捆绑)和the slice that describes their location(板块上和时间上的位置)。
要澄清:walk_sliced_data是一个包含[‘ser_3’,’ser_2’,’sel_1’,’sel_2’,’ser_1’,’sel_3’]的字典,它们是测量的名称。每个测量包含代表所提取的影响的另一个字典[0,1,2,3,4,5,6,7,8,9,10](来自’sel_1’的示例)。
还要注意,“假”的影响,例如爪子部分测量(空间或时间)的位置可以被忽略。它们只是有用的,因为它们可以帮助识别模式,但是
将不会被分析。
对于任何有兴趣的人,I’m keeping a blog with all the updates regarding the project!

回答:

好的!我终于设法使一些工作一贯!这个问题拉了我几天…有趣的东西!对不起,这个答案的长度,但我需要详细阐述一些事情…(虽然我可能为最长的非垃圾邮件stackoverflow答案设置一个记录!)
作为附注,我在他的original question中使用Ivo provided a link to的完整数据集。它是一系列rar文件(每个狗一个),每个文件包含几个不同的实验运行,存储为ascii数组。而不是尝试将独立代码示例复制到此问题中,这里是一个bitbucket mercurial repository,具有完整的独立代码。你可以克隆它
 hg clone https://joferkington@bitbucket.org/joferkington/paw-analysis
 概观
基本上有两种方法来解决问题,正如您在问题中所指出的那样。我实际上会以不同的方式使用。

  1. 使用爪子冲击的(时间和空间)顺序来确定哪个爪子是哪个爪子。
  2. 尝试基于其形状来识别“爪印”。

基本上,第一种方法适用于狗的爪子,遵循Ivo上面的问题所示的梯形样,但是每当爪子不遵循这种模式时,就会失败。当它不起作用时,编程检测是相当容易的。
因此,我们可以使用它所做的测量来建立一个训练数据集(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~图像识别比“正常”监督分类问题有点困难。
 模式分析
要详细说明第一种方法,当狗正常行走(不运行)(这些狗中有一些可能不是)时,我们希望爪子按照以下顺序影响:前左,右后,右前,后左,左前方等等。图案可以从左前爪或右前爪开始。
如果总是这样,我们可以通过初始接触时间来简单地排列影响,并使用模4将其分组。
 Normal Impact Sequence
然而,即使一切都是“正常”,这不行。这是由于图案的梯形形状。后爪空间落在前一个前爪的后面。
因此,在最初的前爪冲击之后的后爪冲击通常会从传感器板上掉落,并且不被记录。类似地,最后的爪冲击通常不是顺序中的下一个爪,因为爪在它从传感器板发生之前冲击而没有记录。
 Missed Hind Paw
尽管如此,我们可以使用爪子碰撞模式的形状来确定何时发生,以及我们是否以左或右前爪开始。 (实际上我忽略了最后一个影响的问题,但是添加它并不难)

def group_paws(data_slices, time):   
    # Sort slices by initial contact time
    data_slices.sort(key=lambda s: s[-1].start)

    # Get the centroid for each paw impact...
    paw_coords = []
    for x,y,z in data_slices:
        paw_coords.append([(item.stop + item.start) / 2.0 for item in (x,y)])
    paw_coords = np.array(paw_coords)

    # Make a vector between each sucessive impact...
    dx, dy = np.diff(paw_coords, axis=0).T

    #-- Group paws -------------------------------------------
    paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
    paw_number = np.arange(len(paw_coords))

    # Did we miss the hind paw impact after the first 
    # front paw impact? If so, first dx will be positive...
    if dx[0] > 0: 
        paw_number[1:] += 1

    # Are we starting with the left or right front paw...
    # We assume we're starting with the left, and check dy[0].
    # If dy[0] > 0 (i.e. the next paw impacts to the left), then
    # it's actually the right front paw, instead of the left.
    if dy[0] > 0: # Right front paw impact...
        paw_number += 2

    # Now we can determine the paw with a simple modulo 4..
    paw_codes = paw_number % 4
    paw_labels = [paw_code for code in paw_codes]

    return paw_labels

尽管如此,它经常不能正常工作。完整数据集中的许多狗似乎正在运行,并且爪子的影响不像狗走路时那样遵循相同的时间顺序。 (或者也许狗只有严重的髋关节问题...)
 Abnormal Impact Sequence
幸运的是,我们仍然可以编程地检测爪子的影响是否符合我们预期的空间格局:

def paw_pattern_problems(paw_labels, dx, dy):
    """Check whether or not the label sequence "paw_labels" conforms to our
    expected spatial pattern of paw impacts. "paw_labels" should be a sequence
    of the strings: "LH", "RH", "LF", "RF" corresponding to the different paws"""
    # Check for problems... (This could be written a _lot_ more cleanly...)
    problems = False
    last = paw_labels[0]
    for paw, dy, dx in zip(paw_labels[1:], dy, dx):
        # Going from a left paw to a right, dy should be negative
        if last.startswith('L') and paw.startswith('R') and (dy > 0):
            problems = True
            break
        # Going from a right paw to a left, dy should be positive
        if last.startswith('R') and paw.startswith('L') and (dy < 0):
            problems = True
            break
        # Going from a front paw to a hind paw, dx should be negative
        if last.endswith('F') and paw.endswith('H') and (dx > 0):
            problems = True
            break
        # Going from a hind paw to a front paw, dx should be positive
        if last.endswith('H') and paw.endswith('F') and (dx < 0):
            problems = True
            break
        last = paw
    return problems
&#91;/code&#93;
因此,尽管简单的空间分类在所有时间内都不起作用,但我们可以确定何时合理的信心。
 <strong>培训数据集</strong>
从基于模式的分类工作正确,我们可以建立一个非常大的训练数据集,正确分类的爪子(来自32个不同的狗!
我们现在可以开始看看什么是“平均”前方,等等,爪子看起来像。
为了做到这一点,我们需要某种类型的“爪子度量”,这对于任何狗来说都是相同的维度。 (在完整的数据集中,有非常大的和非常小的狗!)来自爱尔兰猎犬的爪子比从玩具狮子狗的爪子印刷品要宽得多,比“重”更重。我们需要重新缩放每个爪印,以便a)它们具有相同数量的像素,以及b)压力值被标准化。为了做到这一点,我将每个爪印刷重新取样到20x20格栅上,并根据爪子冲击的最大值,最小值和平均压力值对压力值进行重新缩放。

def paw_image(paw):
    from scipy.ndimage import map_coordinates
    ny, nx = paw.shape

    # Trim off any "blank" edges around the paw...
    mask = paw > 0.01 * paw.max()
    y, x = np.mgrid[:ny, :nx]
    ymin, ymax = y[mask].min(), y[mask].max()
    xmin, xmax = x[mask].min(), x[mask].max()

    # Make a 20x20 grid to resample the paw pressure values onto
    numx, numy = 20, 20
    xi = np.linspace(xmin, xmax, numx)
    yi = np.linspace(ymin, ymax, numy)
    xi, yi = np.meshgrid(xi, yi)  

    # Resample the values onto the 20x20 grid
    coords = np.vstack([yi.flatten(), xi.flatten()])
    zi = map_coordinates(paw, coords)
    zi = zi.reshape((numy, numx))

    # Rescale the pressure values
    zi -= zi.min()
    zi /= zi.max()
    zi -= zi.mean() #<- Helps distinguish front from hind paws...
    return zi
&#91;/code&#93;
所有这一切,我们终于可以看看平均左前方,右后方等爪子的样子。请注意,这是大小不同的30只狗的平均值,我们似乎得到了一致的结果!
 <img alt="Average Paws" src="https://i.stack.imgur.com/1rcSk.png"/>
然而,在我们对此进行任何分析之前,我们需要减去平均值(所有狗的所有腿的平均爪)。
 <img alt="Mean Paw" src="https://i.stack.imgur.com/y6sbC.png"/>
现在我们可以分析平均值的差异,这更容易识别:
 <img alt="Differential Paws" src="https://i.stack.imgur.com/JkNR7.png"/>
 <strong>基于图像的爪识别</strong>
好吧,我们终于有了一套模式,我们可以开始尝试去匹配爪子。每个爪可以被看作可以与这四个400维向量相比较的400维向量(由<code>paw_image</code>函数返回))。
不幸的是,如果我们仅使用“正常”监督分类算法(即,使用简单距离找到4个图案中的哪一个最接近特定爪印),则它不会一致地工作。实际上,训练数据集并不比随机机会好多了。
这是图像识别中的常见问题。由于输入数据的高维度,以及图像(即相邻像素具有高协方差)的稍微“模糊”性质,简单地观察图像与模板图像的差异并不能很好地测量他们的形状相似。
 <strong>Eigenpaws</strong>
为了解决这个问题,我们需要构建一套“特征爪”(就像面部识别中的“特征面”)一样,并将每个爪印描述为这些特征爪的组合。这与主成分分析相同,基本上提供了一种降低数据维度的方式,使得距离是一个很好的形状尺度。
因为我们拥有比维度更多的训练图像(2400 vs 400),所以没有必要对速度进行“花式”线性代数。我们可以直接使用训练数据集的协方差矩阵:

def make_eigenpaws(paw_data):
    """Creates a set of eigenpaws based on paw_data.
    paw_data is a numdata by numdimensions matrix of all of the observations."""
    average_paw = paw_data.mean(axis=0)
    paw_data -= average_paw

    # Determine the eigenvectors of the covariance matrix of the data
    cov = np.cov(paw_data.T)
    eigvals, eigvecs = np.linalg.eig(cov)

    # Sort the eigenvectors by ascending eigenvalue (largest is last)
    eig_idx = np.argsort(eigvals)
    sorted_eigvecs = eigvecs[:,eig_idx]
    sorted_eigvals = eigvals[:,eig_idx]

    # Now choose a cutoff number of eigenvectors to use 
    # (50 seems to work well, but it's arbirtrary...
    num_basis_vecs = 50
    basis_vecs = sorted_eigvecs[:,-num_basis_vecs:]

    return basis_vecs

这些basis_vecs是“特征”。
 Eigenpaws
为了使用这些,我们简单地将每个爪形图像(作为400维矢量,而不是20×20图像)与基矢量进行点(矩阵乘法)。这给了我们一个50维向量(每个基本向量一个元素),我们可以使用它来对图像进行分类。将20x20图像与每个“模板”爪子的20x20图像进行比较,我们将50维转换的图像与每个50维转换的模板爪比较。这对于每个脚趾的位置确切等等的小变化不太敏感,并且基本上将问题的维度降低到相关的尺寸。
 基于特征爪的爪分类
现在我们可以简单地使用每个腿的50维向量和“模板”向量之间的距离来分类哪个爪是哪个:

codebook = np.load('codebook.npy') # Template vectors for each paw
average_paw = np.load('average_paw.npy')
basis_stds = np.load('basis_stds.npy') # Needed to "whiten" the dataset...
basis_vecs = np.load('basis_vecs.npy')
paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
def classify(paw):
    paw = paw.flatten()
    paw -= average_paw
    scores = paw.dot(basis_vecs) / basis_stds
    diff = codebook - scores
    diff *= diff
    diff = np.sqrt(diff.sum(axis=1))
    return paw_code[diff.argmin()]

以下是一些结果:
 alt text alt text alt text
 剩下的问题
还有一些问题,特别是狗太小,无法做出明确的爪子...(最适合大型狗,因为脚趾在传感器的分辨率上更加清晰。)此外,部分爪印不能被识别系统,而它们可以与基于梯形图案的系统。
然而,由于特征分析固有地使用距离度量,所以当特征分析距离“码本”的最小距离超过某个阈值时,我们可以将爪子分为两种方式,并回到基于梯形图案的系统。我还没有实现这个。
呃...那很久了!我的帽子是离开伊沃有一个有趣的问题!

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: How to sort my paws?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

27 − = 19