小编典典

二维阵列中的峰值检测

all

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

我为每个爪子制作了一个二维数组,其中包含爪子随时间加载的每个传感器的最大值。这是一只爪子的示例,我使用 Excel
绘制了我想要“检测”的区域。这些是传感器周围的 2 x 2 框,具有局部最大值,它们的总和最大。

替代文字

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

替代文字

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

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

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

这是一个可以加载的版本np.loadtxt


结果

所以我尝试了@jextee 的解决方案(见下面的结果)。如您所见,它在前爪上效果很好,但对后腿效果较差。

更具体地说,它无法识别第四趾的小峰。这显然是循环自上而下朝向最低值这一事实所固有的,而没有考虑到它在哪里。

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

替代文字

由于我还没有处理任何其他试验,我无法提供任何其他样品。但我之前给出的数据是每只爪子的平均值。该文件是一个数组,其中最大数据为 9
只爪子,按它们与板接触的顺序排列。

这张图片显示了它们是如何在空间上分布在盘子上的。

替代文字

更新:

我已经为任何感兴趣的人建立了一个博客, 并且我已经设置了一个包含所有原始测量值的
OneDrive。
所以对于任何需要更多数据的人:给你更多的权力!


新更新:

因此,在我得到有关爪子检测爪子分类问题的帮助后,我终于能够检查每只爪子的脚趾检测!事实证明,除了像我自己的例子中那样大小的爪子外,它在任何东西上都不能很好地发挥作用。当然,事后看来,如此武断地选择
2x2 是我自己的错。

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

替代文字

爪子太大,因此采用没有重叠的 2x2 大小,会导致某些脚趾被检测到两次。反过来,在小型犬中,它经常找不到第五个脚趾,我怀疑这是由于 2x2 区域太大造成的。

对我所有的测量结果尝试了当前的解决方案之后,我得出了一个惊人的结论,即对于我几乎所有的小型犬来说,它都没有找到第五个脚趾,而在大型犬的 50%
以上的影响中,它会发现更多!

所以很明显我需要改变它。我自己的猜测是把它的大小neighborhood改成适合小型犬的较小的尺寸和较大的大型犬的尺寸。但generate_binary_structure不会让我改变数组的大小。

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


阅读 120

收藏
2022-02-28

共1个答案

小编典典

我使用 局部最大值过滤器 检测到峰值。这是您的第一个 4 爪子数据集的结果:
峰值检测结果

我还在 9 只爪子的第二个数据集上运行了它,效果也很好

这是您的操作方法:

import numpy as np
from scipy.ndimage.filters import maximum_filter
from scipy.ndimage.morphology import generate_binary_structure, binary_erosion
import matplotlib.pyplot as pp

#for some reason I had to reshape. Numpy ignored the shape header.
paws_data = np.loadtxt("paws.txt").reshape(4,11,14)

#getting a list of images
paws = [p.squeeze() for p in np.vsplit(paws_data,4)]


def detect_peaks(image):
    """
    Takes an image and detect the peaks usingthe local maximum filter.
    Returns a boolean mask of the peaks (i.e. 1 when
    the pixel's value is the neighborhood maximum, 0 otherwise)
    """

    # define an 8-connected neighborhood
    neighborhood = generate_binary_structure(2,2)

    #apply the local maximum filter; all pixel of maximal value 
    #in their neighborhood are set to 1
    local_max = maximum_filter(image, footprint=neighborhood)==image
    #local_max is a mask that contains the peaks we are 
    #looking for, but also the background.
    #In order to isolate the peaks we must remove the background from the mask.

    #we create the mask of the background
    background = (image==0)

    #a little technicality: we must erode the background in order to 
    #successfully subtract it form local_max, otherwise a line will 
    #appear along the background border (artifact of the local maximum filter)
    eroded_background = binary_erosion(background, structure=neighborhood, border_value=1)

    #we obtain the final mask, containing only peaks, 
    #by removing the background from the local_max mask (xor operation)
    detected_peaks = local_max ^ eroded_background

    return detected_peaks


#applying the detection and plotting results
for i, paw in enumerate(paws):
    detected_peaks = detect_peaks(paw)
    pp.subplot(4,2,(2*i+1))
    pp.imshow(paw)
    pp.subplot(4,2,(2*i+2) )
    pp.imshow(detected_peaks)

pp.show()

之后您需要做的就是scipy.ndimage.measurements.label在遮罩上使用来标记所有不同的对象。然后你就可以单独和他们一起玩了。

请注意 ,该方法效果很好,因为背景没有噪音。如果是这样,您将在背景中检测到一堆其他不需要的峰。另一个重要因素是 社区
的规模。如果峰值大小发生变化,您将需要对其进行调整(应该保持大致成比例)。

2022-02-28