Python 二维阵列中的峰值检测

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3684484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:14:38  来源:igfitidea点击:

Peak detection in a 2D array

pythonimage-processing

提问by Ivo Flipse

I'm helping a veterinary clinic measuring pressure under a dogs paw. I use Python for my data analysis and now I'm stuck trying to divide the paws into (anatomical) subregions.

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

I made a 2D array of each paw, that consists of the maximal values for each sensor that has been loaded by the paw over time. Here's an example of one paw, where I used Excel to draw the areas I want to 'detect'. These are 2 by 2 boxes around the sensor with local maxima's, that together have the largest sum.

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

alt text

替代文字

So I tried some experimenting and decide to simply look for the maximums of each column and row (can't look in one direction due to the shape of the paw). This seems to 'detect' the location of the separate toes fairly well, but it also marks neighboring sensors.

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

alt text

替代文字

So what would be the best way to tell Python which of these maximums are the ones I want?

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

Note: The 2x2 squares can't overlap, since they have to be separate toes!

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

Also I took 2x2 as a convenience, any more advanced solution is welcome, but I'm simply a human movement scientist, so I'm neither a real programmer or a mathematician, so please keep it 'simple'.

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

Here's a version that can be loaded with np.loadtxt

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



Results

结果

So I tried @jextee's solution (see the results below). As you can see, it works very on the front paws, but it works less well for the hind legs.

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

More specifically, it can't recognize the small peak that's the fourth toe. This is obviously inherent to the fact that the loop looks top down towards the lowest value, without taking into account where this is.

更具体地说,它无法识别第四个脚趾的小峰。这显然是循环自上而下朝向最低值的事实所固有的,而不考虑最低值的位置。

Would anyone know how to tweak @jextee's algorithm, so that it might be able to find the 4th toe too?

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

alt text

替代文字

Since I haven't processed any other trials yet, I can't supply any other samples. But the data I gave before were the averages of each paw. This file is an array with the maximal data of 9 paws in the order they made contact with the plate.

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

This image shows how they were spatially spread out over the plate.

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

alt text

替代文字

Update:

更新:

I have set up a blog for anyone interestedand I have setup a SkyDrive with all the raw measurements.So to anyone requesting more data: more power to you!

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



New update:

新更新:

So after the help I got with my questions regarding paw detectionand paw sorting, I was finally able to check the toe detection for every paw! Turns out, it doesn't work so well in anything but paws sized like the one in my own example. Off course in hindsight, it's my own fault for choosing the 2x2 so arbitrarily.

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

Here's a nice example of where it goes wrong: a nail is being recognized as a toe and the 'heel' is so wide, it gets recognized twice!

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

alt text

替代文字

The paw is too large, so taking a 2x2 size with no overlap, causes some toes to be detected twice. The other way around, in small dogs it often fails to find a 5th toe, which I suspect is being caused by the 2x2 area being too large.

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

After trying the current solution on all my measurementsI came to the staggering conclusion that for nearly all my small dogs it didn't find a 5th toe and that in over 50% of the impacts for the large dogs it would find more!

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

So clearly I need to change it. My own guess was changing the size of the neighborhoodto something smaller for small dogs and larger for large dogs. But generate_binary_structurewouldn't let me change the size of the array.

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

Therefore, I'm hoping that anyone else has a better suggestion for locating the toes, perhaps having the toe area scale with the paw size?

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

采纳答案by Ivan

I detected the peaks using a local maximum filter. Here is the result on your first dataset of 4 paws: Peaks detection result

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

I also ran it on the second dataset of 9 paws and it worked as well.

我还在 9 个爪子的第二个数据集上运行它,它也运行良好

Here is how you do it:

这是你如何做到的:

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()

All you need to do after is use scipy.ndimage.measurements.labelon the mask to label all distinct objects. Then you'll be able to play with them individually.

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

Notethat the method works well because the background is not noisy. If it were, you would detect a bunch of other unwanted peaks in the background. Another important factor is the size of the neighborhood. You will need to adjust it if the peak size changes (the should remain roughly proportional).

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

回答by ChrisC

Just a couple of ideas off the top of my head:

我脑子里只有几个想法:

  • take the gradient (derivative) of the scan, see if that eliminates the false calls
  • take the maximum of the local maxima
  • 取扫描的梯度(导数),看看是否消除了错误调用
  • 取局部最大值的最大值

You might also want to take a look at OpenCV, it's got a fairly decent Python API and might have some functions you'd find useful.

您可能还想看看OpenCV,它有一个相当不错的 Python API,并且可能有一些您会觉得有用的功能。

回答by Johannes Charra

Maybe a naive approach is sufficient here: Build a list of all 2x2 squares on your plane, order them by their sum (in descending order).

也许这里有一个简单的方法就足够了:在你的飞机上建立一个所有 2x2 方块的列表,按它们的总和(降序)排序。

First, select the highest-valued square into your "paw list". Then, iteratively pick 4 of the next-best squares that don't intersect with any of the previously found squares.

首先,选择价值最高的方块到您的“爪子列表”中。然后,迭代地选择 4 个不与之前找到的任何正方形相交的次佳正方形。

回答by Eric O Lebigot

Here is an idea: you calculate the (discrete) Laplacian of the image. I would expect it to be (negative and) large at maxima, in a way that is more dramatic than in the original images. Thus, maxima could be easier to find.

这是一个想法:您计算图像的(离散)拉普拉斯算子。我希望它在最大值处(负和)大,以比原始图像更具戏剧性的方式。因此,最大值可能更容易找到。

Here is another idea: if you know the typical size of the high-pressure spots, you can first smooth your image by convoluting it with a Gaussian of the same size. This may give you simpler images to process.

这是另一个想法:如果您知道高压点的典型大小,您可以首先通过将其与相同大小的高斯卷积来平滑您的图像。这可能会为您提供更简单的图像来处理。

回答by Cedric H.

What if you proceed step by step: you first locate the global maximum, process if needed the surrounding points given their value, then set the found region to zero, and repeat for the next one.

如果您一步一步地进行会怎样:首先找到全局最大值,根据需要处理给定值的周围点,然后将找到的区域设置为零,然后对下一个区域重复。

回答by sastanin

Solution

解决方案

Data file: paw.txt. Source code:

数据文件:paw.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"

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

输出没有重叠的方块。似乎选择了与您的示例相同的区域。

Some comments

一些评论

The tricky part is to calculate sums of all 2x2 squares. I assumed you need all of them, so there might be some overlapping. I used slices to cut the first/last columns and rows from the original 2D array, and then overlapping them all together and calculating sums.

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

To understand it better, imaging a 3x3 array:

为了更好地理解它,对 3x3 阵列进行成像:

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

Then you can take its slices:

然后你可以取它的切片:

>>> 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]])

