2D阵列中的峰值检测 [英] Peak detection in a 2D array

查看:112
本文介绍了2D阵列中的峰值检测的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我制作了每个爪子的2D数组,包括随着时间的推移,由爪子加载的每个传感器的最大值。这是一个爪子的例子,我用Excel绘制了我想要检测的区域。这些是带有局部最大值的传感器周围的2×2个盒子,它们总和最大。





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





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



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



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



这是 和我得出了令人吃惊的结论几乎所有我的小狗都没有找到第5个脚趾,超过50%的大型犬的影响会发现更多!



显然我需要改变它。我自己的猜测是将邻域的大小改为小型犬的较小型犬和大型犬的较大型犬。但是 generate_binary_structure 不允许我改变数组的大小。



因此,我希望其他人有更好的建议来定位脚趾,也许脚趾区域尺寸与爪子尺寸一致?

解决方案

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



我还在9个爪子的第二个数据集上运行它并它工作得很好



以下是您的工作方式:

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

#由于某种原因我不得不重塑。 Numpy忽略了形状标题。
paws_data = np.loadtxt(paws.txt)。reshape(4,11,14)

#getting图像列表
paws = [p.squeeze( )for p in np.vsplit(paws_data,4)]


def detect_peaks(image):

拍摄图像并使用局部最大值过滤器。
返回峰值的布尔值掩码(即,当
时,像素的值是邻域最大值时为1,否则为0)


#定义一个8连通的邻域
邻域= generate_binary_structure(2,2)

#apply本地最大过滤器;所有邻近区域的最大值
#的像素都设置为1
local_max = maximum_filter(图像,足迹=邻域)==图像
#local_max是一个掩码,其中包含我们为$的峰值b $ b #looking for,但也是背景。
#为了隔离峰值,我们必须从掩码中删除背景。

#we创建背景的掩码
background =(image == 0)

#a一点技术性:我们必须侵蚀背景才能
#successfully从local_max中减去它,否则一行将沿着背景边界
#appear(局部最大滤波器的工件)
eroded_background = binary_erosion(background,structure = neighborhood,border_value = 1)

#we获取最终的掩码,仅包含峰值,
#by从local_max掩码中删除背景(xor操作)
detected_peaks = local_max ^ eroded_background

返回detected_peaks


#应用检测和绘图结果
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()

所有你需要做的fter在掩码上使用 scipy.ndimage.measurements.label 来标记所有不同的对象。然后你就可以单独玩它们了。



注意该方法运行良好,因为背景不嘈杂。如果是,您将在后台检测到一堆其他不需要的峰值。另一个重要因素是邻域的大小。如果峰值大小发生变化(应保持大致成比例),则需要对其进行调整。


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.

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.

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.

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

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

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'.

Here's a version that can be loaded with 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.

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?

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.

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

Update:

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


New update:

So after the help I got with my questions regarding paw detection and 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.

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!

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.

After trying the current solution on all my measurements I 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!

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

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?

解决方案

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

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

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.label on the mask to label all distinct objects. Then you'll be able to play with them individually.

Note that 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).

这篇关于2D阵列中的峰值检测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