我在帮助一家兽医诊所测量狗爪下的压力。我使用Python进行数据分析,现在我正试图将爪子划分为(解剖学上的)子区域。

我为每个爪子制作了一个2D数组,其中包括爪子随时间加载的每个传感器的最大值。这里有一个单爪的例子,我使用Excel绘制我想要“检测”的区域。传感器周围是2 * 2的方框带有局部最大值,它们加起来的和最大。

所以我尝试了一些实验,并决定简单地寻找每一列和每一行的最大值(由于爪子的形状,不能只看一个方向)。这似乎能很好地“检测”到不同脚趾的位置,但也能标记出相邻的传感器。

那么告诉Python哪些最大值是我想要的最好方法是什么呢?

注意:2x2的方块不能重叠,因为它们必须是分开的脚趾!

此外,我选择了2x2作为方便,任何更高级的解决方案都是受欢迎的,但我只是一个人类运动科学家,所以我既不是真正的程序员也不是数学家,所以请保持“简单”。

下面是一个可以用np.loadtxt加载的版本


结果

所以我尝试了@jextee的解决方案(见下面的结果)。正如你所看到的,它对前爪很有效,但对后腿就不那么有效了。

更具体地说,它无法识别第四个脚趾的小峰。这显然是固有的事实,即循环从上向下看最低值,而不考虑它在哪里。

有没有人知道如何调整@jextee的算法,让它也能找到第四个脚趾?

因为我还没有处理其他的试验,所以我无法提供其他的样品。但我之前给出的数据是每个爪子的平均值。这个文件是一个数组,其中包含9只爪子的最大数据,按照它们与盘子接触的顺序排列。

这张照片显示了它们在空间上是如何分布在板块上的。

更新:

我已经为任何感兴趣的人建立了一个博客,我已经设置了一个OneDrive与所有的原始测量。因此,对于任何要求更多数据的人:给你更多力量!


新更新:

所以在我得到关于爪子检测和爪子分类的问题的帮助后,我终于能够检查每个爪子的脚趾检测!事实证明,除了像我自己例子中那样大的爪子,它在其他地方都不太管用。当然,事后看来,是我自己的错,我这么武断地选择了2x2。

这里有一个很好的例子,它错在哪里:指甲被识别为脚趾,而“脚跟”太宽了,它被识别了两次!

爪子太大了,所以采用没有重叠的2x2尺寸,会导致一些脚趾被检测两次。反过来,在小型犬中,它经常找不到第5个脚趾,我怀疑这是由于2x2面积太大造成的。

在对我所有的测量都尝试了当前的解决方案后,我得出了一个惊人的结论,几乎所有的小狗都没有发现第5个脚趾,而在超过50%的大型狗的冲击中,它会发现更多!

显然我需要改变它。我自己的想法是改变社区的大小,让小狗住的地方更小,大狗住的地方更大。但是generate_binary_structure不允许我改变数组的大小。

因此,我希望其他人有更好的建议来定位脚趾,也许是脚趾面积与爪子大小的比例?


当前回答

好吧,这里有一些简单而不是非常有效的代码,但对于这样大的数据集来说,这是很好的。

import numpy as np
grid = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0.4,0.4,0.4,0,0,0],
              [0,0,0,0,0.4,1.4,1.4,1.8,0.7,0,0,0,0,0],
              [0,0,0,0,0.4,1.4,4,5.4,2.2,0.4,0,0,0,0],
              [0,0,0.7,1.1,0.4,1.1,3.2,3.6,1.1,0,0,0,0,0],
              [0,0.4,2.9,3.6,1.1,0.4,0.7,0.7,0.4,0.4,0,0,0,0],
              [0,0.4,2.5,3.2,1.8,0.7,0.4,0.4,0.4,1.4,0.7,0,0,0],
              [0,0,0.7,3.6,5.8,2.9,1.4,2.2,1.4,1.8,1.1,0,0,0],
              [0,0,1.1,5,6.8,3.2,4,6.1,1.8,0.4,0.4,0,0,0],
              [0,0,0.4,1.1,1.8,1.8,4.3,3.2,0.7,0,0,0,0,0],
              [0,0,0,0,0,0.4,0.7,0.4,0,0,0,0,0,0]])

arr = []
for i in xrange(grid.shape[0] - 1):
    for j in xrange(grid.shape[1] - 1):
        tot = grid[i][j] + grid[i+1][j] + grid[i][j+1] + grid[i+1][j+1]
        arr.append([(i,j),tot])

best = []

arr.sort(key = lambda x: x[1])

for i in xrange(5):
    best.append(arr.pop())
    badpos = set([(best[-1][0][0]+x,best[-1][0][1]+y)
                  for x in [-1,0,1] for y in [-1,0,1] if x != 0 or y != 0])
    for j in xrange(len(arr)-1,-1,-1):
        if arr[j][0] in badpos:
            arr.pop(j)


for item in best:
    print grid[item[0][0]:item[0][0]+2,item[0][1]:item[0][1]+2]

我基本上只是用左上角的位置和每个2x2平方的和来做一个数组,然后根据和来排序。然后我把和最高的2x2平方从竞争中取出,把它放在最好的数组中,然后删除所有其他使用了这个2x2平方的部分的2x2平方。

