首页 > Python >  Python > 2d二维阵列中的峰值检测

 Python > 2d二维阵列中的峰值检测

上一篇 下一篇

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

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

alt text

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

alt text

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

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

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

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


结果

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

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

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

alt text

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

该图像显示了它们如何在空间上分布在盘子上。

alt text

更新:

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


新更新:

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

这里有一个很好的例子来说明它出错的地方:指甲被识别为脚趾,“脚跟”很宽,它被识别了两次!

alt text

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

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

很明显,我需要改变它。我自己的猜测是将小型犬的大小更改为较小的尺寸,而大型犬的大小则更大。但是不让我更改数组的大小。neighborhoodgenerate_binary_structure

因此,我希望其他人对定位脚趾有更好的建议,也许脚趾区域与爪子大小一起缩放?

分割线

网友回答:

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

我还在 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

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

分割线

网友回答:

溶液

数据文件:爪子.txt。源代码:

from scipy import *
from operator import itemgetter

n = 5  # how many fingers are we looking for

d = loadtxt("paw.txt")
width, height = d.shape

# Create an array where every element is a sum of 2x2 squares.

fourSums = d[:-1,:-1] + d[1:,:-1] + d[1:,1:] + d[:-1,1:]

# Find positions of the fingers.

# Pair each sum with its position number (from 0 to width*height-1),

pairs = zip(arange(width*height), fourSums.flatten())

# Sort by descending sum value, filter overlapping squares

def drop_overlapping(pairs):
    no_overlaps = []
    def does_not_overlap(p1, p2):
        i1, i2 = p1[0], p2[0]
        r1, col1 = i1 / (width-1), i1 % (width-1)
        r2, col2 = i2 / (width-1), i2 % (width-1)
        return (max(abs(r1-r2),abs(col1-col2)) >= 2)
    for p in pairs:
        if all(map(lambda prev: does_not_overlap(p,prev), no_overlaps)):
            no_overlaps.append(p)
    return no_overlaps

pairs2 = drop_overlapping(sorted(pairs, key=itemgetter(1), reverse=True))

# Take the first n with the heighest values

positions = pairs2[:n]

# Print results

print d, "n"

for i, val in positions:
    row = i / (width-1)
    column = i % (width-1)
    print "sum = %f @ %d,%d (%d)" % (val, row, column, i)
    print d[row:row+2,column:column+2], "n"

Output without overlapping squares. It seems that the same areas are selected as in your example.

一些评论

棘手的部分是计算所有 2×2 个平方的总和。我假设你需要所有这些,所以可能会有一些重叠。我使用切片从原始 2D 数组中剪切第一/最后一列和行,然后将它们重叠在一起并计算总和。

为了更好地理解它,对 3×3 数组进行成像:

>>> a = arange(9).reshape(3,3) ; a
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

然后你可以拿它的切片:

>>> a[:-1,:-1]
array([[0, 1],
       [3, 4]])
>>> a[1:,:-1]
array([[3, 4],
       [6, 7]])
>>> a[:-1,1:]
array([[1, 2],
       [4, 5]])
>>> a[1:,1:]
array([[4, 5],
       [7, 8]])

现在想象一下,你把它们一个堆叠在另一个上面,并在相同的位置对元素求和。这些总和将与左上角位于相同位置的 2×2 个正方形上的总和完全相同:

>>> sums = a[:-1,:-1] + a[1:,:-1] + a[:-1,1:] + a[1:,1:]; sums
array([[ 8, 12],
       [20, 24]])

当总和超过 2×2 平方时,可以使用 来查找最大值或 ,或者查找峰值。maxsortsorted

为了记住峰值的位置,我将每个值(总和)与其在扁平数组中的序号位置耦合(请参阅)。然后,当我打印结果时,我会再次计算行/列位置。zip

笔记

我允许 2×2 个正方形重叠。编辑后的版本会过滤掉其中一些,以便结果中仅显示不重叠的正方形。

选择手指(一个想法)

另一个问题是如何从所有峰中选择可能是手指的东西。我有一个想法,可能会也可能不会奏效。我现在没有时间实现它,所以只是伪代码。

我注意到,如果前指几乎停留在一个完美的圆圈上,那么后指应该在该圆圈内。此外,前手指或多或少地间隔相等。我们可以尝试使用这些启发式属性来检测手指。

伪代码:

select the top N finger candidates (not too many, 10 or 12)
consider all possible combinations of 5 out of N (use itertools.combinations)
for each combination of 5 fingers:
    for each finger out of 5:
        fit the best circle to the remaining 4
        => position of the center, radius
        check if the selected finger is inside of the circle
        check if the remaining four are evenly spread
        (for example, consider angles from the center of the circle)
        assign some cost (penalty) to this selection of 4 peaks + a rear finger
        (consider, probably weighted:
             circle fitting error,
             if the rear finger is inside,
             variance in the spreading of the front fingers,
             total intensity of 5 peaks)
choose a combination of 4 peaks + a rear peak with the lowest penalty

这是一种蛮力方法。如果N相对较小,那么我认为它是可行的。对于 N=12,有 C_12^5 = 792 种组合,乘以 5 种选择后指的方法,因此每只爪子要评估 3960 个案例。

分割线

网友回答:

这是一个映像配准问题。总体策略是:

  • 有一个已知的例子,或者某种先验的数据。
  • 将数据拟合到示例,或将示例拟合到数据。
  • 如果您的数据首先大致对齐,它会有所帮助。

这是一个粗略而现成的方法,“可能可行的最愚蠢的事情”:

  • 从大致预期位置的五个脚趾坐标开始。
  • 每一步,都反复爬到山顶。即给定当前位置,如果其值大于当前像素,则移动到最大相邻像素。当您的脚趾坐标停止移动时停止。

为了解决方向问题,您可以对基本方向(北、东北等)进行 8 个左右的初始设置。单独运行每个,并丢弃两个或多个脚趾最终位于同一像素的任何结果。我会再考虑一下,但这种事情仍在图像处理中研究——没有正确的答案!

稍微复杂一点的想法:(加权)K均值聚类。没那么糟糕。

  • 从五个脚趾坐标开始,但现在这些是“集群中心”。

然后迭代直到收敛:

  • 将每个像素分配给最近的聚类(只需为每个聚类创建一个列表)。
  • 计算每个聚类的质心。对于每个聚类,这是:总和(坐标 * 强度值)/总和(坐标)
  • 将每个聚类移动到新的质心。

这种方法几乎肯定会给出更好的结果,并且您可以获得每个集群的质量,这可能有助于识别脚趾。

(同样,您已经预先指定了集群数。使用聚类,您必须以一种或另一种方式指定密度:在这种情况下选择适当的聚类数量,或者选择一个聚类半径,看看您最终得到多少。后者的一个例子是均值偏移。

很抱歉缺少实现细节或其他细节。我会对此进行编码,但我有一个截止日期。如果下周没有其他工作,请告诉我,我会试一试。

模板简介:该模板名称为【 Python > 2d二维阵列中的峰值检测】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Python】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 281次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-09
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
自适应 微信模板 微信素材 html5 单页式简历模板 企业网站 微信文章 响应式 微信图片 微信公众平台
您可能会喜欢的其他模板