一尘不染

Python-二维阵列中的峰值检测

python

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

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

因此,我尝试了一些实验,并决定只寻找每一列和每一行的最大值(由于爪子的形状而无法朝一个方向看)。这似乎可以很好地“检测”到各个脚趾的位置,但是它也标记了相邻的传感器。

UyNRU.png

那么,告诉Python我想要的最大最大值是什么?

注意:2x2的正方形不能重叠,因为它们必须是单独的脚趾!

同样我以2x2为方便,欢迎使用任何更高级的解决方案,但我只是人类运动的科学家,所以我既不是真正的程序员也不是数学家,所以请保持“简单”。

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

结果
因此,我尝试了@jextee的解决方案(请参见下面的结果)。如您所见,它在前爪上非常有效,但在后腿上效果较差。

更具体地说,它无法识别出第四脚趾的小峰。显然,这是回路从上向下朝着最低值而不考虑这在哪里的事实所固有的。

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

FFX0x.png

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

此图显示了它们如何在空间上分布在板上。

iPXEv.png

新更新:
因此,在获得有关爪子检测和爪子分类的问题的帮助后,我终于能够检查每个爪子的脚趾检测!事实证明,除了爪子大小像我自己的示例中的爪子一样,它在任何情况下都无法正常工作。事后看来,如此随意地选择2x2是我自己的错。

这是一个出问题的好例子:指甲被识别为脚趾,而“脚跟”是如此之宽,被识别两次!
JEpIa.png

脚掌太大,因此采用2x2大小且没有重叠的脚掌会使两次脚趾被检测到两次。相反,在小型犬中,它通常找不到第5个脚趾,我怀疑这是2x2区域太大造成的。

在尝试了所有解决方案的最新解决方案后,我得出了一个惊人的结论:几乎对我所有的小型犬来说,它都找不到第五个脚趾,而在大型犬的50%以上的撞击中,它会发现更多!

所以很明显我需要更改它。我自己的猜测是将大小更改neighborhood为小型犬较小,大型犬较大。但是generate_binary_structure不允许我更改数组的大小。

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


阅读 1109

收藏
2020-02-21

共1个答案

一尘不染

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

Kgt4H.png

我还在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在蒙版上使用以标记所有不同的对象。这样你就可以分别与他们一起玩了。

请注意,该方法效果很好,因为背景不嘈杂。如果是这样,你将在背景中检测到许多其他不需要的峰。另一个重要因素是邻里的大小。如果峰大小发生变化,则需要对其进行调整(应保持大致成比例)。

2020-02-21