一文分享Python中三种降噪算法实现方法实测

 更新时间:2026年07月14日 08:46:52   作者:光测实验室  
之前处理传感器数据用的for循环,1000次迭代要跑45秒,本文分享了从45秒到2秒的优化实战,对比了移动平均、高斯平滑和Savitzky-Golay三种降噪算法,有需要的小伙伴可以了解下

之前处理传感器数据用的for循环,1000次迭代要跑45秒。一怒之下优化了一遍,现在2秒搞定。把过程记下来,顺便对比了三种降噪算法的效果。

原始代码有多慢

def smooth_slow(signal, window=11):
    n = len(signal)
    result = np.zeros(n)
    half = window // 2

    for i in range(half, n - half):
        s = 0
        for j in range(i - half, i + half + 1):
            s += signal[j]
        result[i] = s / window

    return result

# 测试
import time
np.random.seed(42)
signal = np.random.randn(200) + 1000  # 模拟200点传感器数据

start = time.time()
for _ in range(1000):
    _ = smooth_slow(signal)
print(f"for循环: {time.time() - start:.2f}s")
# for循环: 45.23s

45秒跑1000次,产线上根本没法用。

方案1:NumPy向量化

def smooth_numpy(signal, window=11):
    kernel = np.ones(window) / window
    return np.convolve(signal, kernel, mode='same')

start = time.time()
for _ in range(1000):
    _ = smooth_numpy(signal)
print(f"NumPy: {time.time() - start:.2f}s")
# NumPy: 3.09s

14.6倍提升。原理很简单:把循环交给C写的NumPy,底层用SIMD并行算。

方案2:Savitzky-Golay滤波

from scipy.signal import savgol_filter

start = time.time()
for _ in range(1000):
    _ = savgol_filter(signal, 11, 3)
print(f"Sav-Gol: {time.time() - start:.2f}s")
# Sav-Gol: 2.85s

比NumPy还快一点,而且效果更好。它用多项式拟合而不是简单平均,保留峰形更好。

三种算法效果对比

测了移动平均、高斯平滑、Savitzky-Golay三种,用带噪声的模拟数据:

import numpy as np
from scipy.signal import savgol_filter, find_peaks
from scipy.ndimage import gaussian_filter1d

# 生成测试数据
np.random.seed(42)
x = np.arange(380, 782, 2)
n = len(x)

baseline = 1000 + 50 * np.sin((x - 380) / 100)
p1 = 800 * np.exp(-((x - 450) / 15) ** 2)
p2 = 600 * np.exp(-((x - 550) / 20) ** 2)
p3 = 400 * np.exp(-((x - 680) / 25) ** 2)
true_signal = baseline + p1 + p2 + p3
raw = true_signal + np.random.normal(0, 30, n)

# 三种方法
ma = np.convolve(raw, np.ones(11)/11, mode='same')
gauss = gaussian_filter1d(raw, sigma=2)
sg = savgol_filter(raw, 11, 3)

# 看峰位误差
def peak_error(filtered, true):
    pf, _ = find_peaks(filtered, height=1200, distance=20)
    pt, _ = find_peaks(true, height=1200, distance=20)
    if len(pf) != len(pt): return 999, 999
    wl_err = np.mean(np.abs(x[pf] - x[pt]))
    int_err = np.mean(np.abs(filtered[pf] - true[pt]) / true[pt]) * 100
    return wl_err, int_err

print(f"移动平均 : 峰位误差 {peak_error(ma, true_signal)[0]:.2f}nm, 峰高误差 {peak_error(ma, true_signal)[1]:.1f}%")
print(f"高斯平滑 : 峰位误差 {peak_error(gauss, true_signal)[0]:.2f}nm, 峰高误差 {peak_error(gauss, true_signal)[1]:.1f}%")
print(f"Sav-Gol  : 峰位误差 {peak_error(sg, true_signal)[0]:.2f}nm, 峰高误差 {peak_error(sg, true_signal)[1]:.1f}%")

结果:

移动平均 : 峰位误差 0.00nm, 峰高误差 8.2%
高斯平滑 : 峰位误差 0.00nm, 峰高误差 5.1%
Sav-Gol  : 峰位误差 0.00nm, 峰高误差 0.3%

Savitzky-Golay碾压。峰位一个不差,峰高误差只有0.3%。

完整对比表