Now imagine you stack them one above the other and sum elements at the same positions. These sums will be exactly the same sums over the 2x2 squares with the top-left corner in the same position:

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

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

When you have the sums over 2x2 squares, you can use maxto find the maximum, or sort, or sortedto find the peaks.

当您的总和超过 2x2 平方时,您可以使用max找到最大值,或者sort,或者sorted找到峰值。

To remember positions of the peaks I couple every value (the sum) with its ordinal position in a flattened array (see zip). Then I calculate row/column position again when I print the results.

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

Notes

笔记

I allowed for the 2x2 squares to overlap. Edited version filters out some of them such that only non-overlapping squares appear in the results.

我允许 2x2 方块重叠。编辑版本过滤掉了其中的一些,以便结果中只出现不重叠的方块。

Choosing fingers (an idea)

选择手指(一个想法)

Another problem is how to choose what is likely to be fingers out of all the peaks. I have an idea which may or may not work. I don't have time to implement it right now, so just pseudo-code.

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

I noticed that if the front fingers stay on almost a perfect circle, the rear finger should be inside of that circle. Also, the front fingers are more or less equally spaced. We may try to use these heuristic properties to detect the fingers.

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

Pseudo code:

伪代码:

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

This is a brute-force approach. If N is relatively small, then I think it is doable. For N=12, there are C_12^5 = 792 combinations, times 5 ways to select a rear finger, so 3960 cases to evaluate for every paw.

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

回答by Justin Peel

Well, here's some simple and not terribly efficient code, but for this size of a data set it is fine.

好吧,这里有一些简单但效率不高的代码,但对于这种大小的数据集来说,它很好。

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]

I basically just make an array with the position of the upper-left and the sum of each 2x2 square and sort it by the sum. I then take the 2x2 square with the highest sum out of contention, put it in the bestarray, and remove all other 2x2 squares that used any part of this just removed 2x2 square.

我基本上只是用左上角的位置和每个 2x2 正方形的总和制作一个数组,然后按总和对其进行排序。然后我从争用中取出总和最高的 2x2 方格,将其放入best数组中,并删除所有其他 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.

除了最后一个爪子(第一张图片中最右侧的总和最小的那个)之外,它似乎工作正常,结果证明还有另外两个符合条件的 2x2 方格的总和更大(并且它们的总和等于彼此)。其中一个仍然从您的 2x2 方格中选择一个方格,但另一个在左侧。幸运的是,幸运的是,我们看到选择了更多您想要的东西,但这可能需要使用其他一些想法来始终获得您真正想要的东西。

回答by CakeMaster

This is an image registration problem. The general strategy is:

这是一个图像配准问题。一般策略是:

  • Have a known example, or some kind of prioron the data.
  • Fit your data to the example, or fit the example to your data.
  • It helps if your data is roughlyaligned in the first place.
  • 有一个已知的例子,或者数据的某种先验
  • 使您的数据适合示例,或使示例适合您的数据。
  • 如果您的数据首先大致对齐,这会有所帮助。

