关于Python:如何分类我的paws?

How to sort my paws?

在我之前的问题中,我得到了一个很好的答案,帮助我检测到一只爪撞击压力盘的位置,但现在我正在努力将这些结果与它们相应的爪联系起来:

alt text

我手动标注了爪(右前,右后,左前,左后)。

正如你所看到的,很明显有一个重复的模式,它在几乎所有的测量中都会出现。这里有一个链接,指向手动注释的6个试验的演示。

我最初的想法是用启发式方法进行排序,比如:

  • 前爪和后爪之间的负重比例约为60-40%;
  • 后爪的表面通常较小;
  • 爪(通常)在空间上分为左右两部分。

然而,我对我的启发式方法有点怀疑,因为一旦我遇到一个我没有想到的变化,它们就会在我身上失败。他们也无法应付跛脚狗的测量,他们可能有自己的规则。

此外,乔建议的注释有时会搞得一团糟,没有考虑到爪子的实际形状。

基于我收到的关于爪内峰值检测的问题的答案,我希望有更先进的解决方案来对爪进行分类。尤其是因为每个单独的爪的压力分布和进展是不同的,几乎就像一个指纹。我希望有一种方法可以用它来聚集我的爪子,而不仅仅是按照发生的顺序对它们进行排序。

alt text

因此,我正在寻找一种更好的方法,用它们相应的爪对结果进行排序。

对于任何面临挑战的人,我已经用包含每个爪(测量捆绑)压力数据的所有切片阵列和描述其位置的切片(在板上的位置和时间)来腌制字典。

对Clarfy来说: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'),表示提取的影响。

还应注意,可以忽略"错误"的影响,例如部分测量爪的位置(空间或时间)。它们之所以有用,是因为它们可以帮助识别模式,但是不会被分析。

对于任何有兴趣的人,我会写一个关于这个项目的所有更新的博客!


好吧!我终于找到了一个始终如一的工作!这个问题把我拉了好几天…好玩的东西!很抱歉这个回答太长了,但我需要详细说明一些事情…(尽管我可以为有史以来最长的非垃圾邮件堆栈溢出答案设置一个记录!)好的。

作为补充说明,我使用的是Ivo在原始问题中提供链接的完整数据集。它是一系列RAR文件(每只狗一个),每个文件包含几个不同的实验运行,存储为ASCII数组。这里是一个包含完整、独立代码的BitBucket Mercurial存储库,而不是试图将粘贴的独立代码示例复制到这个问题中。你可以复制它好的。

hg clone https://[email protected]/joferkington/paw-analysis好的。

概述好的。