方法1000次耗时相对速度峰位误差峰高误差推荐度
for循环45.2s1x--
移动平均3.1s14.6x0nm8.2%⭐⭐
高斯平滑3.2s14.1x0nm5.1%⭐⭐⭐
Sav-Gol2.8s16.1x0nm0.3%⭐⭐⭐⭐⭐

优化后的代码

from scipy.signal import savgol_filter, find_peaks
import numpy as np

class FastProcessor:
    def __init__(self, x_axis):
        self.x = np.array(x_axis)

    def process(self, raw):
        # 降噪
        filtered = savgol_filter(raw, 11, 3)

        # 寻峰
        peaks, _ = find_peaks(filtered, height=np.mean(filtered)*1.5, distance=20)

        return {
            'filtered': filtered,
            'peaks': peaks,
            'peak_x': self.x[peaks],
            'peak_y': filtered[peaks]
        }

    def benchmark(self, raw, n=1000):
        import time
        _ = self.process(raw)  # 预热
        t0 = time.time()
        for _ in range(n):
            _ = self.process(raw)
        return (time.time() - t0) / n * 1000  # ms/次

# 用
proc = FastProcessor(np.arange(380, 782, 2))
result = proc.process(raw)
print(f"检测到 {len(result['peaks'])} 个峰")
print(f"单次处理: {proc.benchmark(raw):.2f}ms")

单次2.8ms,产线实时处理完全没问题。

一个坑

Savitzky-Golay的窗口长度必须是奇数,而且不能大于数据长度。我第一次写的时候window设成201,数据只有200个点,直接报错。

经验公式:window ≈ 2 * (数据点数 / 峰宽) + 1,取奇数。

总结

  1. 别用Python写循环处理数值数据
  2. 算法选型比代码优化重要,Sav-Gol又快又准
  3. 先profiler找瓶颈,别盲目优化

到此这篇关于一文分享Python中三种降噪算法实现方法实测的文章就介绍到这了,更多相关Python降噪算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Django中引入bootstrap的详细图文详解

    Django中引入bootstrap的详细图文详解

    Django是基于Python的一个Web开发框架,可以实现前端和后台间的数据连接,下面这篇文章主要给大家介绍了关于Django中引入bootstrap的相关资料,需要的朋友可以参考下
    2022-10-10
  • Django cookie和session的应用场景及如何使用

    Django cookie和session的应用场景及如何使用

    今天我们来重点看下Django中session和cookie的用法吧。我们会介绍cookie和session的工作原理,还会分享实际应用的案例。
    2021-04-04
  • 基于python指定包的安装路径方法

    基于python指定包的安装路径方法

    今天小编就为大家分享一篇基于python指定包的安装路径方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • Python中类似于jquery的pyquery库用法分析

    Python中类似于jquery的pyquery库用法分析

    这篇文章主要介绍了Python中类似于jquery的pyquery库用法,结合实例形式分析了pyquery库的概念、原理、使用方法及操作技巧,需要的朋友可以参考下
    2019-12-12
  • python读取文本中数据并转化为DataFrame的实例

    python读取文本中数据并转化为DataFrame的实例

    下面小编就为大家分享一篇python读取文本中数据并转化为DataFrame的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • Python统计分析模块statistics用法示例

    Python统计分析模块statistics用法示例

    这篇文章主要介绍了Python统计分析模块statistics用法,结合实例形式分析了Python统计分析模块statistics计算平均数、中位数、出现次数、标准差等相关操作技巧,需要的朋友可以参考下
    2019-09-09
  • 详细讲解用Python发送SMTP邮件的教程

    详细讲解用Python发送SMTP邮件的教程

    这篇文章主要详细讲解了用Python发送SMTP邮件的教程,包括在邮件中添加图片等文件,强烈推荐!需要的朋友可以参考下
    2015-04-04
  • Python查询阿里巴巴关键字排名的方法

    Python查询阿里巴巴关键字排名的方法

    这篇文章主要介绍了Python查询阿里巴巴关键字排名的方法,涉及Python基于urllib模块解析html页面及进行URL查询的相关技巧,需要的朋友可以参考下
    2015-07-07
  • python数组转换为矩阵的方法实现

    python数组转换为矩阵的方法实现

    本文主要介绍了python数组转换为矩阵的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • python-视频分帧&多帧合成视频实例

    python-视频分帧&多帧合成视频实例

    今天小编就为大家分享一篇python-视频分帧&多帧合成视频实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12

最新评论