Here's a rough and ready approach, "the dumbest thing that could possibly work":

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

  • Start with five toe coordinates in roughly the place you expect.
  • With each one, iteratively climb to the top of the hill. i.e. given current position, move to maximum neighbouring pixel, if its value is greater than current pixel. Stop when your toe coordinates have stopped moving.
  • 从大约您期望的位置的五个脚趾坐标开始。
  • 每一个,迭代爬到山顶。即给定当前位置,如果其值大于当前像素,则移动到最大相邻像素。当您的脚趾坐标停止移动时停止。

To counteract the orientation problem, you could have 8 or so initial settings for the basic directions (North, North East, etc). Run each one individually and throw away any results where two or more toes end up at the same pixel. I'll think about this some more, but this kind of thing is still being researched in image processing - there are no right answers!

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

Slightly more complex idea: (weighted) K-means clustering.It's not that bad.

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

  • Start with five toe coordinates, but now these are "cluster centres".
  • 从五个脚趾坐标开始,但现在这些是“簇中心”。

Then iterate until convergence:

然后迭代直到收敛:

  • Assign each pixel to the closest cluster (just make a list for each cluster).
  • Calculate the center of mass of each cluster. For each cluster, this is: Sum(coordinate * intensity value)/Sum(coordinate)
  • Move each cluster to the new centre of mass.
  • 将每个像素分配给最近的集群(只需为每个集群创建一个列表)。
  • 计算每个簇的质心。对于每个集群,这是:Sum(坐标 * 强度值)/Sum(坐标)
  • 将每个簇移动到新的质心。

This method will almost certainly give much better results, and you get the mass of each cluster which may help in identifying the toes.

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

(Again, you've specified the number of clusters up front. With clustering you have to specify the density one way or another: Either choose the number of clusters, appropriate in this case, or choose a cluster radius and see how many you end up with. An example of the latter is mean-shift.)

(同样,您已经预先指定了集群的数量。对于集群,您必须以一种或另一种方式指定密度:要么选择适合这种情况的集群数量,要么选择集群半径并查看您结束的数量up with。后者的一个例子是mean-shift。)

Sorry about the lack of implementation details or other specifics. I would code this up but I've got a deadline. If nothing else has worked by next week let me know and I'll give it a shot.

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

回答by geoff

It seems you can cheat a bit using jetxee's algorithm. He is finding the first three toes fine, and you should be able to guess where the fourth is based off that.

似乎您可以使用 jetxee 的算法作弊。他发现前三个脚趾没问题,你应该能够根据它猜出第四个脚趾在哪里。

回答by Rick Hull

I am not sure this answers the question, but it seems like you can just look for the n highest peaks that don't have neighbors.

我不确定这是否能回答这个问题,但似乎您可以只寻找没有邻居的 n 个最高峰。

Here is the gist.Note that it's in Ruby, but the idea should be clear.

这是要点。请注意,它是用 Ruby 编写的,但思路应该很清楚。

require 'pp'

NUM_PEAKS = 5
NEIGHBOR_DISTANCE = 1

data = [[1,2,3,4,5],
        [2,6,4,4,6],
        [3,6,7,4,3],
       ]

def tuples(matrix)
  tuples = []
  matrix.each_with_index { |row, ri|
    row.each_with_index { |value, ci|
      tuples << [value, ri, ci]
    }
  }
  tuples
end

def neighbor?(t1, t2, distance = 1)
  [1,2].each { |axis|
    return false if (t1[axis] - t2[axis]).abs > distance
  }
  true
end

# convert the matrix into a sorted list of tuples (value, row, col), highest peaks first
sorted = tuples(data).sort_by { |tuple| tuple.first }.reverse

# the list of peaks that don't have neighbors
non_neighboring_peaks = []

sorted.each { |candidate|
  # always take the highest peak
  if non_neighboring_peaks.empty?
    non_neighboring_peaks << candidate
    puts "took the first peak: #{candidate}"
  else
    # check that this candidate doesn't have any accepted neighbors
    is_ok = true
    non_neighboring_peaks.each { |accepted|
      if neighbor?(candidate, accepted, NEIGHBOR_DISTANCE)
        is_ok = false
        break
      end
    }
    if is_ok
      non_neighboring_peaks << candidate
      puts "took #{candidate}"
    else
      puts "denied #{candidate}"
    end
  end
}

pp non_neighboring_peaks

回答by Paulus

Heres another approach that I used when doing something similar for a large telescope:

这是我在为大型望远镜做类似事情时使用的另一种方法:

1) Search for the highest pixel. Once you have that, search around that for the best fit for 2x2 (maybe maximizing the 2x2 sum), or do a 2d gaussian fit inside the sub region of say 4x4 centered on the highest pixel.

1) 搜索最高像素。一旦你有了它,在它周围搜索最适合 2x2(可能最大化 2x2 总和),或者在以最高像素为中心的 4x4 子区域内进行 2d 高斯拟合。

Then set those 2x2 pixels you have found to zero (or maybe 3x3) around the peak center

然后将您发现的那些 2x2 像素设置为零(或者可能是 3x3)围绕峰值中心

go back to 1) and repeat till the highest peak falls below a noise threshold, or you have all the toes you need

回到 1) 并重复直到最高峰值低于噪声阈值,或者您拥有所需的所有脚趾