正如您在问题中提到的,基本上有两种方法来处理这个问题。实际上,我会用不同的方式来使用这两种方法。好的。

  • 使用爪冲击的(时间和空间)顺序来确定哪个爪是哪个。
  • 试着仅仅根据"爪印"的形状来识别它。
  • 基本上,第一种方法适用于狗的爪,遵循上面Ivo问题中所示的梯形模式,但当爪不遵循该模式时失败。当程序不起作用时,很容易检测到。好的。

    因此,我们可以使用它所做的测量来建立一个训练数据集(约2000只来自约30只不同狗的爪撞击),以识别哪只是哪个,并且问题减少到一个有监督的分类(有一些额外的皱纹…图像识别比"正常"监督分类问题要困难一点)。好的。

    模式分析好的。

    详细说明第一种方法,当狗走路时(不跑步!)正常情况下(有些狗可能不是),我们预计爪会按顺序碰撞:左前、右后、右前、左后、左前等。模式可以从左前或右前爪开始。好的。

    如果总是这样的话,我们可以简单地通过初始接触时间对冲击进行分类,并使用模块4按PAW对其进行分组。好的。

    Normal Impact Sequence好的。

    然而,即使一切都是"正常"的,这也行不通。这是由于梯形形状的图案。后爪在空间上落在前爪的后面。好的。

    因此,前爪初始撞击后的后爪撞击经常从传感器板上脱落,并且没有记录下来。同样,最后一个爪冲击通常不是序列中的下一个爪,因为在其发生之前,爪冲击从传感器板上发生,并且没有被记录。好的。

    Missed Hind Paw好的。

    尽管如此,我们可以使用爪冲击模式的形状来确定发生这种情况的时间,以及我们是从左前爪还是右前爪开始。(我实际上忽略了最后一个影响的问题。不过,添加它并不难。)好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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[code] for code in paw_codes]

        return paw_labels

    尽管如此,它经常不能正常工作。在完整的数据集中,许多狗似乎在奔跑,而爪的撞击并不像狗走路时那样遵循相同的时间顺序。(或者也许狗只是有严重的臀部问题…)好的。

    Abnormal Impact Sequence好的。

    幸运的是,我们仍然可以通过编程检测爪的影响是否符合我们预期的空间模式:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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

    因此,即使简单的空间分类并不总是有效的,我们也可以确定它何时能以合理的置信度工作。好的。

    培训数据集好的。

    从正确工作的基于模式的分类中,我们可以建立一个非常大的训练数据集,包含正确分类的爪子(大约2400只来自32只不同的狗的爪冲击!).好的。

    我们现在可以开始研究"一般"的左前角,等等,爪子是什么样子的。好的。

    要做到这一点,我们需要某种"爪度量",对于任何狗来说都是相同的维度。(在完整的数据集中,既有非常大的狗,也有非常小的狗!)爱尔兰麋鹿的爪印要比玩具贵宾犬的爪印宽得多,也要重得多。我们需要重新缩放每个爪印,以便a)它们具有相同数量的像素,b)压力值是标准化的。为此,我将每个爪印重新取样到20x20网格上,并根据爪印冲击的最大、最小和平均压力值重新调整压力值。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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

    在所有这些之后,我们终于可以看看一个平均的左前,右后等爪是什么样子的。请注意,这是平均超过30只不同大小的狗,我们似乎得到了一致的结果!好的。

    Average Paws好的。

    然而,在我们对这些做任何分析之前,我们需要减去平均值(所有狗的所有腿的平均爪)。好的。

    Mean Paw好的。

    现在我们可以分析与平均值之间的差异,这更容易识别:好的。

    Differential Paws好的。

    基于图像的爪形识别好的。

    好啊。。。我们终于有了一套模式,我们可以开始尝试将爪子与之匹配。每个爪可以被视为400维矢量(由paw_image函数返回),可以与这4个400维矢量进行比较。好的。

    不幸的是,如果我们只使用"普通"监督分类算法(即,使用简单的距离找出4个模式中哪一个最接近特定的爪印),它就不能始终如一地工作。事实上,它在训练数据集上的随机机会并没有做得更好。好的。

    这是图像识别中的一个常见问题。由于输入数据的高维性,以及图像的某种"模糊"性质(即相邻像素具有很高的协方差),仅仅看图像与模板图像之间的差异并不能很好地衡量它们形状的相似性。好的。

    eigenpaws

    /好的。

    对"绕过这个,我们需要建立一个集"eigenpaws"(就像"脸"在面部识别),和描述每个爪子打印作为一个组合,这些eigenpaws。。。。。。。这是对委托编号和部件的分析,基本上提供一种方式来减少我们的数据的维数,所以这是一个很好的距离测量的形状。

    /好的。

    因为我们有更多的训练图像的尺寸比(2400-400),有没有需要做的"幻想"的线性代数方法的速度。我们可以直接到工作与协方差矩阵的训练数据集。

    /好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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"。

    /好的。

    Eigenpaws

    /好的。

    对使用这些,我们简单的点(即矩阵最小二乘法)每一爪的图像(作为一个4维向量,而比A 20x20图像与矢量)的基础上的。这给了我们一个50维向量(每一元的基础上的矢量),我们可以使用的classify图像。去大学的比较(一20x20图像的图像20x20 of each"模板"的爪子,我们compare 50维图像,转变到每50维转变模板的爪子。这是更不敏感的小的变化在exactly如何每趾是positioned等,基本上影响的维度的问题是相关的尺寸。

    /好的。

    eigenpaw爪型分类

    /好的。

    现在,我们可以简单的使用之间的距离50维矢量和矢量的"模板"为每个腿的classify '爪'是:

    /好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    /好的。

    剩余的问题

    /好的。

    这仍然是有问题的,是与狗太小让一个明确的爪印的…………………(它的作品最好用的大狗,为的是个脚趾更明确分离的传感器的分辨率。)也部分pawprints不是T recognized与这个系统,当他们可以与梯形型为基础的系统。

    /好的。

    然而,因为eigenpaw分析inherently A使用的距离度量,我们可以classify狗爪中的两种方式,和后退的梯形型为基础的系统,当eigenpaw分析的smallest距离从"码"结束的临界点。我没implemented这个还没,不过。。。。。。。

    /好的。

    phew…………………这是龙!!!!!!!我的帽子是截止到伊沃方法具有这样的有趣的问题!!!!!!!

    /好的。 好的。


    使用纯粹基于持续时间的信息,我认为您可以应用运动学建模的技术,即逆运动学。结合方向、长度、持续时间和总重量,它给出了一定程度的周期性,我希望这可能是第一步,试图解决你的"爪子排序"问题。

    所有这些数据都可以用来创建一个有界多边形(或元组)的列表,您可以使用它按步长排序,然后按paw ness[索引]。


    您是否可以让技术人员手动运行测试,输入第一个棘爪(或前两个)?过程可能是:

    • 向技术人员展示步骤图像的顺序,并要求他们注释第一个爪。
    • 根据第一个棘爪标记其他棘爪,并允许技术人员进行更正或重新运行测试。这允许跛足或3条腿的狗。