商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 一文分享Python中三种降噪算法实现方法实测

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

  发布于2026-07-15 阅读(0)

扫一扫,手机访问

先说个真实案例。之前处理传感器数据时,用for循环写了个降噪函数,1000次迭代跑下来整整45秒。这个速度在产线上根本没法用,于是动手优化了一遍,最终把耗时压到了2秒以内。整个过程有不少值得记录的地方,顺便把三种常见降噪算法的效果也做了实测对比。

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

原始代码有多慢

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

光是跑1000次就得45秒,这在产线上根本没法用。问题出在哪儿?看看代码就明白了——两层嵌套循环,每次都要遍历窗口内的所有数据点,计算量可想而知。

方案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倍的提升。为什么?因为NumPy的底层是C语言写的,还用了SIMD指令集并行计算。把循环交出去,让C去处理,速度自然就上来了。

方案2:Sa vitzky-Golay滤波

from scipy.signal import sa vgol_filter

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

这一轮,Sa vitzky-Golay滤波不仅比NumPy还快,效果也更好。它用的是多项式拟合,而不是简单平均,所以在保留峰形方面表现更出色。

三种算法效果对比

为了更直观地比较,用带噪声的模拟数据测试了移动平均、高斯平滑和Sa vitzky-Golay三种方法。测试数据模拟了三个不同位置、不同强度的信号峰:

import numpy as np
from scipy.signal import sa vgol_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 = sa vgol_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"Sa v-Gol  : 峰位误差 {peak_error(sg, true_signal)[0]:.2f}nm, 峰高误差 {peak_error(sg, true_signal)[1]:.1f}%")

结果非常直观:

移动平均 : 峰位误差 0.00nm, 峰高误差 8.2%

高斯平滑 : 峰位误差 0.00nm, 峰高误差 5.1%

Sa v-Gol : 峰位误差 0.00nm, 峰高误差 0.3%

Sa vitzky-Golay可以说是碾压级别的表现。峰位一个不差,峰高误差只有0.3%,而其他两种方法都在5%以上。这意味着在需要精确分析信号峰的场景下,算法选择直接决定了结果质量。

完整对比表

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

优化后的代码

基于Sa vitzky-Golay滤波,把降噪和寻峰功能封装成了一个类,方便产线调用:

from scipy.signal import sa vgol_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 = sa vgol_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.8毫秒,产线实时处理完全没问题。

一个坑

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

一个经验公式:window ≈ 2 * (数据点数 / 峰宽) + 1,记得取奇数。这样既能保证滤波效果,又不会因为窗口过大而丢失信号细节。

总结

  1. 别用Python写循环处理数值数据——这是最常见的性能瓶颈,交给C底层的库去做就好。
  2. 算法选型比代码优化更重要。Sa vitzky-Golay又快又准,而且效果远优于简单平均和高斯平滑。
  3. 先profile找瓶颈,别盲目优化。很多时候,换一个算法比优化代码逻辑更高效。
本文转载于:https://www.jb51.net/python/367356fu6.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注