It seems to work fine except with the last paw (the one with the smallest sum on the far right in your first picture), it turns out that there are two other eligible 2x2 squares with a larger sum (and they have an equal sum to each other). One of them is still selects one square from your 2x2 square, but the other is off to the left. Fortunately, by luck we see to be choosing more of the one that you would want, but this may require some other ideas to be used to get what you actually want all of the time.

其他回答

好吧,这里有一些简单而不是非常有效的代码,但对于这样大的数据集来说,这是很好的。

import numpy as np
grid = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
              [0,0,0,0,0,0,0,0,0.4,0.4,0.4,0,0,0],
              [0,0,0,0,0.4,1.4,1.4,1.8,0.7,0,0,0,0,0],
              [0,0,0,0,0.4,1.4,4,5.4,2.2,0.4,0,0,0,0],
              [0,0,0.7,1.1,0.4,1.1,3.2,3.6,1.1,0,0,0,0,0],
              [0,0.4,2.9,3.6,1.1,0.4,0.7,0.7,0.4,0.4,0,0,0,0],
              [0,0.4,2.5,3.2,1.8,0.7,0.4,0.4,0.4,1.4,0.7,0,0,0],
              [0,0,0.7,3.6,5.8,2.9,1.4,2.2,1.4,1.8,1.1,0,0,0],
              [0,0,1.1,5,6.8,3.2,4,6.1,1.8,0.4,0.4,0,0,0],
              [0,0,0.4,1.1,1.8,1.8,4.3,3.2,0.7,0,0,0,0,0],
              [0,0,0,0,0,0.4,0.7,0.4,0,0,0,0,0,0]])

arr = []
for i in xrange(grid.shape[0] - 1):
    for j in xrange(grid.shape[1] - 1):
        tot = grid[i][j] + grid[i+1][j] + grid[i][j+1] + grid[i+1][j+1]
        arr.append([(i,j),tot])

best = []

arr.sort(key = lambda x: x[1])

for i in xrange(5):
    best.append(arr.pop())
    badpos = set([(best[-1][0][0]+x,best[-1][0][1]+y)
                  for x in [-1,0,1] for y in [-1,0,1] if x != 0 or y != 0])
    for j in xrange(len(arr)-1,-1,-1):
        if arr[j][0] in badpos:
            arr.pop(j)


for item in best:
    print grid[item[0][0]:item[0][0]+2,item[0][1]:item[0][1]+2]

我基本上只是用左上角的位置和每个2x2平方的和来做一个数组,然后根据和来排序。然后我把和最高的2x2平方从竞争中取出,把它放在最好的数组中,然后删除所有其他使用了这个2x2平方的部分的2x2平方。

It seems to work fine except with the last paw (the one with the smallest sum on the far right in your first picture), it turns out that there are two other eligible 2x2 squares with a larger sum (and they have an equal sum to each other). One of them is still selects one square from your 2x2 square, but the other is off to the left. Fortunately, by luck we see to be choosing more of the one that you would want, but this may require some other ideas to be used to get what you actually want all of the time.

这里有一个想法:你计算图像的(离散)拉普拉斯量。我希望它在最大值处是(负的)大的,以一种比原始图像更引人注目的方式。因此,极大值更容易找到。

这里有另一个想法:如果你知道高压点的典型大小,你可以首先通过用相同大小的高斯函数卷积来平滑你的图像。这可能会让你更容易处理图像。

如果你能够创建一些训练数据,那么使用神经网络可能是值得尝试的……但这需要手工标注许多样本。

有趣的问题。我想尝试的解决方案如下。

应用一个低通滤波器,比如用二维高斯掩码卷积。这将给你一堆(可能,但不一定是浮点数)值。 使用每个爪垫(或脚趾)的已知近似半径执行2D非最大抑制。

这将为你提供最大的职位,而不是有多个候选人。澄清一下,第1步中蒙版的半径也应该与第2步中使用的半径相似。这个半径是可以选择的,或者兽医可以事先明确测量它(它会随着年龄/品种等而变化)。

建议的一些解决方案(均值漂移、神经网络等)可能在某种程度上是可行的,但它们过于复杂,可能并不理想。

大概的轮廓……

你可能想要使用连接组件算法来隔离每个爪子区域。wiki对此有一个很好的描述(有一些代码):http://en.wikipedia.org/wiki/Connected_Component_Labeling

你必须决定是使用4连通性还是8连通性。就我个人而言,对于大多数问题,我更喜欢6连通性。无论如何,一旦你将每个“爪印”分离为一个连通区域,就很容易遍历该区域并找到最大值。一旦你找到了最大值,你就可以迭代地扩大区域,直到达到预定的阈值,以确定它是给定的“脚趾”。

这里有一个微妙的问题是,一旦你开始使用计算机视觉技术来识别某个东西是右/左/前/后爪,你开始观察单个脚趾,你就必须开始考虑旋转、倾斜和平移。这是通过分析所谓的“矩”来完成的。在视觉应用中有几个不同的时刻需要考虑:

中心矩:平移不变量 归一化矩:缩放和平移不变量 胡矩:平移、尺度和旋转不变量

更多关于moments的信息可以在wiki上搜索“image moments”。