检测峰宽的强大算法

16

在这里输入图片描述

我曾经询问过如何通过程序判断频谱带@detly建议使用FWHM(半峰全宽)来确定峰的宽度。我四处搜索后发现,FWHM可用于拟合模型(我对此几乎一窍不通!),特别是高斯模型。具体来说,2.354 * sigma是高斯模型的宽度

我正在查看一个高斯拟合由于存在糟糕的峰值。 图中有4个形态良好的峰值。 然后是“双”峰(虽然两者都可能重要)和两个分散的峰。 对于一个单纯的FWHM,它们将表现出无法解决的难题。

您能帮助生成Scip/Numpy中峰的高斯拟合(用于计算FWHM),给定其x坐标的近似位置吗?如果高斯不是一个好选择,那么可以采用其他方案。


如果您有大量此类数据并希望更快地获得结果,请尝试使用root(root.cern.ch)。它是一个专门设计用于数据分析(以及在您的情况下:拟合例程)的C++框架。 - BandGap
1个回答

21

拟合高斯曲线是一个不错的方法。如果你对初始参数值有一些大致的猜测,可以尝试同时猜测它们。一个大问题是噪声,你可能希望分别拟合每个峰(即一次只考虑给定峰所在范围),或者得到数据中的基线噪声曲线并将其减去。

这里有一些代码尝试拟合多个高斯曲线。我输入了一些相对宽松的参数,用于捕捉8个最突出的峰以及一个额外非常宽泛的高斯曲线,以尝试捕捉背景噪声。处理之前,我稍微清理了您发布的图像,以帮助提取其中的数据(去除了鼠标指针和轴边缘,并反转了图像)。

enter image description here

代码:

import Image
from scipy import *
from scipy.optimize import leastsq
import numpy as np

im = Image.open("proc.png")
im = im.convert('L')
h, w = im.size
#Extract data from the processed image:
im = np.asarray(im)
y_vals, junk  = np.mgrid[w:0:-1, h:0:-1]
y_vals[im < 255] = 0
y_vals = np.amax(y_vals,axis=0)

def gaussian(x, A, x0, sig):
    return A*exp(-(x-x0)**2/(2.0*sig**2))

def fit(p,x):
    return np.sum([gaussian(x, p[i*3],p[i*3+1],p[i*3+2]) 
                   for i in xrange(len(p)/3)],axis=0)

err = lambda p, x, y: fit(p,x)-y

#params are our intitial guesses for fitting gaussians, 
#(Amplitude, x value, sigma):
params = [[50,40,5],[50,110,5],[100,160,5],[100,220,5],
          [50,250,5],[100,260,5],[100,320,5], [100,400,5],   
          [30,300,150]]  # this last one is our noise estimate
params = np.asarray(params).flatten()

x  = xrange(len(y_vals))
results, value = leastsq(err, params, args=(x, y_vals))

for res in results.reshape(-1,3):
    print "amplitude, position, sigma", res

import pylab
pylab.subplot(211, title='original data')
pylab.plot(y_vals)
pylab.subplot(212, title='guassians fit')
y = fit(results, x)
pylab.plot(x, y)
pylab.savefig('fig2.png')
pylab.show()

这些是拟合输出的高斯参数:

#Output:
amplitude, position, sigma [ 23.47418352  41.27086097   5.91012897]
amplitude, position, sigma [  16.26370263  106.39664543    3.67827824]
amplitude, position, sigma [  59.74500239  163.11210316    2.89866545]
amplitude, position, sigma [  57.91752456  220.24444939    2.91145375]
amplitude, position, sigma [  39.78579841  251.25955921    2.74646334]
amplitude, position, sigma [  86.50061756  260.62004831    3.08367483]
amplitude, position, sigma [  79.26849867  319.64343319    3.57224402]
amplitude, position, sigma [ 127.97009966  399.27833126    3.14331212]
amplitude, position, sigma [  20.21224956  379.41066063  195.47122312]

1
你是如何“从图片中获取数据”的?这比读取更高级吗? :) - BandGap
5
@BandGap - 首先我使用Gimp编辑了图像,去除了鼠标指针和坐标轴边缘,然后将图像反转并二值化。然后代码中#从处理后的图像中提取数据:后的4行提取数据:通过创建行值的网格,在图像中将对应位置为零的所有值都设为零,然后取一列中的最大行索引,得到第一个绘图 'original data',该绘图对应于发布的图像。 - fraxel
你能做到吗,只给定x值(没有振幅或SD)? - Jesvin Jose
@aitchnyu - 是的。但是,我需要更多的样本数据集,和/或更好地了解数据可能看起来像什么(特别是极端情况)。也就是说,任务需求需要更加清晰明确-对于您的数据,您需要绝对知道什么构成峰值,什么不构成峰值。在接下来的几天里,我可能没有太多时间,所以您可以考虑提供一些奖励,以鼓励其他人尝试解决这个问题。 - fraxel
我对其进行了修改,以适应单个峰值、近似峰值位置和假定振幅和SD。但是有些峰值往往显示出极端值。因此,它比天真的FWHM不够稳健(@detly在https://dev59.com/uGTWa4cB1Zd3GeqPBka2#10764997上回答了我)。这里是一些样本数据:http://pastebin.com/b3VVZ24w - Jesvin Jose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接